[Pkg-javascript-commits] [less.js] 63/285: untangle the mess of dependencies and remove all circular dependencies. Remove un-necessary dependency injection.

Jonas Smedegaard dr at jones.dk
Mon Oct 26 23:23:38 UTC 2015


This is an automated email from the git hooks/post-receive script.

js pushed a commit to annotated tag v2.0.0
in repository less.js.

commit 16746e9b1eca8e5cbf0b2fb9f8e412a5ad26e95a
Author: Luke Page <luke.a.page at gmail.com>
Date:   Sun Aug 24 17:55:46 2014 +0100

    untangle the mess of dependencies and remove all circular dependencies. Remove un-necessary dependency injection.
---
 lib/less/browser.js                         |   4 +-
 lib/less/{tree => data}/unit-conversions.js |   0
 lib/less/env.js                             | 257 ++++----
 lib/less/functions/default.js               |  44 +-
 lib/less/functions/index.js                 |   6 +-
 lib/less/functions/types.js                 |   5 +-
 lib/less/non-node-index.js                  |   4 +-
 lib/less/parser.js                          |   9 +-
 lib/less/tree.js                            | 178 ++---
 lib/less/tree/alpha.js                      |  43 +-
 lib/less/tree/anonymous.js                  |  47 +-
 lib/less/tree/assignment.js                 |  43 +-
 lib/less/tree/attribute.js                  |  38 +-
 lib/less/tree/call.js                       |  99 ++-
 lib/less/tree/color.js                      | 283 ++++----
 lib/less/tree/combinator.js                 |  25 +-
 lib/less/tree/comment.js                    |  38 +-
 lib/less/tree/condition.js                  |  74 ++-
 lib/less/tree/debug-info.js                 |  34 +
 lib/less/tree/detached-ruleset.js           |  28 +-
 lib/less/tree/dimension.js                  | 267 ++++----
 lib/less/tree/directive.js                  | 143 ++--
 lib/less/tree/element.js                    |  57 +-
 lib/less/tree/expression.js                 |  93 ++-
 lib/less/tree/extend.js                     |  54 +-
 lib/less/tree/import.js                     | 147 ++---
 lib/less/tree/javascript.js                 |  66 +-
 lib/less/tree/js-eval-node.js               |  53 ++
 lib/less/tree/keyword.js                    |  36 +-
 lib/less/tree/media.js                      | 270 ++++----
 lib/less/tree/mixin-call.js                 | 235 +++----
 lib/less/tree/mixin-definition.js           | 254 ++++----
 lib/less/tree/negative.js                   |  32 +-
 lib/less/tree/node.js                       |  35 +
 lib/less/tree/operation.js                  |  80 +--
 lib/less/tree/paren.js                      |  26 +-
 lib/less/tree/quoted.js                     |  84 ++-
 lib/less/tree/rule.js                       | 137 ++--
 lib/less/tree/ruleset-call.js               |  19 +-
 lib/less/tree/ruleset.js                    | 971 ++++++++++++++--------------
 lib/less/tree/selector.js                   | 189 +++---
 lib/less/tree/unicode-descriptor.js         |  16 +-
 lib/less/tree/unit.js                       | 209 +++---
 lib/less/tree/url.js                        |  73 +--
 lib/less/tree/value.js                      |  53 +-
 lib/less/tree/variable.js                   |  65 +-
 lib/less/visitor/import-visitor.js          |   5 +-
 47 files changed, 2434 insertions(+), 2494 deletions(-)

diff --git a/lib/less/browser.js b/lib/less/browser.js
index aed3d99..61d7691 100644
--- a/lib/less/browser.js
+++ b/lib/less/browser.js
@@ -309,7 +309,7 @@ function loadStyles(modifyVars) {
     for (var i = 0; i < styles.length; i++) {
         style = styles[i];
         if (style.type.match(typePattern)) {
-            var env = new less.tree.parseEnv(options),
+            var env = new less.contexts.parseEnv(options),
                 lessText = style.innerHTML || '';
             env.filename = document.location.href.replace(/#.*$/, '');
 
@@ -340,7 +340,7 @@ function loadStyles(modifyVars) {
 
 function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) {
 
-    var env = new less.tree.parseEnv(options);
+    var env = new less.contexts.parseEnv(options);
     env.mime = sheet.type;
 
     if (modifyVars || options.globalVars) {
diff --git a/lib/less/tree/unit-conversions.js b/lib/less/data/unit-conversions.js
similarity index 100%
rename from lib/less/tree/unit-conversions.js
rename to lib/less/data/unit-conversions.js
diff --git a/lib/less/env.js b/lib/less/env.js
index aff2888..5df2cf8 100644
--- a/lib/less/env.js
+++ b/lib/less/env.js
@@ -1,138 +1,139 @@
-module.exports = function (tree) {
-
-    var parseCopyProperties = [
-        'paths',            // option - unmodified - paths to search for imports on
-        'files',            // list of files that have been imported, used for import-once
-        'contents',         // map - filename to contents of all the files
-        'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore
-        'relativeUrls',     // option - whether to adjust URL's to be relative
-        'rootpath',         // option - rootpath to append to URL's
-        'strictImports',    // option -
-        'insecure',         // option - whether to allow imports from insecure ssl hosts
-        'dumpLineNumbers',  // option - whether to dump line numbers
-        'compress',         // option - whether to compress
-        'processImports',   // option - whether to process imports. if false then imports will not be imported
-        'syncImport',       // option - whether to import synchronously
-        'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true
-        'chunkInput',       // option - whether to chunk input. more performant but causes parse issues.
-        'mime',             // browser only - mime type for sheet import
-        'useFileCache',     // browser only - whether to use the per file session cache
-        'currentFileInfo'   // information about the current file - for error reporting and importing and making urls relative etc.
-    ];
+var contexts = {};
+module.exports = contexts;
 
-    //currentFileInfo = {
-    //  'relativeUrls' - option - whether to adjust URL's to be relative
-    //  'filename' - full resolved filename of current file
-    //  'rootpath' - path to append to normal URLs for this node
-    //  'currentDirectory' - path to the current file, absolute
-    //  'rootFilename' - filename of the base file
-    //  'entryPath' - absolute path to the entry file
-    //  'reference' - whether the file should not be output and only output parts that are referenced
-
-    tree.parseEnv = function(options) {
-        copyFromOriginal(options, this, parseCopyProperties);
-
-        if (!this.contents) { this.contents = {}; }
-        if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; }
-        if (!this.files) { this.files = {}; }
-
-        if (typeof this.paths === "string") { this.paths = [this.paths]; }
-
-        if (!this.currentFileInfo) {
-            var filename = (options && options.filename) || "input";
-            var entryPath = filename.replace(/[^\/\\]*$/, "");
-            if (options) {
-                options.filename = null;
-            }
-            this.currentFileInfo = {
-                filename: filename,
-                relativeUrls: this.relativeUrls,
-                rootpath: (options && options.rootpath) || "",
-                currentDirectory: entryPath,
-                entryPath: entryPath,
-                rootFilename: filename
-            };
-        }
-    };
-
-    var evalCopyProperties = [
-        'silent',         // whether to swallow errors and warnings
-        'verbose',        // whether to log more activity
-        'compress',       // whether to compress
-        'yuicompress',    // whether to compress with the outside tool yui compressor
-        'ieCompat',       // whether to enforce IE compatibility (IE8 data-uri)
-        'strictMath',     // whether math has to be within parenthesis
-        'strictUnits',    // whether units need to evaluate correctly
-        'cleancss',       // whether to compress with clean-css
-        'sourceMap',      // whether to output a source map
-        'importMultiple', // whether we are currently importing multiple copies
-        'urlArgs'         // whether to add args into url tokens
-        ];
-
-    tree.evalEnv = function(options, frames) {
-        copyFromOriginal(options, this, evalCopyProperties);
-
-        this.frames = frames || [];
-    };
-
-    tree.evalEnv.prototype.inParenthesis = function () {
-        if (!this.parensStack) {
-            this.parensStack = [];
+var copyFromOriginal = function copyFromOriginal(original, destination, propertiesToCopy) {
+    if (!original) { return; }
+
+    for(var i = 0; i < propertiesToCopy.length; i++) {
+        if (original.hasOwnProperty(propertiesToCopy[i])) {
+            destination[propertiesToCopy[i]] = original[propertiesToCopy[i]];
         }
-        this.parensStack.push(true);
-    };
-
-    tree.evalEnv.prototype.outOfParenthesis = function () {
-        this.parensStack.pop();
-    };
-
-    tree.evalEnv.prototype.isMathOn = function () {
-        return this.strictMath ? (this.parensStack && this.parensStack.length) : true;
-    };
-
-    tree.evalEnv.prototype.isPathRelative = function (path) {
-        return !/^(?:[a-z-]+:|\/)/.test(path);
-    };
-
-    tree.evalEnv.prototype.normalizePath = function( path ) {
-        var
-          segments = path.split("/").reverse(),
-          segment;
-
-        path = [];
-        while (segments.length !== 0 ) {
-            segment = segments.pop();
-            switch( segment ) {
-                case ".":
-                    break;
-                case "..":
-                    if ((path.length === 0) || (path[path.length - 1] === "..")) {
-                        path.push( segment );
-                    } else {
-                        path.pop();
-                    }
-                    break;
-                default:
-                    path.push( segment );
-                    break;
-            }
+    }
+};
+
+var parseCopyProperties = [
+    'paths',            // option - unmodified - paths to search for imports on
+    'files',            // list of files that have been imported, used for import-once
+    'contents',         // map - filename to contents of all the files
+    'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore
+    'relativeUrls',     // option - whether to adjust URL's to be relative
+    'rootpath',         // option - rootpath to append to URL's
+    'strictImports',    // option -
+    'insecure',         // option - whether to allow imports from insecure ssl hosts
+    'dumpLineNumbers',  // option - whether to dump line numbers
+    'compress',         // option - whether to compress
+    'processImports',   // option - whether to process imports. if false then imports will not be imported
+    'syncImport',       // option - whether to import synchronously
+    'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true
+    'chunkInput',       // option - whether to chunk input. more performant but causes parse issues.
+    'mime',             // browser only - mime type for sheet import
+    'useFileCache',     // browser only - whether to use the per file session cache
+    'currentFileInfo'   // information about the current file - for error reporting and importing and making urls relative etc.
+];
+
+//currentFileInfo = {
+//  'relativeUrls' - option - whether to adjust URL's to be relative
+//  'filename' - full resolved filename of current file
+//  'rootpath' - path to append to normal URLs for this node
+//  'currentDirectory' - path to the current file, absolute
+//  'rootFilename' - filename of the base file
+//  'entryPath' - absolute path to the entry file
+//  'reference' - whether the file should not be output and only output parts that are referenced
+
+contexts.parseEnv = function(options) {
+    copyFromOriginal(options, this, parseCopyProperties);
+
+    if (!this.contents) { this.contents = {}; }
+    if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; }
+    if (!this.files) { this.files = {}; }
+
+    if (typeof this.paths === "string") { this.paths = [this.paths]; }
+
+    if (!this.currentFileInfo) {
+        var filename = (options && options.filename) || "input";
+        var entryPath = filename.replace(/[^\/\\]*$/, "");
+        if (options) {
+            options.filename = null;
         }
+        this.currentFileInfo = {
+            filename: filename,
+            relativeUrls: this.relativeUrls,
+            rootpath: (options && options.rootpath) || "",
+            currentDirectory: entryPath,
+            entryPath: entryPath,
+            rootFilename: filename
+        };
+    }
+};
 
-        return path.join("/");
-    };
+var evalCopyProperties = [
+    'silent',         // whether to swallow errors and warnings
+    'verbose',        // whether to log more activity
+    'compress',       // whether to compress
+    'yuicompress',    // whether to compress with the outside tool yui compressor
+    'ieCompat',       // whether to enforce IE compatibility (IE8 data-uri)
+    'strictMath',     // whether math has to be within parenthesis
+    'strictUnits',    // whether units need to evaluate correctly
+    'cleancss',       // whether to compress with clean-css
+    'sourceMap',      // whether to output a source map
+    'importMultiple', // whether we are currently importing multiple copies
+    'urlArgs'         // whether to add args into url tokens
+    ];
 
-    //todo - do the same for the toCSS env
-    //tree.toCSSEnv = function (options) {
-    //};
+contexts.evalEnv = function(options, frames) {
+    copyFromOriginal(options, this, evalCopyProperties);
 
-    var copyFromOriginal = function(original, destination, propertiesToCopy) {
-        if (!original) { return; }
+    this.frames = frames || [];
+};
+
+contexts.evalEnv.prototype.inParenthesis = function () {
+    if (!this.parensStack) {
+        this.parensStack = [];
+    }
+    this.parensStack.push(true);
+};
 
-        for(var i = 0; i < propertiesToCopy.length; i++) {
-            if (original.hasOwnProperty(propertiesToCopy[i])) {
-                destination[propertiesToCopy[i]] = original[propertiesToCopy[i]];
-            }
+contexts.evalEnv.prototype.outOfParenthesis = function () {
+    this.parensStack.pop();
+};
+
+contexts.evalEnv.prototype.isMathOn = function () {
+    return this.strictMath ? (this.parensStack && this.parensStack.length) : true;
+};
+
+contexts.evalEnv.prototype.isPathRelative = function (path) {
+    return !/^(?:[a-z-]+:|\/)/.test(path);
+};
+
+contexts.evalEnv.prototype.normalizePath = function( path ) {
+    var
+      segments = path.split("/").reverse(),
+      segment;
+
+    path = [];
+    while (segments.length !== 0 ) {
+        segment = segments.pop();
+        switch( segment ) {
+            case ".":
+                break;
+            case "..":
+                if ((path.length === 0) || (path[path.length - 1] === "..")) {
+                    path.push( segment );
+                } else {
+                    path.pop();
+                }
+                break;
+            default:
+                path.push( segment );
+                break;
         }
-    };
+    }
 
+    return path.join("/");
 };
+
+//todo - do the same for the toCSS env
+//tree.toCSSEnv = function (options) {
+//};
+
+
diff --git a/lib/less/functions/default.js b/lib/less/functions/default.js
index 6aa78aa..ee256c7 100644
--- a/lib/less/functions/default.js
+++ b/lib/less/functions/default.js
@@ -1,25 +1,23 @@
-module.exports = function(functions, tree) {
-    var defaultFunc = {
-        eval: function () {
-            var v = this.value_, e = this.error_;
-            if (e) {
-                throw e;
-            }
-            if (v != null) {
-                return v ? tree.True : tree.False;
-            }
-        },
-        value: function (v) {
-            this.value_ = v;
-        },
-        error: function (e) {
-            this.error_ = e;
-        },
-        reset: function () {
-            this.value_ = this.error_ = null;
-        }
-    };
+var Keyword = require("../tree/keyword.js");
 
-    functions.functionRegistry.add("default", defaultFunc.eval.bind(defaultFunc));
-    tree.defaultFunc = defaultFunc;
+var defaultFunc = {
+    eval: function () {
+        var v = this.value_, e = this.error_;
+        if (e) {
+            throw e;
+        }
+        if (v != null) {
+            return v ? Keyword.True : Keyword.False;
+        }
+    },
+    value: function (v) {
+        this.value_ = v;
+    },
+    error: function (e) {
+        this.error_ = e;
+    },
+    reset: function () {
+        this.value_ = this.error_ = null;
+    }
 };
+module.exports = defaultFunc;
diff --git a/lib/less/functions/index.js b/lib/less/functions/index.js
index 36d7167..4600500 100644
--- a/lib/less/functions/index.js
+++ b/lib/less/functions/index.js
@@ -7,7 +7,11 @@ module.exports = function(less, tree) {
     require("./color.js")(functions, tree);
     require("./color-blending.js")(functions, tree);
     require("./data-uri.js")(functions, tree, less);
-    require("./default.js")(functions, tree);
+
+    var defaultFunc = require("./default.js");
+    functions.functionRegistry.add("default", defaultFunc.eval.bind(defaultFunc));
+    tree.defaultFunc = defaultFunc;
+
     require("./math.js")(functions, tree);
     require("./number.js")(functions, tree);
     require("./string.js")(functions, tree);
diff --git a/lib/less/functions/types.js b/lib/less/functions/types.js
index 802d809..0473438 100644
--- a/lib/less/functions/types.js
+++ b/lib/less/functions/types.js
@@ -1,9 +1,10 @@
+var Keyword = require("../tree/keyword.js");
 module.exports = function(functions, tree) {
     var isa = function (n, Type) {
-        return (n instanceof Type) ? tree.True : tree.False;
+        return (n instanceof Type) ? Keyword.True : Keyword.False;
         },
         isunit = function (n, unit) {
-            return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False;
+            return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? Keyword.True : Keyword.False;
         };
     functions.functionRegistry.addMultiple({
         iscolor: function (n) {
diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js
index c93846d..7a7e0f5 100644
--- a/lib/less/non-node-index.js
+++ b/lib/less/non-node-index.js
@@ -5,11 +5,11 @@ var less = {
     }
 };
 
-less.tree = (require('./tree'))(less, less.data);
+less.tree = require('./tree');
 less.visitor = require('./visitor/index.js')(less, less.tree);
 less.Parser = (require('./parser'))(less, less.tree, less.visitor);
 less.functions = (require('./functions/index.js'))(less, less.tree);
-require('./env')(less.tree);
+less.contexts = require("./env.js");
 
 less.tree.sourceMapOutput = require('./source-map-output.js')(less);
 
diff --git a/lib/less/parser.js b/lib/less/parser.js
index 05e0d6c..61fcd93 100644
--- a/lib/less/parser.js
+++ b/lib/less/parser.js
@@ -1,5 +1,6 @@
 var chunker = require('./chunker.js');
 var LessError = require('./less-error.js');
+var contexts = require("./env.js");
 
 module.exports = function(less, tree, visitor) {
 //
@@ -51,8 +52,8 @@ var Parser = function Parser(env) {
 
     // Top parser on an import tree must be sure there is one "env"
     // which will then be passed around by reference.
-    if (!(env instanceof tree.parseEnv)) {
-        env = new tree.parseEnv(env);
+    if (!(env instanceof contexts.parseEnv)) {
+        env = new contexts.parseEnv(env);
     }
 
     var imports = this.imports = {
@@ -109,7 +110,7 @@ var Parser = function Parser(env) {
                 }
                 newFileInfo.filename = resolvedFilename;
 
-                var newEnv = new tree.parseEnv(env);
+                var newEnv = new contexts.parseEnv(env);
 
                 newEnv.currentFileInfo = newFileInfo;
                 newEnv.processImports = false;
@@ -451,7 +452,7 @@ var Parser = function Parser(env) {
                     options = options || {};
                     var evaldRoot,
                         css,
-                        evalEnv = new tree.evalEnv(options);
+                        evalEnv = new contexts.evalEnv(options);
 
                     //
                     // Allows setting variables with a hash, so:
diff --git a/lib/less/tree.js b/lib/less/tree.js
index b3d7b1c..ed72dc4 100644
--- a/lib/less/tree.js
+++ b/lib/less/tree.js
@@ -1,144 +1,40 @@
-module.exports = function (less, data) {
-
 var tree = {};
 
-tree.debugInfo = function(env, ctx, lineSeperator) {
-    var result="";
-    if (env.dumpLineNumbers && !env.compress) {
-        switch(env.dumpLineNumbers) {
-            case 'comments':
-                result = tree.debugInfo.asComment(ctx);
-                break;
-            case 'mediaquery':
-                result = tree.debugInfo.asMediaQuery(ctx);
-                break;
-            case 'all':
-                result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx);
-                break;
-        }
-    }
-    return result;
-};
-
-tree.debugInfo.asComment = function(ctx) {
-    return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n';
-};
-
-tree.debugInfo.asMediaQuery = function(ctx) {
-    return '@media -sass-debug-info{filename{font-family:' +
-        ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) {
-            if (a == '\\') {
-                a = '\/';
-            }
-            return '\\' + a;
-        }) +
-        '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n';
-};
-
-tree.find = function (obj, fun) {
-    for (var i = 0, r; i < obj.length; i++) {
-        r = fun.call(obj, obj[i]);
-        if (r) { return r; }
-    }
-    return null;
-};
-
-tree.jsify = function (obj) {
-    if (Array.isArray(obj.value) && (obj.value.length > 1)) {
-        return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']';
-    } else {
-        return obj.toCSS();
-    }
-};
-
-tree.toCSS = function (env) {
-    var strs = [];
-    this.genCSS(env, {
-        add: function(chunk, fileInfo, index) {
-            strs.push(chunk);
-        },
-        isEmpty: function () {
-            return strs.length === 0;
-        }
-    });
-    return strs.join('');
-};
-
-tree.outputRuleset = function (env, output, rules) {
-    var ruleCnt = rules.length, i;
-    env.tabLevel = (env.tabLevel | 0) + 1;
-
-    // Compressed
-    if (env.compress) {
-        output.add('{');
-        for (i = 0; i < ruleCnt; i++) {
-            rules[i].genCSS(env, output);
-        }
-        output.add('}');
-        env.tabLevel--;
-        return;
-    }
-
-    // Non-compressed
-    var tabSetStr = '\n' + Array(env.tabLevel).join("  "), tabRuleStr = tabSetStr + "  ";
-    if (!ruleCnt) {
-        output.add(" {" + tabSetStr + '}');
-    } else {
-        output.add(" {" + tabRuleStr);
-        rules[0].genCSS(env, output);
-        for (i = 1; i < ruleCnt; i++) {
-            output.add(tabRuleStr);
-            rules[i].genCSS(env, output);
-        }
-        output.add(tabSetStr + '}');
-    }
-
-    env.tabLevel--;
-};
-
-tree.Alpha = require('./tree/alpha')(tree);
-tree.Color = require('./tree/color')(data, tree);
-tree.Directive = require('./tree/directive')(tree);
-tree.DetachedRuleset = require('./tree/detached-ruleset')(tree);
-tree.Operation = require('./tree/operation')(tree);
-tree.Dimension = require('./tree/dimension')(tree, require('./tree/unit-conversions')); //todo move conversions
-tree.Unit = require('./tree/unit')(tree, require('./tree/unit-conversions'));
-tree.Keyword = require('./tree/keyword')(tree);
-tree.Variable = require('./tree/variable')(tree);
-tree.Ruleset = require('./tree/ruleset')(tree);
-tree.Element = require('./tree/element')(tree);
-tree.Attribute = require('./tree/attribute')(tree);
-tree.Combinator = require('./tree/combinator')(tree);
-tree.Selector = require('./tree/selector')(tree);
-tree.Quoted = require('./tree/quoted')(tree);
-tree.Expression = require('./tree/expression')(tree);
-tree.Rule = require('./tree/rule')(tree);
-tree.Call = require('./tree/call')(tree, less);
-tree.URL = require('./tree/url')(tree);
-tree.Import = require('./tree/import')(tree);
+tree.Alpha = require('./tree/alpha');
+tree.Color = require('./tree/color');
+tree.Directive = require('./tree/directive');
+tree.DetachedRuleset = require('./tree/detached-ruleset');
+tree.Operation = require('./tree/operation');
+tree.Dimension = require('./tree/dimension');
+tree.Unit = require('./tree/unit');
+tree.Keyword = require('./tree/keyword');
+tree.Variable = require('./tree/variable');
+tree.Ruleset = require('./tree/ruleset');
+tree.Element = require('./tree/element');
+tree.Attribute = require('./tree/attribute');
+tree.Combinator = require('./tree/combinator');
+tree.Selector = require('./tree/selector');
+tree.Quoted = require('./tree/quoted');
+tree.Expression = require('./tree/expression');
+tree.Rule = require('./tree/rule');
+tree.Call = require('./tree/call');
+tree.URL = require('./tree/url');
+tree.Import = require('./tree/import');
 tree.mixin = {
-    Call: require('./tree/mixin-call')(tree),
-    Definition: require('./tree/mixin-definition')(tree)
-};
-tree.Comment = require('./tree/comment')(tree);
-tree.Anonymous = require('./tree/anonymous')(tree);
-tree.Value = require('./tree/value')(tree);
-tree.JavaScript = require('./tree/javascript')(tree);
-tree.Assignment = require('./tree/assignment')(tree);
-tree.Condition = require('./tree/condition')(tree);
-tree.Paren = require('./tree/paren')(tree);
-tree.Media = require('./tree/media')(tree);
-tree.UnicodeDescriptor = require('./tree/unicode-descriptor')(tree);
-tree.Negative = require('./tree/negative')(tree);
-tree.Extend = require('./tree/extend')(tree);
-tree.RulesetCall = require('./tree/ruleset-call')(tree);
-
-tree.fround = function(env, value) {
-    var precision = env && env.numPrecision;
-    //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded...
-    return (precision == null) ? value : Number((value + 2e-16).toFixed(precision));
-};
-
-return tree;
-
-};
+    Call: require('./tree/mixin-call'),
+    Definition: require('./tree/mixin-definition')
+};
+tree.Comment = require('./tree/comment');
+tree.Anonymous = require('./tree/anonymous');
+tree.Value = require('./tree/value');
+tree.JavaScript = require('./tree/javascript');
+tree.Assignment = require('./tree/assignment');
+tree.Condition = require('./tree/condition');
+tree.Paren = require('./tree/paren');
+tree.Media = require('./tree/media');
+tree.UnicodeDescriptor = require('./tree/unicode-descriptor');
+tree.Negative = require('./tree/negative');
+tree.Extend = require('./tree/extend');
+tree.RulesetCall = require('./tree/ruleset-call');
+
+module.exports = tree;
diff --git a/lib/less/tree/alpha.js b/lib/less/tree/alpha.js
index eb247ad..c4a7dd1 100644
--- a/lib/less/tree/alpha.js
+++ b/lib/less/tree/alpha.js
@@ -1,29 +1,28 @@
-module.exports = function (tree) {
+var Node = require("./node.js");
 
 var Alpha = function (val) {
     this.value = val;
 };
-Alpha.prototype = {
-    type: "Alpha",
-    accept: function (visitor) {
-        this.value = visitor.visit(this.value);
-    },
-    eval: function (env) {
-        if (this.value.eval) { return new Alpha(this.value.eval(env)); }
-        return this;
-    },
-    genCSS: function (env, output) {
-        output.add("alpha(opacity=");
+Alpha.prototype = new Node();
+Alpha.prototype.type = "Alpha";
 
-        if (this.value.genCSS) {
-            this.value.genCSS(env, output);
-        } else {
-            output.add(this.value);
-        }
-
-        output.add(")");
-    },
-    toCSS: tree.toCSS
+Alpha.prototype.accept = function (visitor) {
+    this.value = visitor.visit(this.value);
 };
-return Alpha;
+Alpha.prototype.eval = function (env) {
+    if (this.value.eval) { return new Alpha(this.value.eval(env)); }
+    return this;
 };
+Alpha.prototype.genCSS = function (env, output) {
+    output.add("alpha(opacity=");
+
+    if (this.value.genCSS) {
+        this.value.genCSS(env, output);
+    } else {
+        output.add(this.value);
+    }
+
+    output.add(")");
+};
+
+module.exports = Alpha;
diff --git a/lib/less/tree/anonymous.js b/lib/less/tree/anonymous.js
index 711579f..4c1695e 100644
--- a/lib/less/tree/anonymous.js
+++ b/lib/less/tree/anonymous.js
@@ -1,4 +1,4 @@
-module.exports = function (tree) {
+var Node = require("./node.js");
 
 var Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike) {
     this.value = value;
@@ -7,32 +7,29 @@ var Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike)
     this.currentFileInfo = currentFileInfo;
     this.rulesetLike = (typeof rulesetLike === 'undefined')? false : rulesetLike;
 };
-Anonymous.prototype = {
-    type: "Anonymous",
-    eval: function () {
-        return new Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike);
-    },
-    compare: function (x) {
-        if (!x.toCSS) {
-            return -1;
-        }
+Anonymous.prototype = new Node();
+Anonymous.prototype.type = "Anonymous";
+Anonymous.prototype.eval = function () {
+    return new Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike);
+};
+Anonymous.prototype.compare = function (x) {
+    if (!x.toCSS) {
+        return -1;
+    }
 
-        var left = this.toCSS(),
-            right = x.toCSS();
+    var left = this.toCSS(),
+        right = x.toCSS();
 
-        if (left === right) {
-            return 0;
-        }
+    if (left === right) {
+        return 0;
+    }
 
-        return left < right ? -1 : 1;
-    },
-    isRulesetLike: function() {
-        return this.rulesetLike;
-    },
-    genCSS: function (env, output) {
-        output.add(this.value, this.currentFileInfo, this.index, this.mapLines);
-    },
-    toCSS: tree.toCSS
+    return left < right ? -1 : 1;
+};
+Anonymous.prototype.isRulesetLike = function() {
+    return this.rulesetLike;
 };
-return Anonymous;
+Anonymous.prototype.genCSS = function (env, output) {
+    output.add(this.value, this.currentFileInfo, this.index, this.mapLines);
 };
+module.exports = Anonymous;
diff --git a/lib/less/tree/assignment.js b/lib/less/tree/assignment.js
index afe6293..efcc903 100644
--- a/lib/less/tree/assignment.js
+++ b/lib/less/tree/assignment.js
@@ -1,29 +1,28 @@
-module.exports = function (tree) {
+var Node = require("./node.js");
 
 var Assignment = function (key, val) {
     this.key = key;
     this.value = val;
 };
-Assignment.prototype = {
-    type: "Assignment",
-    accept: function (visitor) {
-        this.value = visitor.visit(this.value);
-    },
-    eval: function (env) {
-        if (this.value.eval) {
-            return new(Assignment)(this.key, this.value.eval(env));
-        }
-        return this;
-    },
-    genCSS: function (env, output) {
-        output.add(this.key + '=');
-        if (this.value.genCSS) {
-            this.value.genCSS(env, output);
-        } else {
-            output.add(this.value);
-        }
-    },
-    toCSS: tree.toCSS
+
+Assignment.prototype = new Node();
+Assignment.prototype.type = "Assignment";
+Assignment.prototype.accept = function (visitor) {
+    this.value = visitor.visit(this.value);
+};
+Assignment.prototype.eval = function (env) {
+    if (this.value.eval) {
+        return new(Assignment)(this.key, this.value.eval(env));
+    }
+    return this;
 };
-return Assignment;
+Assignment.prototype.genCSS = function (env, output) {
+    output.add(this.key + '=');
+    if (this.value.genCSS) {
+        this.value.genCSS(env, output);
+    } else {
+        output.add(this.value);
+    }
 };
+module.exports = Assignment;
+
diff --git a/lib/less/tree/attribute.js b/lib/less/tree/attribute.js
index 4103bfc..4d713f2 100644
--- a/lib/less/tree/attribute.js
+++ b/lib/less/tree/attribute.js
@@ -1,29 +1,27 @@
-module.exports = function (tree) {
+var Node = require("./node.js");
 
 var Attribute = function (key, op, value) {
     this.key = key;
     this.op = op;
     this.value = value;
 };
-Attribute.prototype = {
-    type: "Attribute",
-    eval: function (env) {
-        return new(Attribute)(this.key.eval ? this.key.eval(env) : this.key,
-            this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value);
-    },
-    genCSS: function (env, output) {
-        output.add(this.toCSS(env));
-    },
-    toCSS: function (env) {
-        var value = this.key.toCSS ? this.key.toCSS(env) : this.key;
-
-        if (this.op) {
-            value += this.op;
-            value += (this.value.toCSS ? this.value.toCSS(env) : this.value);
-        }
+Attribute.prototype = new Node();
+Attribute.prototype.type = "Attribute";
+Attribute.prototype.eval = function (env) {
+    return new(Attribute)(this.key.eval ? this.key.eval(env) : this.key,
+        this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value);
+};
+Attribute.prototype.genCSS = function (env, output) {
+    output.add(this.toCSS(env));
+};
+Attribute.prototype.toCSS = function (env) {
+    var value = this.key.toCSS ? this.key.toCSS(env) : this.key;
 
-        return '[' + value + ']';
+    if (this.op) {
+        value += this.op;
+        value += (this.value.toCSS ? this.value.toCSS(env) : this.value);
     }
+
+    return '[' + value + ']';
 };
-return Attribute;
-};
+module.exports = Attribute;
diff --git a/lib/less/tree/call.js b/lib/less/tree/call.js
index 08dc989..f7cbfed 100644
--- a/lib/less/tree/call.js
+++ b/lib/less/tree/call.js
@@ -1,5 +1,4 @@
-module.exports = function (tree, less) {
-
+var Node = require("./node.js");
 //
 // A function call node.
 //
@@ -9,61 +8,57 @@ var Call = function (name, args, index, currentFileInfo) {
     this.index = index;
     this.currentFileInfo = currentFileInfo;
 };
-Call.prototype = {
-    type: "Call",
-    accept: function (visitor) {
-        if (this.args) {
-            this.args = visitor.visitArray(this.args);
-        }
-    },
-    //
-    // When evaluating a function call,
-    // we either find the function in `less.functions` [1],
-    // in which case we call it, passing the  evaluated arguments,
-    // if this returns null or we cannot find the function, we
-    // simply print it out as it appeared originally [2].
-    //
-    // The *functions.js* file contains the built-in functions.
-    //
-    // The reason why we evaluate the arguments, is in the case where
-    // we try to pass a variable to a function, like: `saturate(@color)`.
-    // The function should receive the value, not the variable.
-    //
-    eval: function (env) {
-        var args = this.args.map(function (a) { return a.eval(env); }),
-            result, funcCaller = new less.functions.functionCaller(this.name, env, this.currentFileInfo);
+Call.prototype = new Node();
+Call.prototype.type = "Call";
+Call.prototype.accept = function (visitor) {
+    if (this.args) {
+        this.args = visitor.visitArray(this.args);
+    }
+};
+//
+// When evaluating a function call,
+// we either find the function in `less.functions` [1],
+// in which case we call it, passing the  evaluated arguments,
+// if this returns null or we cannot find the function, we
+// simply print it out as it appeared originally [2].
+//
+// The *functions.js* file contains the built-in functions.
+//
+// The reason why we evaluate the arguments, is in the case where
+// we try to pass a variable to a function, like: `saturate(@color)`.
+// The function should receive the value, not the variable.
+//
+Call.prototype.eval = function (env) {
+    var args = this.args.map(function (a) { return a.eval(env); }),
+        FunctionCaller = require("../non-node-index.js").functions.functionCaller, //TODO! Move out
+        result, funcCaller = new FunctionCaller(this.name, env, this.currentFileInfo);
 
-        if (funcCaller.isValid()) { // 1.
-            try {
-                result = funcCaller.call(args);
-                if (result != null) {
-                    return result;
-                }
-            } catch (e) {
-                throw { type: e.type || "Runtime",
-                        message: "error evaluating function `" + this.name + "`" +
-                                 (e.message ? ': ' + e.message : ''),
-                        index: this.index, filename: this.currentFileInfo.filename };
+    if (funcCaller.isValid()) { // 1.
+        try {
+            result = funcCaller.call(args);
+            if (result != null) {
+                return result;
             }
+        } catch (e) {
+            throw { type: e.type || "Runtime",
+                    message: "error evaluating function `" + this.name + "`" +
+                             (e.message ? ': ' + e.message : ''),
+                    index: this.index, filename: this.currentFileInfo.filename };
         }
+    }
 
-        return new Call(this.name, args, this.index, this.currentFileInfo);
-    },
-
-    genCSS: function (env, output) {
-        output.add(this.name + "(", this.currentFileInfo, this.index);
+    return new Call(this.name, args, this.index, this.currentFileInfo);
+};
+Call.prototype.genCSS = function (env, output) {
+    output.add(this.name + "(", this.currentFileInfo, this.index);
 
-        for(var i = 0; i < this.args.length; i++) {
-            this.args[i].genCSS(env, output);
-            if (i + 1 < this.args.length) {
-                output.add(", ");
-            }
+    for(var i = 0; i < this.args.length; i++) {
+        this.args[i].genCSS(env, output);
+        if (i + 1 < this.args.length) {
+            output.add(", ");
         }
+    }
 
-        output.add(")");
-    },
-
-    toCSS: tree.toCSS
-};
-return Call;
+    output.add(")");
 };
+module.exports = Call;
diff --git a/lib/less/tree/color.js b/lib/less/tree/color.js
index 533235f..5a51d1c 100644
--- a/lib/less/tree/color.js
+++ b/lib/less/tree/color.js
@@ -1,4 +1,6 @@
-module.exports = function (data, tree) {
+var Node = require("./node.js"),
+    colors = require("../data/colors.js");
+
 //
 // RGB Colors - #ff0014, #eee
 //
@@ -23,150 +25,157 @@ var Color = function (rgb, a) {
     this.alpha = typeof(a) === 'number' ? a : 1;
 };
 
-Color.prototype = {
-    type: "Color",
-    eval: function () { return this; },
-    luma: function () {
-        var r = this.rgb[0] / 255,
-            g = this.rgb[1] / 255,
-            b = this.rgb[2] / 255;
-
-        r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4);
-        g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4);
-        b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4);
-
-        return 0.2126 * r + 0.7152 * g + 0.0722 * b;
-    },
-
-    genCSS: function (env, output) {
-        output.add(this.toCSS(env));
-    },
-    toCSS: function (env, doNotCompress) {
-        var compress = env && env.compress && !doNotCompress, color, alpha;
-
-        // `keyword` is set if this color was originally
-        // converted from a named color string so we need
-        // to respect this and try to output named color too.
-        if (this.keyword) {
-            return this.keyword;
-        }
+Color.prototype = new Node();
+Color.prototype.type = "Color";
 
-        // If we have some transparency, the only way to represent it
-        // is via `rgba`. Otherwise, we use the hex representation,
-        // which has better compatibility with older browsers.
-        // Values are capped between `0` and `255`, rounded and zero-padded.
-        alpha = tree.fround(env, this.alpha);
-        if (alpha < 1) {
-            return "rgba(" + this.rgb.map(function (c) {
-                return clamp(Math.round(c), 255);
-            }).concat(clamp(alpha, 1))
-                .join(',' + (compress ? '' : ' ')) + ")";
-        }
+function clamp(v, max) {
+    return Math.min(Math.max(v, 0), max);
+}
 
-        color = this.toRGB();
+function toHex(v) {
+    return '#' + v.map(function (c) {
+        c = clamp(Math.round(c), 255);
+        return (c < 16 ? '0' : '') + c.toString(16);
+    }).join('');
+}
 
-        if (compress) {
-            var splitcolor = color.split('');
+Color.prototype.luma = function () {
+    var r = this.rgb[0] / 255,
+        g = this.rgb[1] / 255,
+        b = this.rgb[2] / 255;
 
-            // Convert color to short format
-            if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) {
-                color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[5];
-            }
-        }
+    r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4);
+    g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4);
+    b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4);
+
+    return 0.2126 * r + 0.7152 * g + 0.0722 * b;
+};
+Color.prototype.genCSS = function (env, output) {
+    output.add(this.toCSS(env));
+};
+Color.prototype.toCSS = function (env, doNotCompress) {
+    var compress = env && env.compress && !doNotCompress, color, alpha;
+
+    // `keyword` is set if this color was originally
+    // converted from a named color string so we need
+    // to respect this and try to output named color too.
+    if (this.keyword) {
+        return this.keyword;
+    }
 
-        return color;
-    },
+    // If we have some transparency, the only way to represent it
+    // is via `rgba`. Otherwise, we use the hex representation,
+    // which has better compatibility with older browsers.
+    // Values are capped between `0` and `255`, rounded and zero-padded.
+    alpha = this.fround(env, this.alpha);
+    if (alpha < 1) {
+        return "rgba(" + this.rgb.map(function (c) {
+            return clamp(Math.round(c), 255);
+        }).concat(clamp(alpha, 1))
+            .join(',' + (compress ? '' : ' ')) + ")";
+    }
 
-    //
-    // Operations have to be done per-channel, if not,
-    // channels will spill onto each other. Once we have
-    // our result, in the form of an integer triplet,
-    // we create a new Color node to hold the result.
-    //
-    operate: function (env, op, other) {
-        var rgb = [];
-        var alpha = this.alpha * (1 - other.alpha) + other.alpha;
-        for (var c = 0; c < 3; c++) {
-            rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]);
-        }
-        return new(Color)(rgb, alpha);
-    },
-
-    toRGB: function () {
-        return toHex(this.rgb);
-    },
-
-    toHSL: function () {
-        var r = this.rgb[0] / 255,
-            g = this.rgb[1] / 255,
-            b = this.rgb[2] / 255,
-            a = this.alpha;
-
-        var max = Math.max(r, g, b), min = Math.min(r, g, b);
-        var h, s, l = (max + min) / 2, d = max - min;
-
-        if (max === min) {
-            h = s = 0;
-        } else {
-            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
-
-            switch (max) {
-                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
-                case g: h = (b - r) / d + 2;               break;
-                case b: h = (r - g) / d + 4;               break;
-            }
-            h /= 6;
-        }
-        return { h: h * 360, s: s, l: l, a: a };
-    },
-    //Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
-    toHSV: function () {
-        var r = this.rgb[0] / 255,
-            g = this.rgb[1] / 255,
-            b = this.rgb[2] / 255,
-            a = this.alpha;
-
-        var max = Math.max(r, g, b), min = Math.min(r, g, b);
-        var h, s, v = max;
-
-        var d = max - min;
-        if (max === 0) {
-            s = 0;
-        } else {
-            s = d / max;
-        }
+    color = this.toRGB();
 
-        if (max === min) {
-            h = 0;
-        } else {
-            switch(max){
-                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
-                case g: h = (b - r) / d + 2; break;
-                case b: h = (r - g) / d + 4; break;
-            }
-            h /= 6;
+    if (compress) {
+        var splitcolor = color.split('');
+
+        // Convert color to short format
+        if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) {
+            color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[5];
         }
-        return { h: h * 360, s: s, v: v, a: a };
-    },
-    toARGB: function () {
-        return toHex([this.alpha * 255].concat(this.rgb));
-    },
-    compare: function (x) {
-        if (!x.rgb) {
-            return -1;
+    }
+
+    return color;
+};
+
+//
+// Operations have to be done per-channel, if not,
+// channels will spill onto each other. Once we have
+// our result, in the form of an integer triplet,
+// we create a new Color node to hold the result.
+//
+Color.prototype.operate = function (env, op, other) {
+    var rgb = [];
+    var alpha = this.alpha * (1 - other.alpha) + other.alpha;
+    for (var c = 0; c < 3; c++) {
+        rgb[c] = this._operate(env, op, this.rgb[c], other.rgb[c]);
+    }
+    return new(Color)(rgb, alpha);
+};
+Color.prototype.toRGB = function () {
+    return toHex(this.rgb);
+};
+Color.prototype.toHSL = function () {
+    var r = this.rgb[0] / 255,
+        g = this.rgb[1] / 255,
+        b = this.rgb[2] / 255,
+        a = this.alpha;
+
+    var max = Math.max(r, g, b), min = Math.min(r, g, b);
+    var h, s, l = (max + min) / 2, d = max - min;
+
+    if (max === min) {
+        h = s = 0;
+    } else {
+        s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+
+        switch (max) {
+            case r: h = (g - b) / d + (g < b ? 6 : 0); break;
+            case g: h = (b - r) / d + 2;               break;
+            case b: h = (r - g) / d + 4;               break;
         }
+        h /= 6;
+    }
+    return { h: h * 360, s: s, l: l, a: a };
+};
+//Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
+Color.prototype.toHSV = function () {
+    var r = this.rgb[0] / 255,
+        g = this.rgb[1] / 255,
+        b = this.rgb[2] / 255,
+        a = this.alpha;
+
+    var max = Math.max(r, g, b), min = Math.min(r, g, b);
+    var h, s, v = max;
+
+    var d = max - min;
+    if (max === 0) {
+        s = 0;
+    } else {
+        s = d / max;
+    }
 
-        return (x.rgb[0] === this.rgb[0] &&
-            x.rgb[1] === this.rgb[1] &&
-            x.rgb[2] === this.rgb[2] &&
-            x.alpha === this.alpha) ? 0 : -1;
+    if (max === min) {
+        h = 0;
+    } else {
+        switch(max){
+            case r: h = (g - b) / d + (g < b ? 6 : 0); break;
+            case g: h = (b - r) / d + 2; break;
+            case b: h = (r - g) / d + 4; break;
+        }
+        h /= 6;
     }
+    return { h: h * 360, s: s, v: v, a: a };
+};
+Color.prototype.toARGB = function () {
+    return toHex([this.alpha * 255].concat(this.rgb));
+};
+Color.prototype.compare = function (x) {
+    if (!x.rgb) {
+        return -1;
+    }
+
+    return (x.rgb[0] === this.rgb[0] &&
+        x.rgb[1] === this.rgb[1] &&
+        x.rgb[2] === this.rgb[2] &&
+        x.alpha === this.alpha) ? 0 : -1;
 };
 
 Color.fromKeyword = function(keyword) {
     var c, key = keyword.toLowerCase();
-    if (data.colors.hasOwnProperty(key)) {
-        c = new(Color)(data.colors[key].slice(1));
+    if (colors.hasOwnProperty(key)) {
+        c = new(Color)(colors[key].slice(1));
     }
     else if (key === "transparent") {
         c = new(Color)([0, 0, 0], 0);
@@ -177,16 +186,4 @@ Color.fromKeyword = function(keyword) {
         return c;
     }
 };
-
-function toHex(v) {
-    return '#' + v.map(function (c) {
-        c = clamp(Math.round(c), 255);
-        return (c < 16 ? '0' : '') + c.toString(16);
-    }).join('');
-}
-
-function clamp(v, max) {
-    return Math.min(Math.max(v, 0), max);
-}
-return Color;
-};
+module.exports = Color;
diff --git a/lib/less/tree/combinator.js b/lib/less/tree/combinator.js
index 0e34baf..ba0abb0 100644
--- a/lib/less/tree/combinator.js
+++ b/lib/less/tree/combinator.js
@@ -1,4 +1,4 @@
-module.exports = function (tree) {
+var Node = require("./node.js");
 
 var Combinator = function (value) {
     if (value === ' ') {
@@ -7,18 +7,15 @@ var Combinator = function (value) {
         this.value = value ? value.trim() : "";
     }
 };
-Combinator.prototype = {
-    type: "Combinator",
-    _noSpaceCombinators: {
-        '': true,
-        ' ': true,
-        '|': true
-    },
-    genCSS: function (env, output) {
-        var spaceOrEmpty = (env.compress || this._noSpaceCombinators[this.value]) ? '' : ' ';
-        output.add(spaceOrEmpty + this.value + spaceOrEmpty);
-    },
-    toCSS: tree.toCSS
+Combinator.prototype = new Node();
+Combinator.prototype.type = "Combinator";
+var _noSpaceCombinators = {
+    '': true,
+    ' ': true,
+    '|': true
 };
-return Combinator;
+Combinator.prototype.genCSS = function (env, output) {
+    var spaceOrEmpty = (env.compress || _noSpaceCombinators[this.value]) ? '' : ' ';
+    output.add(spaceOrEmpty + this.value + spaceOrEmpty);
 };
+module.exports = Combinator;
diff --git a/lib/less/tree/comment.js b/lib/less/tree/comment.js
index 10c1e79..4226231 100644
--- a/lib/less/tree/comment.js
+++ b/lib/less/tree/comment.js
@@ -1,28 +1,28 @@
-module.exports = function (tree) {
+var Node = require("./node.js"),
+    getDebugInfo = require("./debug-info.js");
 
 var Comment = function (value, isLineComment, index, currentFileInfo) {
     this.value = value;
     this.isLineComment = isLineComment;
     this.currentFileInfo = currentFileInfo;
 };
-Comment.prototype = {
-    type: "Comment",
-    genCSS: function (env, output) {
-        if (this.debugInfo) {
-            output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index);
-        }
-        output.add(this.value);
-    },
-    toCSS: tree.toCSS,
-    isSilent: function(env) {
-        var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced),
-            isCompressed = env.compress && this.value[2] !== "!";
-        return this.isLineComment || isReference || isCompressed;
-    },
-    eval: function () { return this; },
-    markReferenced: function () {
-        this.isReferenced = true;
+Comment.prototype = new Node();
+Comment.prototype.type = "Comment";
+Comment.prototype.genCSS = function (env, output) {
+    if (this.debugInfo) {
+        output.add(getDebugInfo(env, this), this.currentFileInfo, this.index);
     }
+    output.add(this.value);
 };
-return Comment;
+Comment.prototype.isSilent = function(env) {
+    var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced),
+        isCompressed = env.compress && this.value[2] !== "!";
+    return this.isLineComment || isReference || isCompressed;
 };
+Comment.prototype.markReferenced = function () {
+    this.isReferenced = true;
+};
+Comment.prototype.isRulesetLike = function(root) {
+    return Boolean(root);
+};
+module.exports = Comment;
diff --git a/lib/less/tree/condition.js b/lib/less/tree/condition.js
index fdc7981..464c96c 100644
--- a/lib/less/tree/condition.js
+++ b/lib/less/tree/condition.js
@@ -1,4 +1,4 @@
-module.exports = function (tree) {
+var Node = require("./node.js");
 
 var Condition = function (op, l, r, i, negate) {
     this.op = op.trim();
@@ -7,43 +7,41 @@ var Condition = function (op, l, r, i, negate) {
     this.index = i;
     this.negate = negate;
 };
-Condition.prototype = {
-    type: "Condition",
-    accept: function (visitor) {
-        this.lvalue = visitor.visit(this.lvalue);
-        this.rvalue = visitor.visit(this.rvalue);
-    },
-    eval: function (env) {
-        var a = this.lvalue.eval(env),
-            b = this.rvalue.eval(env);
+Condition.prototype = new Node();
+Condition.prototype.type = "Condition";
+Condition.prototype.accept = function (visitor) {
+    this.lvalue = visitor.visit(this.lvalue);
+    this.rvalue = visitor.visit(this.rvalue);
+};
+Condition.prototype.eval = function (env) {
+    var a = this.lvalue.eval(env),
+        b = this.rvalue.eval(env);
 
-        var i = this.index, result;
+    var i = this.index, result;
 
-        result = (function (op) {
-            switch (op) {
-                case 'and':
-                    return a && b;
-                case 'or':
-                    return a || b;
-                default:
-                    if (a.compare) {
-                        result = a.compare(b);
-                    } else if (b.compare) {
-                        result = b.compare(a);
-                    } else {
-                        throw { type: "Type",
-                                message: "Unable to perform comparison",
-                                index: i };
-                    }
-                    switch (result) {
-                        case -1: return op === '<' || op === '=<' || op === '<=';
-                        case  0: return op === '=' || op === '>=' || op === '=<' || op === '<=';
-                        case  1: return op === '>' || op === '>=';
-                    }
-            }
-        })(this.op);
-        return this.negate ? !result : result;
-    }
-};
-return Condition;
+    result = (function (op) {
+        switch (op) {
+            case 'and':
+                return a && b;
+            case 'or':
+                return a || b;
+            default:
+                if (a.compare) {
+                    result = a.compare(b);
+                } else if (b.compare) {
+                    result = b.compare(a);
+                } else {
+                    throw { type: "Type",
+                            message: "Unable to perform comparison",
+                            index: i };
+                }
+                switch (result) {
+                    case -1: return op === '<' || op === '=<' || op === '<=';
+                    case  0: return op === '=' || op === '>=' || op === '=<' || op === '<=';
+                    case  1: return op === '>' || op === '>=';
+                }
+        }
+    })(this.op);
+    return this.negate ? !result : result;
 };
+module.exports = Condition;
diff --git a/lib/less/tree/debug-info.js b/lib/less/tree/debug-info.js
new file mode 100644
index 0000000..5af873f
--- /dev/null
+++ b/lib/less/tree/debug-info.js
@@ -0,0 +1,34 @@
+var debugInfo = function(env, ctx, lineSeperator) {
+    var result="";
+    if (env.dumpLineNumbers && !env.compress) {
+        switch(env.dumpLineNumbers) {
+            case 'comments':
+                result = debugInfo.asComment(ctx);
+                break;
+            case 'mediaquery':
+                result = debugInfo.asMediaQuery(ctx);
+                break;
+            case 'all':
+                result = debugInfo.asComment(ctx) + (lineSeperator || "") + debugInfo.asMediaQuery(ctx);
+                break;
+        }
+    }
+    return result;
+};
+
+debugInfo.asComment = function(ctx) {
+    return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n';
+};
+
+debugInfo.asMediaQuery = function(ctx) {
+    return '@media -sass-debug-info{filename{font-family:' +
+        ('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) {
+            if (a == '\\') {
+                a = '\/';
+            }
+            return '\\' + a;
+        }) +
+        '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n';
+};
+
+module.exports = debugInfo;
diff --git a/lib/less/tree/detached-ruleset.js b/lib/less/tree/detached-ruleset.js
index a58029c..009d67c 100644
--- a/lib/less/tree/detached-ruleset.js
+++ b/lib/less/tree/detached-ruleset.js
@@ -1,21 +1,21 @@
-module.exports = function (tree) {
+var Node = require("./node.js"),
+    contexts = require("../env.js");
 
 var DetachedRuleset = function (ruleset, frames) {
     this.ruleset = ruleset;
     this.frames = frames;
 };
-DetachedRuleset.prototype = {
-    type: "DetachedRuleset",
-    accept: function (visitor) {
-        this.ruleset = visitor.visit(this.ruleset);
-    },
-    eval: function (env) {
-        var frames = this.frames || env.frames.slice(0);
-        return new DetachedRuleset(this.ruleset, frames);
-    },
-    callEval: function (env) {
-        return this.ruleset.eval(this.frames ? new(tree.evalEnv)(env, this.frames.concat(env.frames)) : env);
-    }
+DetachedRuleset.prototype = new Node();
+DetachedRuleset.prototype.type = "DetachedRuleset";
+DetachedRuleset.prototype.evalFirst = true;
+DetachedRuleset.prototype.accept = function (visitor) {
+    this.ruleset = visitor.visit(this.ruleset);
 };
-return DetachedRuleset;
+DetachedRuleset.prototype.eval = function (env) {
+    var frames = this.frames || env.frames.slice(0);
+    return new DetachedRuleset(this.ruleset, frames);
 };
+DetachedRuleset.prototype.callEval = function (env) {
+    return this.ruleset.eval(this.frames ? new(contexts.evalEnv)(env, this.frames.concat(env.frames)) : env);
+};
+module.exports = DetachedRuleset;
diff --git a/lib/less/tree/dimension.js b/lib/less/tree/dimension.js
index 2b24a8c..84b92c0 100644
--- a/lib/less/tree/dimension.js
+++ b/lib/less/tree/dimension.js
@@ -1,167 +1,164 @@
-module.exports = function (tree, unitConversions) {
+var Node = require("./node.js"),
+    unitConversions = require("../data/unit-conversions.js"),
+    Unit = require("./unit.js"),
+    Color = require("./color.js");
 
 //
 // A number with a unit
 //
 var Dimension = function (value, unit) {
     this.value = parseFloat(value);
-    this.unit = (unit && unit instanceof tree.Unit) ? unit :
-      new(tree.Unit)(unit ? [unit] : undefined);
+    this.unit = (unit && unit instanceof Unit) ? unit :
+      new(Unit)(unit ? [unit] : undefined);
 };
 
-Dimension.prototype = {
-    type: "Dimension",
-    accept: function (visitor) {
-        this.unit = visitor.visit(this.unit);
-    },
-    eval: function (env) {
-        return this;
-    },
-    toColor: function () {
-        return new(tree.Color)([this.value, this.value, this.value]);
-    },
-    genCSS: function (env, output) {
-        if ((env && env.strictUnits) && !this.unit.isSingular()) {
-            throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());
-        }
+Dimension.prototype = new Node();
+Dimension.prototype.type = "Dimension";
+Dimension.prototype.accept = function (visitor) {
+    this.unit = visitor.visit(this.unit);
+};
+Dimension.prototype.eval = function (env) {
+    return this;
+};
+Dimension.prototype.toColor = function () {
+    return new(Color)([this.value, this.value, this.value]);
+};
+Dimension.prototype.genCSS = function (env, output) {
+    if ((env && env.strictUnits) && !this.unit.isSingular()) {
+        throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());
+    }
 
-        var value = tree.fround(env, this.value),
-            strValue = String(value);
+    var value = this.fround(env, this.value),
+        strValue = String(value);
 
-        if (value !== 0 && value < 0.000001 && value > -0.000001) {
-            // would be output 1e-6 etc.
-            strValue = value.toFixed(20).replace(/0+$/, "");
-        }
-
-        if (env && env.compress) {
-            // Zero values doesn't need a unit
-            if (value === 0 && this.unit.isLength()) {
-                output.add(strValue);
-                return;
-            }
+    if (value !== 0 && value < 0.000001 && value > -0.000001) {
+        // would be output 1e-6 etc.
+        strValue = value.toFixed(20).replace(/0+$/, "");
+    }
 
-            // Float values doesn't need a leading zero
-            if (value > 0 && value < 1) {
-                strValue = (strValue).substr(1);
-            }
+    if (env && env.compress) {
+        // Zero values doesn't need a unit
+        if (value === 0 && this.unit.isLength()) {
+            output.add(strValue);
+            return;
         }
 
-        output.add(strValue);
-        this.unit.genCSS(env, output);
-    },
-    toCSS: tree.toCSS,
-
-    // In an operation between two Dimensions,
-    // we default to the first Dimension's unit,
-    // so `1px + 2` will yield `3px`.
-    operate: function (env, op, other) {
-        /*jshint noempty:false */
-        var value = tree.operate(env, op, this.value, other.value),
-            unit = this.unit.clone();
-
-        if (op === '+' || op === '-') {
-            if (unit.numerator.length === 0 && unit.denominator.length === 0) {
-                unit.numerator = other.unit.numerator.slice(0);
-                unit.denominator = other.unit.denominator.slice(0);
-            } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) {
-                // do nothing
-            } else {
-                other = other.convertTo(this.unit.usedUnits());
-
-                if(env.strictUnits && other.unit.toString() !== unit.toString()) {
-                  throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() +
-                    "' and '" + other.unit.toString() + "'.");
-                }
-
-                value = tree.operate(env, op, this.value, other.value);
-            }
-        } else if (op === '*') {
-            unit.numerator = unit.numerator.concat(other.unit.numerator).sort();
-            unit.denominator = unit.denominator.concat(other.unit.denominator).sort();
-            unit.cancel();
-        } else if (op === '/') {
-            unit.numerator = unit.numerator.concat(other.unit.denominator).sort();
-            unit.denominator = unit.denominator.concat(other.unit.numerator).sort();
-            unit.cancel();
+        // Float values doesn't need a leading zero
+        if (value > 0 && value < 1) {
+            strValue = (strValue).substr(1);
         }
-        return new(Dimension)(value, unit);
-    },
+    }
+
+    output.add(strValue);
+    this.unit.genCSS(env, output);
+};
 
-    compare: function (other) {
-        if (other instanceof Dimension) {
-            var a, b,
-                aValue, bValue;
+// In an operation between two Dimensions,
+// we default to the first Dimension's unit,
+// so `1px + 2` will yield `3px`.
+Dimension.prototype.operate = function (env, op, other) {
+    /*jshint noempty:false */
+    var value = this._operate(env, op, this.value, other.value),
+        unit = this.unit.clone();
+
+    if (op === '+' || op === '-') {
+        if (unit.numerator.length === 0 && unit.denominator.length === 0) {
+            unit.numerator = other.unit.numerator.slice(0);
+            unit.denominator = other.unit.denominator.slice(0);
+        } else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) {
+            // do nothing
+        } else {
+            other = other.convertTo(this.unit.usedUnits());
 
-            if (this.unit.isEmpty() || other.unit.isEmpty()) {
-                a = this;
-                b = other;
-            } else {
-                a = this.unify();
-                b = other.unify();
-                if (a.unit.compare(b.unit) !== 0) {
-                    return -1;
-                }
+            if(env.strictUnits && other.unit.toString() !== unit.toString()) {
+              throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() +
+                "' and '" + other.unit.toString() + "'.");
             }
-            aValue = a.value;
-            bValue = b.value;
 
-            if (bValue > aValue) {
+            value = this._operate(env, op, this.value, other.value);
+        }
+    } else if (op === '*') {
+        unit.numerator = unit.numerator.concat(other.unit.numerator).sort();
+        unit.denominator = unit.denominator.concat(other.unit.denominator).sort();
+        unit.cancel();
+    } else if (op === '/') {
+        unit.numerator = unit.numerator.concat(other.unit.denominator).sort();
+        unit.denominator = unit.denominator.concat(other.unit.numerator).sort();
+        unit.cancel();
+    }
+    return new(Dimension)(value, unit);
+};
+Dimension.prototype.compare = function (other) {
+    if (other instanceof Dimension) {
+        var a, b,
+            aValue, bValue;
+
+        if (this.unit.isEmpty() || other.unit.isEmpty()) {
+            a = this;
+            b = other;
+        } else {
+            a = this.unify();
+            b = other.unify();
+            if (a.unit.compare(b.unit) !== 0) {
                 return -1;
-            } else if (bValue < aValue) {
-                return 1;
-            } else {
-                return 0;
             }
-        } else {
+        }
+        aValue = a.value;
+        bValue = b.value;
+
+        if (bValue > aValue) {
             return -1;
+        } else if (bValue < aValue) {
+            return 1;
+        } else {
+            return 0;
         }
-    },
-
-    unify: function () {
-        return this.convertTo({ length: 'px', duration: 's', angle: 'rad' });
-    },
-
-    convertTo: function (conversions) {
-        var value = this.value, unit = this.unit.clone(),
-            i, groupName, group, targetUnit, derivedConversions = {}, applyUnit;
-
-        if (typeof conversions === 'string') {
-            for(i in unitConversions) {
-                if (unitConversions[i].hasOwnProperty(conversions)) {
-                    derivedConversions = {};
-                    derivedConversions[i] = conversions;
-                }
+    } else {
+        return -1;
+    }
+};
+Dimension.prototype.unify = function () {
+    return this.convertTo({ length: 'px', duration: 's', angle: 'rad' });
+};
+Dimension.prototype.convertTo = function (conversions) {
+    var value = this.value, unit = this.unit.clone(),
+        i, groupName, group, targetUnit, derivedConversions = {}, applyUnit;
+
+    if (typeof conversions === 'string') {
+        for(i in unitConversions) {
+            if (unitConversions[i].hasOwnProperty(conversions)) {
+                derivedConversions = {};
+                derivedConversions[i] = conversions;
             }
-            conversions = derivedConversions;
         }
-        applyUnit = function (atomicUnit, denominator) {
-          /*jshint loopfunc:true */
-            if (group.hasOwnProperty(atomicUnit)) {
-                if (denominator) {
-                    value = value / (group[atomicUnit] / group[targetUnit]);
-                } else {
-                    value = value * (group[atomicUnit] / group[targetUnit]);
-                }
-
-                return targetUnit;
+        conversions = derivedConversions;
+    }
+    applyUnit = function (atomicUnit, denominator) {
+      /*jshint loopfunc:true */
+        if (group.hasOwnProperty(atomicUnit)) {
+            if (denominator) {
+                value = value / (group[atomicUnit] / group[targetUnit]);
+            } else {
+                value = value * (group[atomicUnit] / group[targetUnit]);
             }
 
-            return atomicUnit;
-        };
+            return targetUnit;
+        }
 
-        for (groupName in conversions) {
-            if (conversions.hasOwnProperty(groupName)) {
-                targetUnit = conversions[groupName];
-                group = unitConversions[groupName];
+        return atomicUnit;
+    };
 
-                unit.map(applyUnit);
-            }
+    for (groupName in conversions) {
+        if (conversions.hasOwnProperty(groupName)) {
+            targetUnit = conversions[groupName];
+            group = unitConversions[groupName];
+
+            unit.map(applyUnit);
         }
+    }
 
-        unit.cancel();
+    unit.cancel();
 
-        return new(Dimension)(value, unit);
-    }
-};
-return Dimension;
+    return new(Dimension)(value, unit);
 };
+module.exports = Dimension;
diff --git a/lib/less/tree/directive.js b/lib/less/tree/directive.js
index 7789ba3..2f99d3f 100644
--- a/lib/less/tree/directive.js
+++ b/lib/less/tree/directive.js
@@ -1,4 +1,5 @@
-module.exports = function (tree) {
+var Node = require("./node.js"),
+    Ruleset = require("./ruleset.js");
 
 var Directive = function (name, value, rules, index, currentFileInfo, debugInfo) {
     this.name  = name;
@@ -12,64 +13,92 @@ var Directive = function (name, value, rules, index, currentFileInfo, debugInfo)
     this.debugInfo = debugInfo;
 };
 
-Directive.prototype = {
-    type: "Directive",
-    accept: function (visitor) {
-        var value = this.value, rules = this.rules;
-        if (rules) {
-            rules = visitor.visit(rules);
-        }
-        if (value) {
-            value = visitor.visit(value);
-        }
-    },
-    isRulesetLike: function() {
-        return !this.isCharset();
-    },
-    isCharset: function() {
-        return "@charset" === this.name;
-    },
-    genCSS: function (env, output) {
-        var value = this.value, rules = this.rules;
-        output.add(this.name, this.currentFileInfo, this.index);
-        if (value) {
-            output.add(' ');
-            value.genCSS(env, output);
-        }
-        if (rules) {
-            tree.outputRuleset(env, output, [rules]);
-        } else {
-            output.add(';');
-        }
-    },
-    toCSS: tree.toCSS,
-    eval: function (env) {
-        var value = this.value, rules = this.rules;
-        if (value) {
-            value = value.eval(env);
-        }
-        if (rules) {
-            rules = rules.eval(env);
-            rules.root = true;
-        }
-        return new(Directive)(this.name, value, rules,
-            this.index, this.currentFileInfo, this.debugInfo);
-    },
-    variable: function (name) { if (this.rules) return tree.Ruleset.prototype.variable.call(this.rules, name); },
-    find: function () { if (this.rules) return tree.Ruleset.prototype.find.apply(this.rules, arguments); },
-    rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); },
-    markReferenced: function () {
-        var i, rules;
-        this.isReferenced = true;
-        if (this.rules) {
-            rules = this.rules.rules;
-            for (i = 0; i < rules.length; i++) {
-                if (rules[i].markReferenced) {
-                    rules[i].markReferenced();
-                }
+Directive.prototype = new Node();
+Directive.prototype.type = "Directive";
+Directive.prototype.accept = function (visitor) {
+    var value = this.value, rules = this.rules;
+    if (rules) {
+        rules = visitor.visit(rules);
+    }
+    if (value) {
+        value = visitor.visit(value);
+    }
+};
+Directive.prototype.isRulesetLike = function() {
+    return this.rules || !this.isCharset();
+};
+Directive.prototype.isCharset = function() {
+    return "@charset" === this.name;
+};
+Directive.prototype.genCSS = function (env, output) {
+    var value = this.value, rules = this.rules;
+    output.add(this.name, this.currentFileInfo, this.index);
+    if (value) {
+        output.add(' ');
+        value.genCSS(env, output);
+    }
+    if (rules) {
+        this.outputRuleset(env, output, [rules]);
+    } else {
+        output.add(';');
+    }
+};
+Directive.prototype.eval = function (env) {
+    var value = this.value, rules = this.rules;
+    if (value) {
+        value = value.eval(env);
+    }
+    if (rules) {
+        rules = rules.eval(env);
+        rules.root = true;
+    }
+    return new(Directive)(this.name, value, rules,
+        this.index, this.currentFileInfo, this.debugInfo);
+};
+Directive.prototype.variable = function (name) { if (this.rules) return Ruleset.prototype.variable.call(this.rules, name); };
+Directive.prototype.find = function () { if (this.rules) return Ruleset.prototype.find.apply(this.rules, arguments); };
+Directive.prototype.rulesets = function () { if (this.rules) return Ruleset.prototype.rulesets.apply(this.rules); };
+Directive.prototype.markReferenced = function () {
+    var i, rules;
+    this.isReferenced = true;
+    if (this.rules) {
+        rules = this.rules.rules;
+        for (i = 0; i < rules.length; i++) {
+            if (rules[i].markReferenced) {
+                rules[i].markReferenced();
             }
         }
     }
 };
-return Directive;
+Directive.prototype.outputRuleset = function (env, output, rules) {
+    var ruleCnt = rules.length, i;
+    env.tabLevel = (env.tabLevel | 0) + 1;
+
+    // Compressed
+    if (env.compress) {
+        output.add('{');
+        for (i = 0; i < ruleCnt; i++) {
+            rules[i].genCSS(env, output);
+        }
+        output.add('}');
+        env.tabLevel--;
+        return;
+    }
+
+    // Non-compressed
+    var tabSetStr = '\n' + Array(env.tabLevel).join("  "), tabRuleStr = tabSetStr + "  ";
+    if (!ruleCnt) {
+        output.add(" {" + tabSetStr + '}');
+    } else {
+        output.add(" {" + tabRuleStr);
+        rules[0].genCSS(env, output);
+        for (i = 1; i < ruleCnt; i++) {
+            output.add(tabRuleStr);
+            rules[i].genCSS(env, output);
+        }
+        output.add(tabSetStr + '}');
+    }
+
+    env.tabLevel--;
 };
+module.exports = Directive;
diff --git a/lib/less/tree/element.js b/lib/less/tree/element.js
index 925b4b9..8142d11 100644
--- a/lib/less/tree/element.js
+++ b/lib/less/tree/element.js
@@ -1,8 +1,9 @@
-module.exports = function (tree) {
+var Node = require("./node.js"),
+    Combinator = require("./combinator.js");
 
 var Element = function (combinator, value, index, currentFileInfo) {
-    this.combinator = combinator instanceof tree.Combinator ?
-                      combinator : new(tree.Combinator)(combinator);
+    this.combinator = combinator instanceof Combinator ?
+                      combinator : new(Combinator)(combinator);
 
     if (typeof(value) === 'string') {
         this.value = value.trim();
@@ -14,32 +15,30 @@ var Element = function (combinator, value, index, currentFileInfo) {
     this.index = index;
     this.currentFileInfo = currentFileInfo;
 };
-Element.prototype = {
-    type: "Element",
-    accept: function (visitor) {
-        var value = this.value;
-        this.combinator = visitor.visit(this.combinator);
-        if (typeof value === "object") {
-            this.value = visitor.visit(value);
-        }
-    },
-    eval: function (env) {
-        return new(Element)(this.combinator,
-                                 this.value.eval ? this.value.eval(env) : this.value,
-                                 this.index,
-                                 this.currentFileInfo);
-    },
-    genCSS: function (env, output) {
-        output.add(this.toCSS(env), this.currentFileInfo, this.index);
-    },
-    toCSS: function (env) {
-        var value = (this.value.toCSS ? this.value.toCSS(env) : this.value);
-        if (value === '' && this.combinator.value.charAt(0) === '&') {
-            return '';
-        } else {
-            return this.combinator.toCSS(env || {}) + value;
-        }
+Element.prototype = new Node();
+Element.prototype.type = "Element";
+Element.prototype.accept = function (visitor) {
+    var value = this.value;
+    this.combinator = visitor.visit(this.combinator);
+    if (typeof value === "object") {
+        this.value = visitor.visit(value);
     }
 };
-return Element;
+Element.prototype.eval = function (env) {
+    return new(Element)(this.combinator,
+                             this.value.eval ? this.value.eval(env) : this.value,
+                             this.index,
+                             this.currentFileInfo);
 };
+Element.prototype.genCSS = function (env, output) {
+    output.add(this.toCSS(env), this.currentFileInfo, this.index);
+};
+Element.prototype.toCSS = function (env) {
+    var value = (this.value.toCSS ? this.value.toCSS(env) : this.value);
+    if (value === '' && this.combinator.value.charAt(0) === '&') {
+        return '';
+    } else {
+        return this.combinator.toCSS(env || {}) + value;
+    }
+};
+module.exports = Element;
diff --git a/lib/less/tree/expression.js b/lib/less/tree/expression.js
index 4614e15..11d09cb 100644
--- a/lib/less/tree/expression.js
+++ b/lib/less/tree/expression.js
@@ -1,54 +1,53 @@
-module.exports = function (tree) {
+var Node = require("./node.js"),
+    Paren = require("./paren.js"),
+    Comment = require("./comment.js");
 
 var Expression = function (value) { this.value = value; };
-Expression.prototype = {
-    type: "Expression",
-    accept: function (visitor) {
-        if (this.value) {
-            this.value = visitor.visitArray(this.value);
-        }
-    },
-    eval: function (env) {
-        var returnValue,
-            inParenthesis = this.parens && !this.parensInOp,
-            doubleParen = false;
-        if (inParenthesis) {
-            env.inParenthesis();
-        }
-        if (this.value.length > 1) {
-            returnValue = new(Expression)(this.value.map(function (e) {
-                return e.eval(env);
-            }));
-        } else if (this.value.length === 1) {
-            if (this.value[0].parens && !this.value[0].parensInOp) {
-                doubleParen = true;
-            }
-            returnValue = this.value[0].eval(env);
-        } else {
-            returnValue = this;
-        }
-        if (inParenthesis) {
-            env.outOfParenthesis();
-        }
-        if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) {
-            returnValue = new(tree.Paren)(returnValue);
+Expression.prototype = new Node();
+Expression.prototype.type = "Expression";
+Expression.prototype.accept = function (visitor) {
+    if (this.value) {
+        this.value = visitor.visitArray(this.value);
+    }
+};
+Expression.prototype.eval = function (env) {
+    var returnValue,
+        inParenthesis = this.parens && !this.parensInOp,
+        doubleParen = false;
+    if (inParenthesis) {
+        env.inParenthesis();
+    }
+    if (this.value.length > 1) {
+        returnValue = new(Expression)(this.value.map(function (e) {
+            return e.eval(env);
+        }));
+    } else if (this.value.length === 1) {
+        if (this.value[0].parens && !this.value[0].parensInOp) {
+            doubleParen = true;
         }
-        return returnValue;
-    },
-    genCSS: function (env, output) {
-        for(var i = 0; i < this.value.length; i++) {
-            this.value[i].genCSS(env, output);
-            if (i + 1 < this.value.length) {
-                output.add(" ");
-            }
+        returnValue = this.value[0].eval(env);
+    } else {
+        returnValue = this;
+    }
+    if (inParenthesis) {
+        env.outOfParenthesis();
+    }
+    if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) {
+        returnValue = new(Paren)(returnValue);
+    }
+    return returnValue;
+};
+Expression.prototype.genCSS = function (env, output) {
+    for(var i = 0; i < this.value.length; i++) {
+        this.value[i].genCSS(env, output);
+        if (i + 1 < this.value.length) {
+            output.add(" ");
         }
-    },
-    toCSS: tree.toCSS,
-    throwAwayComments: function () {
-        this.value = this.value.filter(function(v) {
-            return !(v instanceof tree.Comment);
-        });
     }
 };
-return Expression;
+Expression.prototype.throwAwayComments = function () {
+    this.value = this.value.filter(function(v) {
+        return !(v instanceof Comment);
+    });
 };
+module.exports = Expression;
diff --git a/lib/less/tree/extend.js b/lib/less/tree/extend.js
index 746bdeb..d4d0676 100644
--- a/lib/less/tree/extend.js
+++ b/lib/less/tree/extend.js
@@ -1,4 +1,4 @@
-module.exports = function (tree) {
+var Node = require("./node.js");
 
 var Extend = function Extend(selector, option, index) {
     this.selector = selector;
@@ -20,34 +20,32 @@ var Extend = function Extend(selector, option, index) {
 };
 Extend.next_id = 0;
 
-Extend.prototype = {
-    type: "Extend",
-    accept: function (visitor) {
-        this.selector = visitor.visit(this.selector);
-    },
-    eval: function (env) {
-        return new(Extend)(this.selector.eval(env), this.option, this.index);
-    },
-    clone: function (env) {
-        return new(Extend)(this.selector, this.option, this.index);
-    },
-    findSelfSelectors: function (selectors) {
-        var selfElements = [],
-            i,
-            selectorElements;
+Extend.prototype = new Node();
+Extend.prototype.type = "Extend";
+Extend.prototype.accept = function (visitor) {
+    this.selector = visitor.visit(this.selector);
+};
+Extend.prototype.eval = function (env) {
+    return new(Extend)(this.selector.eval(env), this.option, this.index);
+};
+Extend.prototype.clone = function (env) {
+    return new(Extend)(this.selector, this.option, this.index);
+};
+Extend.prototype.findSelfSelectors = function (selectors) {
+    var selfElements = [],
+        i,
+        selectorElements;
 
-        for(i = 0; i < selectors.length; i++) {
-            selectorElements = selectors[i].elements;
-            // duplicate the logic in genCSS function inside the selector node.
-            // future TODO - move both logics into the selector joiner visitor
-            if (i > 0 && selectorElements.length && selectorElements[0].combinator.value === "") {
-                selectorElements[0].combinator.value = ' ';
-            }
-            selfElements = selfElements.concat(selectors[i].elements);
+    for(i = 0; i < selectors.length; i++) {
+        selectorElements = selectors[i].elements;
+        // duplicate the logic in genCSS function inside the selector node.
+        // future TODO - move both logics into the selector joiner visitor
+        if (i > 0 && selectorElements.length && selectorElements[0].combinator.value === "") {
+            selectorElements[0].combinator.value = ' ';
         }
-
-        this.selfSelectors = [{ elements: selfElements }];
+        selfElements = selfElements.concat(selectors[i].elements);
     }
+
+    this.selfSelectors = [{ elements: selfElements }];
 };
-return Extend;
-};
+module.exports = Extend;
diff --git a/lib/less/tree/import.js b/lib/less/tree/import.js
index 2042e2e..c0d0459 100644
--- a/lib/less/tree/import.js
+++ b/lib/less/tree/import.js
@@ -1,4 +1,10 @@
-module.exports = function (tree) {
+var Node = require("./node.js"),
+    Media = require("./media.js"),
+    URL = require("./url.js"),
+    Quoted = require("./quoted.js"),
+    Ruleset = require("./ruleset.js"),
+    Anonymous = require("./anonymous.js");
+
 //
 // CSS @import node
 //
@@ -37,88 +43,85 @@ var Import = function (path, features, options, index, currentFileInfo) {
 // we end up with a flat structure, which can easily be imported in the parent
 // ruleset.
 //
-Import.prototype = {
-    type: "Import",
-    accept: function (visitor) {
+Import.prototype = new Node();
+Import.prototype.type = "Import";
+Import.prototype.accept = function (visitor) {
+    if (this.features) {
+        this.features = visitor.visit(this.features);
+    }
+    this.path = visitor.visit(this.path);
+    if (!this.options.inline && this.root) {
+        this.root = visitor.visit(this.root);
+    }
+};
+Import.prototype.genCSS = function (env, output) {
+    if (this.css) {
+        output.add("@import ", this.currentFileInfo, this.index);
+        this.path.genCSS(env, output);
         if (this.features) {
-            this.features = visitor.visit(this.features);
-        }
-        this.path = visitor.visit(this.path);
-        if (!this.options.inline && this.root) {
-            this.root = visitor.visit(this.root);
-        }
-    },
-    genCSS: function (env, output) {
-        if (this.css) {
-            output.add("@import ", this.currentFileInfo, this.index);
-            this.path.genCSS(env, output);
-            if (this.features) {
-                output.add(" ");
-                this.features.genCSS(env, output);
-            }
-            output.add(';');
-        }
-    },
-    toCSS: tree.toCSS,
-    getPath: function () {
-        if (this.path instanceof tree.Quoted) {
-            var path = this.path.value;
-            return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less';
-        } else if (this.path instanceof tree.URL) {
-            return this.path.value.value;
+            output.add(" ");
+            this.features.genCSS(env, output);
         }
-        return null;
-    },
-    evalForImport: function (env) {
-        return new(Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo);
-    },
-    evalPath: function (env) {
-        var path = this.path.eval(env);
-        var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
+        output.add(';');
+    }
+};
+Import.prototype.getPath = function () {
+    if (this.path instanceof Quoted) {
+        var path = this.path.value;
+        return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less';
+    } else if (this.path instanceof URL) {
+        return this.path.value.value;
+    }
+    return null;
+};
+Import.prototype.evalForImport = function (env) {
+    return new(Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo);
+};
+Import.prototype.evalPath = function (env) {
+    var path = this.path.eval(env);
+    var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
 
-        if (!(path instanceof tree.URL)) {
-            if (rootpath) {
-                var pathValue = path.value;
-                // Add the base path if the import is relative
-                if (pathValue && env.isPathRelative(pathValue)) {
-                    path.value = rootpath +pathValue;
-                }
+    if (!(path instanceof URL)) {
+        if (rootpath) {
+            var pathValue = path.value;
+            // Add the base path if the import is relative
+            if (pathValue && env.isPathRelative(pathValue)) {
+                path.value = rootpath +pathValue;
             }
-            path.value = env.normalizePath(path.value);
         }
+        path.value = env.normalizePath(path.value);
+    }
 
-        return path;
-    },
-    eval: function (env) {
-        var ruleset, features = this.features && this.features.eval(env);
+    return path;
+};
+Import.prototype.eval = function (env) {
+    var ruleset, features = this.features && this.features.eval(env);
 
+    if (this.skip) {
+        if (typeof this.skip === "function") {
+            this.skip = this.skip();
+        }
         if (this.skip) {
-            if (typeof this.skip === "function") {
-                this.skip = this.skip();
-            }
-            if (this.skip) {
-                return [];
-            }
+            return [];
         }
+    }
 
-        if (this.options.inline) {
-            //todo needs to reference css file not import
-            var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true, true);
-            return this.features ? new(tree.Media)([contents], this.features.value) : [contents];
-        } else if (this.css) {
-            var newImport = new(Import)(this.evalPath(env), features, this.options, this.index);
-            if (!newImport.css && this.error) {
-                throw this.error;
-            }
-            return newImport;
-        } else {
-            ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0));
+    if (this.options.inline) {
+        //todo needs to reference css file not import
+        var contents = new(Anonymous)(this.root, 0, {filename: this.importedFilename}, true, true);
+        return this.features ? new(Media)([contents], this.features.value) : [contents];
+    } else if (this.css) {
+        var newImport = new(Import)(this.evalPath(env), features, this.options, this.index);
+        if (!newImport.css && this.error) {
+            throw this.error;
+        }
+        return newImport;
+    } else {
+        ruleset = new(Ruleset)(null, this.root.rules.slice(0));
 
-            ruleset.evalImports(env);
+        ruleset.evalImports(env);
 
-            return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules;
-        }
+        return this.features ? new(Media)(ruleset.rules, this.features.value) : ruleset.rules;
     }
 };
-return Import;
-};
+module.exports = Import;
diff --git a/lib/less/tree/javascript.js b/lib/less/tree/javascript.js
index fdca7bf..a8f5533 100644
--- a/lib/less/tree/javascript.js
+++ b/lib/less/tree/javascript.js
@@ -1,57 +1,27 @@
-module.exports = function (tree) {
+var JsEvalNode = require("./js-eval-node.js"),
+    Dimension = require("./dimension.js"),
+    Quoted = require("./quoted.js"),
+    Anonymous = require("./anonymous.js");
 
 var JavaScript = function (string, index, escaped) {
     this.escaped = escaped;
     this.expression = string;
     this.index = index;
 };
-JavaScript.prototype = {
-    type: "JavaScript",
-    eval: function (env) {
-        var result,
-            that = this,
-            context = {};
+JavaScript.prototype = new JsEvalNode();
+JavaScript.prototype.type = "JavaScript";
+JavaScript.prototype.eval = function(env) {
+    var result = this.evaluateJavaScript(this.expression, env);
 
-        var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) {
-            return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env));
-        });
-
-        try {
-            expression = new(Function)('return (' + expression + ')');
-        } catch (e) {
-            throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" ,
-                    index: this.index };
-        }
-
-        var variables = env.frames[0].variables();
-        for (var k in variables) {
-            if (variables.hasOwnProperty(k)) {
-                /*jshint loopfunc:true */
-                context[k.slice(1)] = {
-                    value: variables[k].value,
-                    toJS: function () {
-                        return this.value.eval(env).toCSS();
-                    }
-                };
-            }
-        }
-
-        try {
-            result = expression.call(context);
-        } catch (e) {
-            throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" ,
-                    index: this.index };
-        }
-        if (typeof(result) === 'number') {
-            return new(tree.Dimension)(result);
-        } else if (typeof(result) === 'string') {
-            return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index);
-        } else if (Array.isArray(result)) {
-            return new(tree.Anonymous)(result.join(', '));
-        } else {
-            return new(tree.Anonymous)(result);
-        }
+    if (typeof(result) === 'number') {
+        return new(Dimension)(result);
+    } else if (typeof(result) === 'string') {
+        return new(Quoted)('"' + result + '"', result, this.escaped, this.index);
+    } else if (Array.isArray(result)) {
+        return new(Anonymous)(result.join(', '));
+    } else {
+        return new(Anonymous)(result);
     }
 };
-return JavaScript;
-};
+
+module.exports = JavaScript;
diff --git a/lib/less/tree/js-eval-node.js b/lib/less/tree/js-eval-node.js
new file mode 100644
index 0000000..0693873
--- /dev/null
+++ b/lib/less/tree/js-eval-node.js
@@ -0,0 +1,53 @@
+var Node = require("./node.js"),
+    Variable = require("./variable.js");
+
+var jsEvalNode = function() {
+};
+jsEvalNode.prototype = new Node();
+
+jsEvalNode.prototype.evaluateJavaScript = function (expression, env) {
+    var result,
+        that = this,
+        context = {};
+
+    expression = expression.replace(/@\{([\w-]+)\}/g, function (_, name) {
+        return that.jsify(new(Variable)('@' + name, that.index).eval(env));
+    });
+
+    try {
+        expression = new(Function)('return (' + expression + ')');
+    } catch (e) {
+        throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" ,
+            index: this.index };
+    }
+
+    var variables = env.frames[0].variables();
+    for (var k in variables) {
+        if (variables.hasOwnProperty(k)) {
+            /*jshint loopfunc:true */
+            context[k.slice(1)] = {
+                value: variables[k].value,
+                toJS: function () {
+                    return this.value.eval(env).toCSS();
+                }
+            };
+        }
+    }
+
+    try {
+        result = expression.call(context);
+    } catch (e) {
+        throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" ,
+            index: this.index };
+    }
+    return result;
+};
+jsEvalNode.prototype.jsify = function (obj) {
+    if (Array.isArray(obj.value) && (obj.value.length > 1)) {
+        return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']';
+    } else {
+        return obj.toCSS();
+    }
+};
+
+module.exports = jsEvalNode;
diff --git a/lib/less/tree/keyword.js b/lib/less/tree/keyword.js
index 46f5648..7f99a71 100644
--- a/lib/less/tree/keyword.js
+++ b/lib/less/tree/keyword.js
@@ -1,27 +1,21 @@
-module.exports = function (tree) {
+var Node = require("./node.js");
 
 var Keyword = function (value) { this.value = value; };
-Keyword.prototype = {
-    type: "Keyword",
-    eval: function () { return this; },
-    genCSS: function (env, output) {
-        if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; }
-        output.add(this.value);
-    },
-    toCSS: tree.toCSS,
-    compare: function (other) {
-        if (other instanceof Keyword) {
-            return other.value === this.value ? 0 : 1;
-        } else {
-            return -1;
-        }
+Keyword.prototype = new Node();
+Keyword.prototype.type = "Keyword";
+Keyword.prototype.genCSS = function (env, output) {
+    if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; }
+    output.add(this.value);
+};
+Keyword.prototype.compare = function (other) {
+    if (other instanceof Keyword) {
+        return other.value === this.value ? 0 : 1;
+    } else {
+        return -1;
     }
 };
 
-//TODO move?
-tree.True = new(Keyword)('true');
-tree.False = new(Keyword)('false');
-
-return Keyword;
+Keyword.True = new(Keyword)('true');
+Keyword.False = new(Keyword)('false');
 
-};
+module.exports = Keyword;
diff --git a/lib/less/tree/media.js b/lib/less/tree/media.js
index 494e229..5926b32 100644
--- a/lib/less/tree/media.js
+++ b/lib/less/tree/media.js
@@ -1,4 +1,10 @@
-module.exports = function (tree) {
+var Ruleset = require("./ruleset.js"),
+    Value = require("./value.js"),
+    Element = require("./element.js"),
+    Selector = require("./selector.js"),
+    Anonymous = require("./anonymous.js"),
+    Expression = require("./expression.js"),
+    Directive = require("./directive.js");
 
 var Media = function (value, features, index, currentFileInfo) {
     this.index = index;
@@ -6,152 +12,150 @@ var Media = function (value, features, index, currentFileInfo) {
 
     var selectors = this.emptySelectors();
 
-    this.features = new(tree.Value)(features);
-    this.rules = [new(tree.Ruleset)(selectors, value)];
+    this.features = new(Value)(features);
+    this.rules = [new(Ruleset)(selectors, value)];
     this.rules[0].allowImports = true;
 };
-Media.prototype = {
-    type: "Media",
-    accept: function (visitor) {
-        if (this.features) {
-            this.features = visitor.visit(this.features);
-        }
-        if (this.rules) {
-            this.rules = visitor.visitArray(this.rules);
-        }
-    },
-    genCSS: function (env, output) {
-        output.add('@media ', this.currentFileInfo, this.index);
-        this.features.genCSS(env, output);
-        tree.outputRuleset(env, output, this.rules);
-    },
-    toCSS: tree.toCSS,
-    eval: function (env) {
-        if (!env.mediaBlocks) {
-            env.mediaBlocks = [];
-            env.mediaPath = [];
-        }
+Media.prototype = new Directive();
+Media.prototype.type = "Media";
+Media.prototype.isRulesetLike = true;
+Media.prototype.accept = function (visitor) {
+    if (this.features) {
+        this.features = visitor.visit(this.features);
+    }
+    if (this.rules) {
+        this.rules = visitor.visitArray(this.rules);
+    }
+};
+Media.prototype.genCSS = function (env, output) {
+    output.add('@media ', this.currentFileInfo, this.index);
+    this.features.genCSS(env, output);
+    this.outputRuleset(env, output, this.rules);
+};
+Media.prototype.eval = function (env) {
+    if (!env.mediaBlocks) {
+        env.mediaBlocks = [];
+        env.mediaPath = [];
+    }
 
-        var media = new(Media)(null, [], this.index, this.currentFileInfo);
-        if(this.debugInfo) {
-            this.rules[0].debugInfo = this.debugInfo;
-            media.debugInfo = this.debugInfo;
-        }
-        var strictMathBypass = false;
-        if (!env.strictMath) {
-            strictMathBypass = true;
-            env.strictMath = true;
-        }
-        try {
-            media.features = this.features.eval(env);
-        }
-        finally {
-            if (strictMathBypass) {
-                env.strictMath = false;
-            }
+    var media = new(Media)(null, [], this.index, this.currentFileInfo);
+    if(this.debugInfo) {
+        this.rules[0].debugInfo = this.debugInfo;
+        media.debugInfo = this.debugInfo;
+    }
+    var strictMathBypass = false;
+    if (!env.strictMath) {
+        strictMathBypass = true;
+        env.strictMath = true;
+    }
+    try {
+        media.features = this.features.eval(env);
+    }
+    finally {
+        if (strictMathBypass) {
+            env.strictMath = false;
         }
+    }
 
-        env.mediaPath.push(media);
-        env.mediaBlocks.push(media);
-
-        env.frames.unshift(this.rules[0]);
-        media.rules = [this.rules[0].eval(env)];
-        env.frames.shift();
-
-        env.mediaPath.pop();
-
-        return env.mediaPath.length === 0 ? media.evalTop(env) :
-                    media.evalNested(env);
-    },
-    variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); },
-    find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); },
-    rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); },
-    emptySelectors: function() {
-        var el = new(tree.Element)('', '&', this.index, this.currentFileInfo),
-            sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)];
-        sels[0].mediaEmpty = true;
-        return sels;
-    },
-    markReferenced: function () {
-        var i, rules = this.rules[0].rules;
-        this.rules[0].markReferenced();
-        this.isReferenced = true;
-        for (i = 0; i < rules.length; i++) {
-            if (rules[i].markReferenced) {
-                rules[i].markReferenced();
-            }
-        }
-    },
+    env.mediaPath.push(media);
+    env.mediaBlocks.push(media);
 
-    evalTop: function (env) {
-        var result = this;
+    env.frames.unshift(this.rules[0]);
+    media.rules = [this.rules[0].eval(env)];
+    env.frames.shift();
 
-        // Render all dependent Media blocks.
-        if (env.mediaBlocks.length > 1) {
-            var selectors = this.emptySelectors();
-            result = new(tree.Ruleset)(selectors, env.mediaBlocks);
-            result.multiMedia = true;
+    env.mediaPath.pop();
+
+    return env.mediaPath.length === 0 ? media.evalTop(env) :
+                media.evalNested(env);
+};
+//TODO merge with directive
+Media.prototype.variable = function (name) { return Ruleset.prototype.variable.call(this.rules[0], name); };
+Media.prototype.find = function () { return Ruleset.prototype.find.apply(this.rules[0], arguments); };
+Media.prototype.rulesets = function () { return Ruleset.prototype.rulesets.apply(this.rules[0]); };
+Media.prototype.emptySelectors = function() {
+    var el = new(Element)('', '&', this.index, this.currentFileInfo),
+        sels = [new(Selector)([el], null, null, this.index, this.currentFileInfo)];
+    sels[0].mediaEmpty = true;
+    return sels;
+};
+Media.prototype.markReferenced = function () {
+    var i, rules = this.rules[0].rules;
+    this.rules[0].markReferenced();
+    this.isReferenced = true;
+    for (i = 0; i < rules.length; i++) {
+        if (rules[i].markReferenced) {
+            rules[i].markReferenced();
         }
+    }
+};
+Media.prototype.evalTop = function (env) {
+    var result = this;
+
+    // Render all dependent Media blocks.
+    if (env.mediaBlocks.length > 1) {
+        var selectors = this.emptySelectors();
+        result = new(Ruleset)(selectors, env.mediaBlocks);
+        result.multiMedia = true;
+    }
 
-        delete env.mediaBlocks;
-        delete env.mediaPath;
+    delete env.mediaBlocks;
+    delete env.mediaPath;
 
-        return result;
-    },
-    evalNested: function (env) {
-        var i, value,
-            path = env.mediaPath.concat([this]);
+    return result;
+};
+Media.prototype.evalNested = function (env) {
+    var i, value,
+        path = env.mediaPath.concat([this]);
+
+    // Extract the media-query conditions separated with `,` (OR).
+    for (i = 0; i < path.length; i++) {
+        value = path[i].features instanceof Value ?
+                    path[i].features.value : path[i].features;
+        path[i] = Array.isArray(value) ? value : [value];
+    }
 
-        // Extract the media-query conditions separated with `,` (OR).
-        for (i = 0; i < path.length; i++) {
-            value = path[i].features instanceof tree.Value ?
-                        path[i].features.value : path[i].features;
-            path[i] = Array.isArray(value) ? value : [value];
+    // Trace all permutations to generate the resulting media-query.
+    //
+    // (a, b and c) with nested (d, e) ->
+    //    a and d
+    //    a and e
+    //    b and c and d
+    //    b and c and e
+    this.features = new(Value)(this.permute(path).map(function (path) {
+        path = path.map(function (fragment) {
+            return fragment.toCSS ? fragment : new(Anonymous)(fragment);
+        });
+
+        for(i = path.length - 1; i > 0; i--) {
+            path.splice(i, 0, new(Anonymous)("and"));
         }
 
-        // Trace all permutations to generate the resulting media-query.
-        //
-        // (a, b and c) with nested (d, e) ->
-        //    a and d
-        //    a and e
-        //    b and c and d
-        //    b and c and e
-        this.features = new(tree.Value)(this.permute(path).map(function (path) {
-            path = path.map(function (fragment) {
-                return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment);
-            });
-
-            for(i = path.length - 1; i > 0; i--) {
-                path.splice(i, 0, new(tree.Anonymous)("and"));
-            }
-
-            return new(tree.Expression)(path);
-        }));
-
-        // Fake a tree-node that doesn't output anything.
-        return new(tree.Ruleset)([], []);
-    },
-    permute: function (arr) {
-      if (arr.length === 0) {
-          return [];
-      } else if (arr.length === 1) {
-          return arr[0];
-      } else {
-          var result = [];
-          var rest = this.permute(arr.slice(1));
-          for (var i = 0; i < rest.length; i++) {
-              for (var j = 0; j < arr[0].length; j++) {
-                  result.push([arr[0][j]].concat(rest[i]));
-              }
+        return new(Expression)(path);
+    }));
+
+    // Fake a tree-node that doesn't output anything.
+    return new(Ruleset)([], []);
+};
+Media.prototype.permute = function (arr) {
+  if (arr.length === 0) {
+      return [];
+  } else if (arr.length === 1) {
+      return arr[0];
+  } else {
+      var result = [];
+      var rest = this.permute(arr.slice(1));
+      for (var i = 0; i < rest.length; i++) {
+          for (var j = 0; j < arr[0].length; j++) {
+              result.push([arr[0][j]].concat(rest[i]));
           }
-          return result;
       }
-    },
-    bubbleSelectors: function (selectors) {
-      if (!selectors)
-        return;
-      this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])];
-    }
+      return result;
+  }
 };
-return Media;
+Media.prototype.bubbleSelectors = function (selectors) {
+  if (!selectors)
+    return;
+  this.rules = [new(Ruleset)(selectors.slice(0), [this.rules[0]])];
 };
+module.exports = Media;
diff --git a/lib/less/tree/mixin-call.js b/lib/less/tree/mixin-call.js
index e8faebf..e577b87 100644
--- a/lib/less/tree/mixin-call.js
+++ b/lib/less/tree/mixin-call.js
@@ -1,153 +1,154 @@
-module.exports = function (tree) {
+var Node = require("./node.js"),
+    Selector = require("./selector.js"),
+    MixinDefinition = require("./mixin-definition.js"),
+    defaultFunc = require("../functions/default.js");
 
-var Call = function (elements, args, index, currentFileInfo, important) {
-    this.selector = new(tree.Selector)(elements);
+var MixinCall = function (elements, args, index, currentFileInfo, important) {
+    this.selector = new(Selector)(elements);
     this.arguments = (args && args.length) ? args : null;
     this.index = index;
     this.currentFileInfo = currentFileInfo;
     this.important = important;
 };
-Call.prototype = {
-    type: "MixinCall",
-    accept: function (visitor) {
-        if (this.selector) {
-            this.selector = visitor.visit(this.selector);
-        }
-        if (this.arguments) {
-            this.arguments = visitor.visitArray(this.arguments);
-        }
-    },
-    eval: function (env) {
-        var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule,
-            candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc,
-            defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset;
+MixinCall.prototype = new Node();
+MixinCall.prototype.type = "MixinCall";
+MixinCall.prototype.accept = function (visitor) {
+    if (this.selector) {
+        this.selector = visitor.visit(this.selector);
+    }
+    if (this.arguments) {
+        this.arguments = visitor.visitArray(this.arguments);
+    }
+};
+MixinCall.prototype.eval = function (env) {
+    var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule,
+        candidates = [], candidate, conditionResult = [],
+        defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset;
 
-        args = this.arguments && this.arguments.map(function (a) {
-            return { name: a.name, value: a.value.eval(env) };
-        });
+    args = this.arguments && this.arguments.map(function (a) {
+        return { name: a.name, value: a.value.eval(env) };
+    });
 
-        for (i = 0; i < env.frames.length; i++) {
-            if ((mixins = env.frames[i].find(this.selector)).length > 0) {
-                isOneFound = true;
+    for (i = 0; i < env.frames.length; i++) {
+        if ((mixins = env.frames[i].find(this.selector)).length > 0) {
+            isOneFound = true;
 
-                // To make `default()` function independent of definition order we have two "subpasses" here.
-                // At first we evaluate each guard *twice* (with `default() == true` and `default() == false`),
-                // and build candidate list with corresponding flags. Then, when we know all possible matches,
-                // we make a final decision.
+            // To make `default()` function independent of definition order we have two "subpasses" here.
+            // At first we evaluate each guard *twice* (with `default() == true` and `default() == false`),
+            // and build candidate list with corresponding flags. Then, when we know all possible matches,
+            // we make a final decision.
 
-                for (m = 0; m < mixins.length; m++) {
-                    mixin = mixins[m];
-                    isRecursive = false;
-                    for(f = 0; f < env.frames.length; f++) {
-                        if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) {
-                            isRecursive = true;
-                            break;
-                        }
-                    }
-                    if (isRecursive) {
-                        continue;
+            for (m = 0; m < mixins.length; m++) {
+                mixin = mixins[m];
+                isRecursive = false;
+                for(f = 0; f < env.frames.length; f++) {
+                    if ((!(mixin instanceof MixinDefinition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) {
+                        isRecursive = true;
+                        break;
                     }
+                }
+                if (isRecursive) {
+                    continue;
+                }
 
-                    if (mixin.matchArgs(args, env)) {
-                        candidate = {mixin: mixin, group: defNone};
+                if (mixin.matchArgs(args, env)) {
+                    candidate = {mixin: mixin, group: defNone};
 
-                        if (mixin.matchCondition) {
-                            for (f = 0; f < 2; f++) {
-                                defaultFunc.value(f);
-                                conditionResult[f] = mixin.matchCondition(args, env);
+                    if (mixin.matchCondition) {
+                        for (f = 0; f < 2; f++) {
+                            defaultFunc.value(f);
+                            conditionResult[f] = mixin.matchCondition(args, env);
+                        }
+                        if (conditionResult[0] || conditionResult[1]) {
+                            if (conditionResult[0] != conditionResult[1]) {
+                                candidate.group = conditionResult[1] ?
+                                    defTrue : defFalse;
                             }
-                            if (conditionResult[0] || conditionResult[1]) {
-                                if (conditionResult[0] != conditionResult[1]) {
-                                    candidate.group = conditionResult[1] ?
-                                        defTrue : defFalse;
-                                }
 
-                                candidates.push(candidate);
-                            }
-                        }
-                        else {
                             candidates.push(candidate);
                         }
-
-                        match = true;
                     }
+                    else {
+                        candidates.push(candidate);
+                    }
+
+                    match = true;
                 }
+            }
 
-                defaultFunc.reset();
+            defaultFunc.reset();
 
-                count = [0, 0, 0];
-                for (m = 0; m < candidates.length; m++) {
-                    count[candidates[m].group]++;
-                }
+            count = [0, 0, 0];
+            for (m = 0; m < candidates.length; m++) {
+                count[candidates[m].group]++;
+            }
 
-                if (count[defNone] > 0) {
-                    defaultResult = defFalse;
-                } else {
-                    defaultResult = defTrue;
-                    if ((count[defTrue] + count[defFalse]) > 1) {
-                        throw { type: 'Runtime',
-                            message: 'Ambiguous use of `default()` found when matching for `'
-                                + this.format(args) + '`',
-                            index: this.index, filename: this.currentFileInfo.filename };
-                    }
+            if (count[defNone] > 0) {
+                defaultResult = defFalse;
+            } else {
+                defaultResult = defTrue;
+                if ((count[defTrue] + count[defFalse]) > 1) {
+                    throw { type: 'Runtime',
+                        message: 'Ambiguous use of `default()` found when matching for `'
+                            + this.format(args) + '`',
+                        index: this.index, filename: this.currentFileInfo.filename };
                 }
+            }
 
-                for (m = 0; m < candidates.length; m++) {
-                    candidate = candidates[m].group;
-                    if ((candidate === defNone) || (candidate === defaultResult)) {
-                        try {
-                            mixin = candidates[m].mixin;
-                            if (!(mixin instanceof tree.mixin.Definition)) {
-                                originalRuleset = mixin.originalRuleset || mixin;
-                                mixin = new tree.mixin.Definition("", [], mixin.rules, null, false);
-                                mixin.originalRuleset = originalRuleset;
-                            }
-                            Array.prototype.push.apply(
-                                  rules, mixin.evalCall(env, args, this.important).rules);
-                        } catch (e) {
-                            throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack };
+            for (m = 0; m < candidates.length; m++) {
+                candidate = candidates[m].group;
+                if ((candidate === defNone) || (candidate === defaultResult)) {
+                    try {
+                        mixin = candidates[m].mixin;
+                        if (!(mixin instanceof MixinDefinition)) {
+                            originalRuleset = mixin.originalRuleset || mixin;
+                            mixin = new MixinDefinition("", [], mixin.rules, null, false);
+                            mixin.originalRuleset = originalRuleset;
                         }
+                        Array.prototype.push.apply(
+                              rules, mixin.evalCall(env, args, this.important).rules);
+                    } catch (e) {
+                        throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack };
                     }
                 }
+            }
 
-                if (match) {
-                    if (!this.currentFileInfo || !this.currentFileInfo.reference) {
-                        for (i = 0; i < rules.length; i++) {
-                            rule = rules[i];
-                            if (rule.markReferenced) {
-                                rule.markReferenced();
-                            }
+            if (match) {
+                if (!this.currentFileInfo || !this.currentFileInfo.reference) {
+                    for (i = 0; i < rules.length; i++) {
+                        rule = rules[i];
+                        if (rule.markReferenced) {
+                            rule.markReferenced();
                         }
                     }
-                    return rules;
                 }
+                return rules;
             }
         }
-        if (isOneFound) {
-            throw { type:    'Runtime',
-                    message: 'No matching definition was found for `' + this.format(args) + '`',
-                    index:   this.index, filename: this.currentFileInfo.filename };
-        } else {
-            throw { type:    'Name',
-                    message: this.selector.toCSS().trim() + " is undefined",
-                    index:   this.index, filename: this.currentFileInfo.filename };
-        }
-    },
-    format: function (args) {
-        return this.selector.toCSS().trim() + '(' +
-            (args ? args.map(function (a) {
-                var argValue = "";
-                if (a.name) {
-                    argValue += a.name + ":";
-                }
-                if (a.value.toCSS) {
-                    argValue += a.value.toCSS();
-                } else {
-                    argValue += "???";
-                }
-                return argValue;
-            }).join(', ') : "") + ")";
+    }
+    if (isOneFound) {
+        throw { type:    'Runtime',
+                message: 'No matching definition was found for `' + this.format(args) + '`',
+                index:   this.index, filename: this.currentFileInfo.filename };
+    } else {
+        throw { type:    'Name',
+                message: this.selector.toCSS().trim() + " is undefined",
+                index:   this.index, filename: this.currentFileInfo.filename };
     }
 };
-return Call;
+MixinCall.prototype.format = function (args) {
+    return this.selector.toCSS().trim() + '(' +
+        (args ? args.map(function (a) {
+            var argValue = "";
+            if (a.name) {
+                argValue += a.name + ":";
+            }
+            if (a.value.toCSS) {
+                argValue += a.value.toCSS();
+            } else {
+                argValue += "???";
+            }
+            return argValue;
+        }).join(', ') : "") + ")";
 };
+module.exports = MixinCall;
diff --git a/lib/less/tree/mixin-definition.js b/lib/less/tree/mixin-definition.js
index 7a891d6..044250b 100644
--- a/lib/less/tree/mixin-definition.js
+++ b/lib/less/tree/mixin-definition.js
@@ -1,8 +1,13 @@
-module.exports = function (tree) {
+var Selector = require("./selector.js"),
+    Element = require("./element.js"),
+    Ruleset = require("./ruleset.js"),
+    Rule = require("./rule.js"),
+    Expression = require("./expression.js"),
+    contexts = require("../env.js");
 
 var Definition = function (name, params, rules, condition, variadic, frames) {
     this.name = name;
-    this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])];
+    this.selectors = [new(Selector)([new(Element)(null, name, this.index, this.currentFileInfo)])];
     this.params = params;
     this.condition = condition;
     this.variadic = variadic;
@@ -13,153 +18,146 @@ var Definition = function (name, params, rules, condition, variadic, frames) {
         if (!p.name || (p.name && !p.value)) { return count + 1; }
         else                                 { return count; }
     }, 0);
-    this.parent = tree.Ruleset.prototype;
     this.frames = frames;
 };
-Definition.prototype = {
-    type: "MixinDefinition",
-    accept: function (visitor) {
-        if (this.params && this.params.length) {
-            this.params = visitor.visitArray(this.params);
-        }
-        this.rules = visitor.visitArray(this.rules);
-        if (this.condition) {
-            this.condition = visitor.visit(this.condition);
-        }
-    },
-    variable:  function (name) { return this.parent.variable.call(this, name); },
-    variables: function ()     { return this.parent.variables.call(this); },
-    find:      function ()     { return this.parent.find.apply(this, arguments); },
-    rulesets:  function ()     { return this.parent.rulesets.apply(this); },
-
-    evalParams: function (env, mixinEnv, args, evaldArguments) {
-        /*jshint boss:true */
-        var frame = new(tree.Ruleset)(null, null),
-            varargs, arg,
-            params = this.params.slice(0),
-            i, j, val, name, isNamedFound, argIndex, argsLength = 0;
-
-        mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames));
-
-        if (args) {
-            args = args.slice(0);
-            argsLength = args.length;
-
-            for(i = 0; i < argsLength; i++) {
-                arg = args[i];
-                if (name = (arg && arg.name)) {
-                    isNamedFound = false;
-                    for(j = 0; j < params.length; j++) {
-                        if (!evaldArguments[j] && name === params[j].name) {
-                            evaldArguments[j] = arg.value.eval(env);
-                            frame.prependRule(new(tree.Rule)(name, arg.value.eval(env)));
-                            isNamedFound = true;
-                            break;
-                        }
-                    }
-                    if (isNamedFound) {
-                        args.splice(i, 1);
-                        i--;
-                        continue;
-                    } else {
-                        throw { type: 'Runtime', message: "Named argument for " + this.name +
-                            ' ' + args[i].name + ' not found' };
+Definition.prototype = new Ruleset();
+Definition.prototype.type = "MixinDefinition";
+Definition.prototype.evalFirst = true;
+Definition.prototype.accept = function (visitor) {
+    if (this.params && this.params.length) {
+        this.params = visitor.visitArray(this.params);
+    }
+    this.rules = visitor.visitArray(this.rules);
+    if (this.condition) {
+        this.condition = visitor.visit(this.condition);
+    }
+};
+Definition.prototype.evalParams = function (env, mixinEnv, args, evaldArguments) {
+    /*jshint boss:true */
+    var frame = new(Ruleset)(null, null),
+        varargs, arg,
+        params = this.params.slice(0),
+        i, j, val, name, isNamedFound, argIndex, argsLength = 0;
+
+    mixinEnv = new contexts.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames));
+
+    if (args) {
+        args = args.slice(0);
+        argsLength = args.length;
+
+        for(i = 0; i < argsLength; i++) {
+            arg = args[i];
+            if (name = (arg && arg.name)) {
+                isNamedFound = false;
+                for(j = 0; j < params.length; j++) {
+                    if (!evaldArguments[j] && name === params[j].name) {
+                        evaldArguments[j] = arg.value.eval(env);
+                        frame.prependRule(new(Rule)(name, arg.value.eval(env)));
+                        isNamedFound = true;
+                        break;
                     }
                 }
+                if (isNamedFound) {
+                    args.splice(i, 1);
+                    i--;
+                    continue;
+                } else {
+                    throw { type: 'Runtime', message: "Named argument for " + this.name +
+                        ' ' + args[i].name + ' not found' };
+                }
             }
         }
-        argIndex = 0;
-        for (i = 0; i < params.length; i++) {
-            if (evaldArguments[i]) { continue; }
+    }
+    argIndex = 0;
+    for (i = 0; i < params.length; i++) {
+        if (evaldArguments[i]) { continue; }
 
-            arg = args && args[argIndex];
+        arg = args && args[argIndex];
 
-            if (name = params[i].name) {
-                if (params[i].variadic) {
-                    varargs = [];
-                    for (j = argIndex; j < argsLength; j++) {
-                        varargs.push(args[j].value.eval(env));
-                    }
-                    frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env)));
+        if (name = params[i].name) {
+            if (params[i].variadic) {
+                varargs = [];
+                for (j = argIndex; j < argsLength; j++) {
+                    varargs.push(args[j].value.eval(env));
+                }
+                frame.prependRule(new(Rule)(name, new(Expression)(varargs).eval(env)));
+            } else {
+                val = arg && arg.value;
+                if (val) {
+                    val = val.eval(env);
+                } else if (params[i].value) {
+                    val = params[i].value.eval(mixinEnv);
+                    frame.resetCache();
                 } else {
-                    val = arg && arg.value;
-                    if (val) {
-                        val = val.eval(env);
-                    } else if (params[i].value) {
-                        val = params[i].value.eval(mixinEnv);
-                        frame.resetCache();
-                    } else {
-                        throw { type: 'Runtime', message: "wrong number of arguments for " + this.name +
-                            ' (' + argsLength + ' for ' + this.arity + ')' };
-                    }
-
-                    frame.prependRule(new(tree.Rule)(name, val));
-                    evaldArguments[i] = val;
+                    throw { type: 'Runtime', message: "wrong number of arguments for " + this.name +
+                        ' (' + argsLength + ' for ' + this.arity + ')' };
                 }
-            }
 
-            if (params[i].variadic && args) {
-                for (j = argIndex; j < argsLength; j++) {
-                    evaldArguments[j] = args[j].value.eval(env);
-                }
+                frame.prependRule(new(Rule)(name, val));
+                evaldArguments[i] = val;
             }
-            argIndex++;
         }
 
-        return frame;
-    },
-    eval: function (env) {
-        return new tree.mixin.Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0));
-    },
-    evalCall: function (env, args, important) {
-        var _arguments = [],
-            mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames,
-            frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments),
-            rules, ruleset;
+        if (params[i].variadic && args) {
+            for (j = argIndex; j < argsLength; j++) {
+                evaldArguments[j] = args[j].value.eval(env);
+            }
+        }
+        argIndex++;
+    }
 
-        frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env)));
+    return frame;
+};
+Definition.prototype.eval = function (env) {
+    return new Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0));
+};
+Definition.prototype.evalCall = function (env, args, important) {
+    var _arguments = [],
+        mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames,
+        frame = this.evalParams(env, new(contexts.evalEnv)(env, mixinFrames), args, _arguments),
+        rules, ruleset;
 
-        rules = this.rules.slice(0);
+    frame.prependRule(new(Rule)('@arguments', new(Expression)(_arguments).eval(env)));
 
-        ruleset = new(tree.Ruleset)(null, rules);
-        ruleset.originalRuleset = this;
-        ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames)));
-        if (important) {
-            ruleset = this.parent.makeImportant.apply(ruleset);
-        }
-        return ruleset;
-    },
-    matchCondition: function (args, env) {
-        if (this.condition && !this.condition.eval(
-            new(tree.evalEnv)(env,
-                [this.evalParams(env, new(tree.evalEnv)(env, this.frames ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables
-                    .concat(this.frames) // the parent namespace/mixin frames
-                    .concat(env.frames)))) { // the current environment frames
-            return false;
-        }
-        return true;
-    },
-    matchArgs: function (args, env) {
-        var argsLength = (args && args.length) || 0, len;
+    rules = this.rules.slice(0);
 
-        if (! this.variadic) {
-            if (argsLength < this.required)                               { return false; }
-            if (argsLength > this.params.length)                          { return false; }
-        } else {
-            if (argsLength < (this.required - 1))                         { return false; }
-        }
+    ruleset = new(Ruleset)(null, rules);
+    ruleset.originalRuleset = this;
+    ruleset = ruleset.eval(new(contexts.evalEnv)(env, [this, frame].concat(mixinFrames)));
+    if (important) {
+        ruleset = this.makeImportant.apply(ruleset);
+    }
+    return ruleset;
+};
+Definition.prototype.matchCondition = function (args, env) {
+    if (this.condition && !this.condition.eval(
+        new(contexts.evalEnv)(env,
+            [this.evalParams(env, new(contexts.evalEnv)(env, this.frames ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables
+                .concat(this.frames) // the parent namespace/mixin frames
+                .concat(env.frames)))) { // the current environment frames
+        return false;
+    }
+    return true;
+};
+Definition.prototype.matchArgs = function (args, env) {
+    var argsLength = (args && args.length) || 0, len;
+
+    if (! this.variadic) {
+        if (argsLength < this.required)                               { return false; }
+        if (argsLength > this.params.length)                          { return false; }
+    } else {
+        if (argsLength < (this.required - 1))                         { return false; }
+    }
 
-        len = Math.min(argsLength, this.arity);
+    len = Math.min(argsLength, this.arity);
 
-        for (var i = 0; i < len; i++) {
-            if (!this.params[i].name && !this.params[i].variadic) {
-                if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) {
-                    return false;
-                }
+    for (var i = 0; i < len; i++) {
+        if (!this.params[i].name && !this.params[i].variadic) {
+            if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) {
+                return false;
             }
         }
-        return true;
     }
+    return true;
 };
-return Definition;
-};
+module.exports = Definition;
diff --git a/lib/less/tree/negative.js b/lib/less/tree/negative.js
index 6f3e9af..d33a315 100644
--- a/lib/less/tree/negative.js
+++ b/lib/less/tree/negative.js
@@ -1,24 +1,20 @@
-module.exports = function (tree) {
+var Node = require("./node.js"),
+    Operation = require("./operation.js"),
+    Dimension = require("./dimension.js");
 
 var Negative = function (node) {
     this.value = node;
 };
-Negative.prototype = {
-    type: "Negative",
-    accept: function (visitor) {
-        this.value = visitor.visit(this.value);
-    },
-    genCSS: function (env, output) {
-        output.add('-');
-        this.value.genCSS(env, output);
-    },
-    toCSS: tree.toCSS,
-    eval: function (env) {
-        if (env.isMathOn()) {
-            return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env);
-        }
-        return new(Negative)(this.value.eval(env));
-    }
+Negative.prototype = new Node();
+Negative.prototype.type = "Negative";
+Negative.prototype.genCSS = function (env, output) {
+    output.add('-');
+    this.value.genCSS(env, output);
 };
-return Negative;
+Negative.prototype.eval = function (env) {
+    if (env.isMathOn()) {
+        return (new(Operation)('*', [new(Dimension)(-1), this.value])).eval(env);
+    }
+    return new(Negative)(this.value.eval(env));
 };
+module.exports = Negative;
diff --git a/lib/less/tree/node.js b/lib/less/tree/node.js
new file mode 100644
index 0000000..c6bdd57
--- /dev/null
+++ b/lib/less/tree/node.js
@@ -0,0 +1,35 @@
+var Node = function() {
+};
+Node.prototype.toCSS = function (env) {
+    var strs = [];
+    this.genCSS(env, {
+        add: function(chunk, fileInfo, index) {
+            strs.push(chunk);
+        },
+        isEmpty: function () {
+            return strs.length === 0;
+        }
+    });
+    return strs.join('');
+};
+Node.prototype.genCSS = function (env, output) {
+    output.add(this.value);
+};
+Node.prototype.accept = function (visitor) {
+    this.value = visitor.visit(this.value);
+};
+Node.prototype.eval = function () { return this; };
+Node.prototype._operate = function (env, op, a, b) {
+    switch (op) {
+        case '+': return a + b;
+        case '-': return a - b;
+        case '*': return a * b;
+        case '/': return a / b;
+    }
+};
+Node.prototype.fround = function(env, value) {
+    var precision = env && env.numPrecision;
+    //add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded...
+    return (precision == null) ? value : Number((value + 2e-16).toFixed(precision));
+};
+module.exports = Node;
diff --git a/lib/less/tree/operation.js b/lib/less/tree/operation.js
index 53dc37b..6b558e2 100644
--- a/lib/less/tree/operation.js
+++ b/lib/less/tree/operation.js
@@ -1,60 +1,48 @@
-module.exports = function (tree) {
+var Node = require("./node.js"),
+    Color = require("./color.js"),
+    Dimension = require("./dimension.js");
 
 var Operation = function (op, operands, isSpaced) {
     this.op = op.trim();
     this.operands = operands;
     this.isSpaced = isSpaced;
 };
-Operation.prototype = {
-    type: "Operation",
-    accept: function (visitor) {
-        this.operands = visitor.visit(this.operands);
-    },
-    eval: function (env) {
-        var a = this.operands[0].eval(env),
-            b = this.operands[1].eval(env);
-
-        if (env.isMathOn()) {
-            if (a instanceof tree.Dimension && b instanceof tree.Color) {
-                a = a.toColor();
-            }
-            if (b instanceof tree.Dimension && a instanceof tree.Color) {
-                b = b.toColor();
-            }
-            if (!a.operate) {
-                throw { type: "Operation",
-                        message: "Operation on an invalid type" };
-            }
+Operation.prototype = new Node();
+Operation.prototype.type = "Operation";
+Operation.prototype.accept = function (visitor) {
+    this.operands = visitor.visit(this.operands);
+};
+Operation.prototype.eval = function (env) {
+    var a = this.operands[0].eval(env),
+        b = this.operands[1].eval(env);
 
-            return a.operate(env, this.op, b);
-        } else {
-            return new(Operation)(this.op, [a, b], this.isSpaced);
+    if (env.isMathOn()) {
+        if (a instanceof Dimension && b instanceof Color) {
+            a = a.toColor();
         }
-    },
-    genCSS: function (env, output) {
-        this.operands[0].genCSS(env, output);
-        if (this.isSpaced) {
-            output.add(" ");
+        if (b instanceof Dimension && a instanceof Color) {
+            b = b.toColor();
         }
-        output.add(this.op);
-        if (this.isSpaced) {
-            output.add(" ");
+        if (!a.operate) {
+            throw { type: "Operation",
+                    message: "Operation on an invalid type" };
         }
-        this.operands[1].genCSS(env, output);
-    },
-    toCSS: tree.toCSS
-};
 
-// todo move!
-tree.operate = function (env, op, a, b) {
-    switch (op) {
-        case '+': return a + b;
-        case '-': return a - b;
-        case '*': return a * b;
-        case '/': return a / b;
+        return a.operate(env, this.op, b);
+    } else {
+        return new(Operation)(this.op, [a, b], this.isSpaced);
     }
 };
-    
-return Operation;
-
+Operation.prototype.genCSS = function (env, output) {
+    this.operands[0].genCSS(env, output);
+    if (this.isSpaced) {
+        output.add(" ");
+    }
+    output.add(this.op);
+    if (this.isSpaced) {
+        output.add(" ");
+    }
+    this.operands[1].genCSS(env, output);
 };
+
+module.exports = Operation;
diff --git a/lib/less/tree/paren.js b/lib/less/tree/paren.js
index 5c7dca8..ab9b22a 100644
--- a/lib/less/tree/paren.js
+++ b/lib/less/tree/paren.js
@@ -1,22 +1,16 @@
-module.exports = function (tree) {
+var Node = require("./node.js");
 
 var Paren = function (node) {
     this.value = node;
 };
-Paren.prototype = {
-    type: "Paren",
-    accept: function (visitor) {
-        this.value = visitor.visit(this.value);
-    },
-    genCSS: function (env, output) {
-        output.add('(');
-        this.value.genCSS(env, output);
-        output.add(')');
-    },
-    toCSS: tree.toCSS,
-    eval: function (env) {
-        return new(Paren)(this.value.eval(env));
-    }
+Paren.prototype = new Node();
+Paren.prototype.type = "Paren";
+Paren.prototype.genCSS = function (env, output) {
+    output.add('(');
+    this.value.genCSS(env, output);
+    output.add(')');
 };
-return Paren;
+Paren.prototype.eval = function (env) {
+    return new(Paren)(this.value.eval(env));
 };
+module.exports = Paren;
diff --git a/lib/less/tree/quoted.js b/lib/less/tree/quoted.js
index b0d3805..c46f8d5 100644
--- a/lib/less/tree/quoted.js
+++ b/lib/less/tree/quoted.js
@@ -1,4 +1,5 @@
-module.exports = function (tree) {
+var JsEvalNode = require("./js-eval-node.js"),
+    Variable = require("./variable.js");
 
 var Quoted = function (str, content, escaped, index, currentFileInfo) {
     this.escaped = escaped;
@@ -7,50 +8,47 @@ var Quoted = function (str, content, escaped, index, currentFileInfo) {
     this.index = index;
     this.currentFileInfo = currentFileInfo;
 };
-Quoted.prototype = {
-    type: "Quoted",
-    genCSS: function (env, output) {
-        if (!this.escaped) {
-            output.add(this.quote, this.currentFileInfo, this.index);
-        }
-        output.add(this.value);
-        if (!this.escaped) {
-            output.add(this.quote);
-        }
-    },
-    toCSS: tree.toCSS,
-    eval: function (env) {
-        var that = this;
-        var value = this.value.replace(/`([^`]+)`/g, function (_, exp) {
-            return new(tree.JavaScript)(exp, that.index, true).eval(env).value;
-        }).replace(/@\{([\w-]+)\}/g, function (_, name) {
-            var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true);
-            return (v instanceof tree.Quoted) ? v.value : v.toCSS();
-        });
-        return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo);
-    },
-    compare: function (x) {
-        if (!x.toCSS) {
-            return -1;
-        }
-
-        var left, right;
+Quoted.prototype = new JsEvalNode();
+Quoted.prototype.type = "Quoted";
+Quoted.prototype.genCSS = function (env, output) {
+    if (!this.escaped) {
+        output.add(this.quote, this.currentFileInfo, this.index);
+    }
+    output.add(this.value);
+    if (!this.escaped) {
+        output.add(this.quote);
+    }
+};
+Quoted.prototype.eval = function (env) {
+    var that = this;
+    var value = this.value.replace(/`([^`]+)`/g, function (_, exp) {
+        return String(that.evaluateJavaScript(exp, env));
+    }).replace(/@\{([\w-]+)\}/g, function (_, name) {
+        var v = new(Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true);
+        return (v instanceof Quoted) ? v.value : v.toCSS();
+    });
+    return new(Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo);
+};
+Quoted.prototype.compare = function (x) {
+    if (!x.toCSS) {
+        return -1;
+    }
 
-        // when comparing quoted strings allow the quote to differ
-        if (x.type === "Quoted" && !this.escaped && !x.escaped) {
-            left = x.value;
-            right = this.value;
-        } else {
-            left = this.toCSS();
-            right = x.toCSS();
-        }
+    var left, right;
 
-        if (left === right) {
-            return 0;
-        }
+    // when comparing quoted strings allow the quote to differ
+    if (x.type === "Quoted" && !this.escaped && !x.escaped) {
+        left = x.value;
+        right = this.value;
+    } else {
+        left = this.toCSS();
+        right = x.toCSS();
+    }
 
-        return left < right ? -1 : 1;
+    if (left === right) {
+        return 0;
     }
+
+    return left < right ? -1 : 1;
 };
-return Quoted;
-};
+module.exports = Quoted;
diff --git a/lib/less/tree/rule.js b/lib/less/tree/rule.js
index 8dad863..ec09dcf 100644
--- a/lib/less/tree/rule.js
+++ b/lib/less/tree/rule.js
@@ -1,8 +1,10 @@
-module.exports = function (tree) {
+var Node = require("./node.js"),
+    Value = require("./value.js"),
+    Keyword = require("./keyword.js");
 
 var Rule = function (name, value, important, merge, index, currentFileInfo, inline) {
     this.name = name;
-    this.value = (value instanceof tree.Value || value instanceof tree.Ruleset) ? value : new(tree.Value)([value]);
+    this.value = (value instanceof Node) ? value : new(Value)([value]); //value instanceof tree.Value || value instanceof tree.Ruleset ??
     this.important = important ? ' ' + important.trim() : '';
     this.merge = merge;
     this.index = index;
@@ -11,73 +13,6 @@ var Rule = function (name, value, important, merge, index, currentFileInfo, inli
     this.variable = name.charAt && (name.charAt(0) === '@');
 };
 
-Rule.prototype = {
-    type: "Rule",
-    accept: function (visitor) {
-        this.value = visitor.visit(this.value);
-    },
-    genCSS: function (env, output) {
-        output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index);
-        try {
-            this.value.genCSS(env, output);
-        }
-        catch(e) {
-            e.index = this.index;
-            e.filename = this.currentFileInfo.filename;
-            throw e;
-        }
-        output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index);
-    },
-    toCSS: tree.toCSS,
-    eval: function (env) {
-        var strictMathBypass = false, name = this.name, evaldValue;
-        if (typeof name !== "string") {
-            // expand 'primitive' name directly to get
-            // things faster (~10% for benchmark.less):
-            name = (name.length === 1)
-                && (name[0] instanceof tree.Keyword)
-                    ? name[0].value : evalName(env, name);
-        }
-        if (name === "font" && !env.strictMath) {
-            strictMathBypass = true;
-            env.strictMath = true;
-        }
-        try {
-            evaldValue = this.value.eval(env);
-
-            if (!this.variable && evaldValue.type === "DetachedRuleset") {
-                throw { message: "Rulesets cannot be evaluated on a property.",
-                        index: this.index, filename: this.currentFileInfo.filename };
-            }
-
-            return new(Rule)(name,
-                              evaldValue,
-                              this.important,
-                              this.merge,
-                              this.index, this.currentFileInfo, this.inline);
-        }
-        catch(e) {
-            if (typeof e.index !== 'number') {
-                e.index = this.index;
-                e.filename = this.currentFileInfo.filename;
-            }
-            throw e;
-        }
-        finally {
-            if (strictMathBypass) {
-                env.strictMath = false;
-            }
-        }
-    },
-    makeImportant: function () {
-        return new(Rule)(this.name,
-                              this.value,
-                              "!important",
-                              this.merge,
-                              this.index, this.currentFileInfo, this.inline);
-    }
-};
-
 function evalName(env, name) {
     var value = "", i, n = name.length,
         output = {add: function (s) {value += s;}};
@@ -86,7 +21,67 @@ function evalName(env, name) {
     }
     return value;
 }
-    
-return Rule;
 
+Rule.prototype = new Node();
+Rule.prototype.type = "Rule";
+Rule.prototype.genCSS = function (env, output) {
+    output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index);
+    try {
+        this.value.genCSS(env, output);
+    }
+    catch(e) {
+        e.index = this.index;
+        e.filename = this.currentFileInfo.filename;
+        throw e;
+    }
+    output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index);
 };
+Rule.prototype.eval = function (env) {
+    var strictMathBypass = false, name = this.name, evaldValue;
+    if (typeof name !== "string") {
+        // expand 'primitive' name directly to get
+        // things faster (~10% for benchmark.less):
+        name = (name.length === 1)
+            && (name[0] instanceof Keyword)
+                ? name[0].value : evalName(env, name);
+    }
+    if (name === "font" && !env.strictMath) {
+        strictMathBypass = true;
+        env.strictMath = true;
+    }
+    try {
+        evaldValue = this.value.eval(env);
+
+        if (!this.variable && evaldValue.type === "DetachedRuleset") {
+            throw { message: "Rulesets cannot be evaluated on a property.",
+                    index: this.index, filename: this.currentFileInfo.filename };
+        }
+
+        return new(Rule)(name,
+                          evaldValue,
+                          this.important,
+                          this.merge,
+                          this.index, this.currentFileInfo, this.inline);
+    }
+    catch(e) {
+        if (typeof e.index !== 'number') {
+            e.index = this.index;
+            e.filename = this.currentFileInfo.filename;
+        }
+        throw e;
+    }
+    finally {
+        if (strictMathBypass) {
+            env.strictMath = false;
+        }
+    }
+};
+Rule.prototype.makeImportant = function () {
+    return new(Rule)(this.name,
+                          this.value,
+                          "!important",
+                          this.merge,
+                          this.index, this.currentFileInfo, this.inline);
+};
+
+module.exports = Rule;
diff --git a/lib/less/tree/ruleset-call.js b/lib/less/tree/ruleset-call.js
index e7d998c..537a978 100644
--- a/lib/less/tree/ruleset-call.js
+++ b/lib/less/tree/ruleset-call.js
@@ -1,16 +1,13 @@
-module.exports = function (tree) {
+var Node = require("./node.js"),
+    Variable = require("./variable.js");
 
 var RulesetCall = function (variable) {
     this.variable = variable;
 };
-RulesetCall.prototype = {
-    type: "RulesetCall",
-    accept: function (visitor) {
-    },
-    eval: function (env) {
-        var detachedRuleset = new(tree.Variable)(this.variable).eval(env);
-        return detachedRuleset.callEval(env);
-    }
-};
-return RulesetCall;
+RulesetCall.prototype = new Node();
+RulesetCall.prototype.type = "RulesetCall";
+RulesetCall.prototype.eval = function (env) {
+    var detachedRuleset = new(Variable)(this.variable).eval(env);
+    return detachedRuleset.callEval(env);
 };
+module.exports = RulesetCall;
diff --git a/lib/less/tree/ruleset.js b/lib/less/tree/ruleset.js
index 92337cf..4c2ecf9 100644
--- a/lib/less/tree/ruleset.js
+++ b/lib/less/tree/ruleset.js
@@ -1,4 +1,10 @@
-module.exports = function (tree) {
+var Node = require("./node.js"),
+    Rule = require("./rule.js"),
+    Selector = require("./selector.js"),
+    Element = require("./element.js"),
+    contexts = require("../env.js"),
+    defaultFunc = require("../functions/default.js"),
+    getDebugInfo = require("./debug-info.js");
 
 var Ruleset = function (selectors, rules, strictImports) {
     this.selectors = selectors;
@@ -6,575 +12,566 @@ var Ruleset = function (selectors, rules, strictImports) {
     this._lookups = {};
     this.strictImports = strictImports;
 };
-Ruleset.prototype = {
-    type: "Ruleset",
-    accept: function (visitor) {
-        if (this.paths) {
-            visitor.visitArray(this.paths, true);
-        } else if (this.selectors) {
-            this.selectors = visitor.visitArray(this.selectors);
-        }
-        if (this.rules && this.rules.length) {
-            this.rules = visitor.visitArray(this.rules);
-        }
-    },
-    eval: function (env) {
-        var thisSelectors = this.selectors, selectors,
-            selCnt, selector, i, defaultFunc = tree.defaultFunc, hasOnePassingSelector = false;
-
-        if (thisSelectors && (selCnt = thisSelectors.length)) {
-            selectors = [];
-            defaultFunc.error({
-                type: "Syntax",
-                message: "it is currently only allowed in parametric mixin guards,"
-            });
-            for (i = 0; i < selCnt; i++) {
-                selector = thisSelectors[i].eval(env);
-                selectors.push(selector);
-                if (selector.evaldCondition) {
-                    hasOnePassingSelector = true;
-                }
+Ruleset.prototype = new Node();
+Ruleset.prototype.type = "Ruleset";
+Ruleset.prototype.isRuleset = true;
+Ruleset.prototype.isRulesetLike = true;
+Ruleset.prototype.accept = function (visitor) {
+    if (this.paths) {
+        visitor.visitArray(this.paths, true);
+    } else if (this.selectors) {
+        this.selectors = visitor.visitArray(this.selectors);
+    }
+    if (this.rules && this.rules.length) {
+        this.rules = visitor.visitArray(this.rules);
+    }
+};
+Ruleset.prototype.eval = function (env) {
+    var thisSelectors = this.selectors, selectors,
+        selCnt, selector, i, hasOnePassingSelector = false;
+
+    if (thisSelectors && (selCnt = thisSelectors.length)) {
+        selectors = [];
+        defaultFunc.error({
+            type: "Syntax",
+            message: "it is currently only allowed in parametric mixin guards,"
+        });
+        for (i = 0; i < selCnt; i++) {
+            selector = thisSelectors[i].eval(env);
+            selectors.push(selector);
+            if (selector.evaldCondition) {
+                hasOnePassingSelector = true;
             }
-            defaultFunc.reset();
-        } else {
-            hasOnePassingSelector = true;
         }
+        defaultFunc.reset();
+    } else {
+        hasOnePassingSelector = true;
+    }
 
-        var rules = this.rules ? this.rules.slice(0) : null,
-            ruleset = new(Ruleset)(selectors, rules, this.strictImports),
-            rule, subRule;
+    var rules = this.rules ? this.rules.slice(0) : null,
+        ruleset = new(Ruleset)(selectors, rules, this.strictImports),
+        rule, subRule;
 
-        ruleset.originalRuleset = this;
-        ruleset.root = this.root;
-        ruleset.firstRoot = this.firstRoot;
-        ruleset.allowImports = this.allowImports;
+    ruleset.originalRuleset = this;
+    ruleset.root = this.root;
+    ruleset.firstRoot = this.firstRoot;
+    ruleset.allowImports = this.allowImports;
 
-        if(this.debugInfo) {
-            ruleset.debugInfo = this.debugInfo;
-        }
+    if(this.debugInfo) {
+        ruleset.debugInfo = this.debugInfo;
+    }
 
-        if (!hasOnePassingSelector) {
-            rules.length = 0;
-        }
+    if (!hasOnePassingSelector) {
+        rules.length = 0;
+    }
 
-        // push the current ruleset to the frames stack
-        var envFrames = env.frames;
-        envFrames.unshift(ruleset);
+    // push the current ruleset to the frames stack
+    var envFrames = env.frames;
+    envFrames.unshift(ruleset);
 
-        // currrent selectors
-        var envSelectors = env.selectors;
-        if (!envSelectors) {
-            env.selectors = envSelectors = [];
-        }
-        envSelectors.unshift(this.selectors);
+    // currrent selectors
+    var envSelectors = env.selectors;
+    if (!envSelectors) {
+        env.selectors = envSelectors = [];
+    }
+    envSelectors.unshift(this.selectors);
 
-        // Evaluate imports
-        if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) {
-            ruleset.evalImports(env);
-        }
+    // Evaluate imports
+    if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) {
+        ruleset.evalImports(env);
+    }
 
-        // Store the frames around mixin definitions,
-        // so they can be evaluated like closures when the time comes.
-        var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0;
-        for (i = 0; i < rsRuleCnt; i++) {
-            if (rsRules[i] instanceof tree.mixin.Definition || rsRules[i] instanceof tree.DetachedRuleset) {
-                rsRules[i] = rsRules[i].eval(env);
-            }
+    // Store the frames around mixin definitions,
+    // so they can be evaluated like closures when the time comes.
+    var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0;
+    for (i = 0; i < rsRuleCnt; i++) {
+        if (rsRules[i].evalFirst) {
+            rsRules[i] = rsRules[i].eval(env);
         }
+    }
 
-        var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0;
-
-        // Evaluate mixin calls.
-        for (i = 0; i < rsRuleCnt; i++) {
-            if (rsRules[i] instanceof tree.mixin.Call) {
-                /*jshint loopfunc:true */
-                rules = rsRules[i].eval(env).filter(function(r) {
-                    if ((r instanceof tree.Rule) && r.variable) {
-                        // do not pollute the scope if the variable is
-                        // already there. consider returning false here
-                        // but we need a way to "return" variable from mixins
-                        return !(ruleset.variable(r.name));
-                    }
-                    return true;
-                });
-                rsRules.splice.apply(rsRules, [i, 1].concat(rules));
-                rsRuleCnt += rules.length - 1;
-                i += rules.length-1;
-                ruleset.resetCache();
-            } else if (rsRules[i] instanceof tree.RulesetCall) {
-                /*jshint loopfunc:true */
-                rules = rsRules[i].eval(env).rules.filter(function(r) {
-                    if ((r instanceof tree.Rule) && r.variable) {
-                        // do not pollute the scope at all
-                        return false;
-                    }
-                    return true;
-                });
-                rsRules.splice.apply(rsRules, [i, 1].concat(rules));
-                rsRuleCnt += rules.length - 1;
-                i += rules.length-1;
-                ruleset.resetCache();
-            }
+    var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0;
+
+    // Evaluate mixin calls.
+    for (i = 0; i < rsRuleCnt; i++) {
+        if (rsRules[i].type === "MixinCall") {
+            /*jshint loopfunc:true */
+            rules = rsRules[i].eval(env).filter(function(r) {
+                if ((r instanceof Rule) && r.variable) {
+                    // do not pollute the scope if the variable is
+                    // already there. consider returning false here
+                    // but we need a way to "return" variable from mixins
+                    return !(ruleset.variable(r.name));
+                }
+                return true;
+            });
+            rsRules.splice.apply(rsRules, [i, 1].concat(rules));
+            rsRuleCnt += rules.length - 1;
+            i += rules.length-1;
+            ruleset.resetCache();
+        } else if (rsRules[i].type === "RulesetCall") {
+            /*jshint loopfunc:true */
+            rules = rsRules[i].eval(env).rules.filter(function(r) {
+                if ((r instanceof Rule) && r.variable) {
+                    // do not pollute the scope at all
+                    return false;
+                }
+                return true;
+            });
+            rsRules.splice.apply(rsRules, [i, 1].concat(rules));
+            rsRuleCnt += rules.length - 1;
+            i += rules.length-1;
+            ruleset.resetCache();
         }
+    }
 
-        // Evaluate everything else
-        for (i = 0; i < rsRules.length; i++) {
-            rule = rsRules[i];
-            if (! (rule instanceof tree.mixin.Definition || rule instanceof tree.DetachedRuleset)) {
-                rsRules[i] = rule = rule.eval ? rule.eval(env) : rule;
-            }
+    // Evaluate everything else
+    for (i = 0; i < rsRules.length; i++) {
+        rule = rsRules[i];
+        if (!rule.evalFirst) {
+            rsRules[i] = rule = rule.eval ? rule.eval(env) : rule;
         }
+    }
 
-        // Evaluate everything else
-        for (i = 0; i < rsRules.length; i++) {
-            rule = rsRules[i];
-            // for rulesets, check if it is a css guard and can be removed
-            if (rule instanceof Ruleset && rule.selectors && rule.selectors.length === 1) {
-                // check if it can be folded in (e.g. & where)
-                if (rule.selectors[0].isJustParentSelector()) {
-                    rsRules.splice(i--, 1);
-
-                    for(var j = 0; j < rule.rules.length; j++) {
-                        subRule = rule.rules[j];
-                        if (!(subRule instanceof tree.Rule) || !subRule.variable) {
-                            rsRules.splice(++i, 0, subRule);
-                        }
+    // Evaluate everything else
+    for (i = 0; i < rsRules.length; i++) {
+        rule = rsRules[i];
+        // for rulesets, check if it is a css guard and can be removed
+        if (rule instanceof Ruleset && rule.selectors && rule.selectors.length === 1) {
+            // check if it can be folded in (e.g. & where)
+            if (rule.selectors[0].isJustParentSelector()) {
+                rsRules.splice(i--, 1);
+
+                for(var j = 0; j < rule.rules.length; j++) {
+                    subRule = rule.rules[j];
+                    if (!(subRule instanceof Rule) || !subRule.variable) {
+                        rsRules.splice(++i, 0, subRule);
                     }
                 }
             }
         }
+    }
 
-        // Pop the stack
-        envFrames.shift();
-        envSelectors.shift();
+    // Pop the stack
+    envFrames.shift();
+    envSelectors.shift();
 
-        if (env.mediaBlocks) {
-            for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) {
-                env.mediaBlocks[i].bubbleSelectors(selectors);
-            }
+    if (env.mediaBlocks) {
+        for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) {
+            env.mediaBlocks[i].bubbleSelectors(selectors);
         }
+    }
 
-        return ruleset;
-    },
-    evalImports: function(env) {
-        var rules = this.rules, i, importRules;
-        if (!rules) { return; }
-
-        for (i = 0; i < rules.length; i++) {
-            if (rules[i] instanceof tree.Import) {
-                importRules = rules[i].eval(env);
-                if (importRules && importRules.length) {
-                    rules.splice.apply(rules, [i, 1].concat(importRules));
-                    i+= importRules.length-1;
-                } else {
-                    rules.splice(i, 1, importRules);
-                }
-                this.resetCache();
+    return ruleset;
+};
+Ruleset.prototype.evalImports = function(env) {
+    var rules = this.rules, i, importRules;
+    if (!rules) { return; }
+
+    for (i = 0; i < rules.length; i++) {
+        if (rules[i].type === "Import") {
+            importRules = rules[i].eval(env);
+            if (importRules && importRules.length) {
+                rules.splice.apply(rules, [i, 1].concat(importRules));
+                i+= importRules.length-1;
+            } else {
+                rules.splice(i, 1, importRules);
             }
+            this.resetCache();
         }
-    },
-    makeImportant: function() {
-        return new Ruleset(this.selectors, this.rules.map(function (r) {
-                    if (r.makeImportant) {
-                        return r.makeImportant();
-                    } else {
-                        return r;
-                    }
-                }), this.strictImports);
-    },
-    matchArgs: function (args) {
-        return !args || args.length === 0;
-    },
-    // lets you call a css selector with a guard
-    matchCondition: function (args, env) {
-        var lastSelector = this.selectors[this.selectors.length-1];
-        if (!lastSelector.evaldCondition) {
-            return false;
-        }
-        if (lastSelector.condition &&
-            !lastSelector.condition.eval(
-                new(tree.evalEnv)(env,
-                    env.frames))) {
-            return false;
-        }
-        return true;
-    },
-    resetCache: function () {
-        this._rulesets = null;
-        this._variables = null;
-        this._lookups = {};
-    },
-    variables: function () {
-        if (!this._variables) {
-            this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) {
-                if (r instanceof tree.Rule && r.variable === true) {
-                    hash[r.name] = r;
+    }
+};
+Ruleset.prototype.makeImportant = function() {
+    return new Ruleset(this.selectors, this.rules.map(function (r) {
+                if (r.makeImportant) {
+                    return r.makeImportant();
+                } else {
+                    return r;
                 }
-                return hash;
-            }, {});
-        }
-        return this._variables;
-    },
-    variable: function (name) {
-        return this.variables()[name];
-    },
-    rulesets: function () {
-        if (!this.rules) { return null; }
-
-        var _MixinDefinition = tree.mixin.Definition,
-            filtRules = [], rules = this.rules, cnt = rules.length,
-            i, rule;
-
-        for (i = 0; i < cnt; i++) {
-            rule = rules[i];
-            if ((rule instanceof Ruleset) || (rule instanceof _MixinDefinition)) {
-                filtRules.push(rule);
+            }), this.strictImports);
+};
+Ruleset.prototype.matchArgs = function (args) {
+    return !args || args.length === 0;
+};
+// lets you call a css selector with a guard
+Ruleset.prototype.matchCondition = function (args, env) {
+    var lastSelector = this.selectors[this.selectors.length-1];
+    if (!lastSelector.evaldCondition) {
+        return false;
+    }
+    if (lastSelector.condition &&
+        !lastSelector.condition.eval(
+            new(contexts.evalEnv)(env,
+                env.frames))) {
+        return false;
+    }
+    return true;
+};
+Ruleset.prototype.resetCache = function () {
+    this._rulesets = null;
+    this._variables = null;
+    this._lookups = {};
+};
+Ruleset.prototype.variables = function () {
+    if (!this._variables) {
+        this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) {
+            if (r instanceof Rule && r.variable === true) {
+                hash[r.name] = r;
             }
+            return hash;
+        }, {});
+    }
+    return this._variables;
+};
+Ruleset.prototype.variable = function (name) {
+    return this.variables()[name];
+};
+Ruleset.prototype.rulesets = function () {
+    if (!this.rules) { return null; }
+
+    var filtRules = [], rules = this.rules, cnt = rules.length,
+        i, rule;
+
+    for (i = 0; i < cnt; i++) {
+        rule = rules[i];
+        if (rule.isRuleset) {
+            filtRules.push(rule);
         }
+    }
 
-        return filtRules;
-    },
-    prependRule: function (rule) {
-        var rules = this.rules;
-        if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; }
-    },
-    find: function (selector, self) {
-        self = self || this;
-        var rules = [], match,
-            key = selector.toCSS();
-
-        if (key in this._lookups) { return this._lookups[key]; }
-
-        this.rulesets().forEach(function (rule) {
-            if (rule !== self) {
-                for (var j = 0; j < rule.selectors.length; j++) {
-                    match = selector.match(rule.selectors[j]);
-                    if (match) {
-                        if (selector.elements.length > match) {
-                            Array.prototype.push.apply(rules, rule.find(
-                                new(tree.Selector)(selector.elements.slice(match)), self));
-                        } else {
-                            rules.push(rule);
-                        }
-                        break;
+    return filtRules;
+};
+Ruleset.prototype.prependRule = function (rule) {
+    var rules = this.rules;
+    if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; }
+};
+Ruleset.prototype.find = function (selector, self) {
+    self = self || this;
+    var rules = [], match,
+        key = selector.toCSS();
+
+    if (key in this._lookups) { return this._lookups[key]; }
+
+    this.rulesets().forEach(function (rule) {
+        if (rule !== self) {
+            for (var j = 0; j < rule.selectors.length; j++) {
+                match = selector.match(rule.selectors[j]);
+                if (match) {
+                    if (selector.elements.length > match) {
+                        Array.prototype.push.apply(rules, rule.find(
+                            new(Selector)(selector.elements.slice(match)), self));
+                    } else {
+                        rules.push(rule);
                     }
+                    break;
                 }
             }
-        });
-        this._lookups[key] = rules;
-        return rules;
-    },
-    genCSS: function (env, output) {
-        var i, j,
-            charsetRuleNodes = [],
-            ruleNodes = [],
-            rulesetNodes = [],
-            rulesetNodeCnt,
-            debugInfo,     // Line number debugging
-            rule,
-            path;
-
-        env.tabLevel = (env.tabLevel || 0);
-
-        if (!this.root) {
-            env.tabLevel++;
         }
+    });
+    this._lookups[key] = rules;
+    return rules;
+};
+Ruleset.prototype.genCSS = function (env, output) {
+    var i, j,
+        charsetRuleNodes = [],
+        ruleNodes = [],
+        rulesetNodes = [],
+        rulesetNodeCnt,
+        debugInfo,     // Line number debugging
+        rule,
+        path;
+
+    env.tabLevel = (env.tabLevel || 0);
+
+    if (!this.root) {
+        env.tabLevel++;
+    }
 
-        var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join("  "),
-            tabSetStr = env.compress ? '' : Array(env.tabLevel).join("  "),
-            sep;
-
-        function isRulesetLikeNode(rule, root) {
-             // if it has nested rules, then it should be treated like a ruleset
-             if (rule.rules)
-                 return true;
-
-             // medias and comments do not have nested rules, but should be treated like rulesets anyway
-             if ( (rule instanceof tree.Media) || (root && rule instanceof tree.Comment))
-                 return true;
-
-             // some directives and anonumoust nodes are ruleset like, others are not
-             if ((rule instanceof tree.Directive) || (rule instanceof tree.Anonymous)) {
-                 return rule.isRulesetLike();
-             }
-
-             //anything else is assumed to be a rule
-             return false;
-        }
+    var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join("  "),
+        tabSetStr = env.compress ? '' : Array(env.tabLevel).join("  "),
+        sep;
+
+    function isRulesetLikeNode(rule, root) {
+         // if it has nested rules, then it should be treated like a ruleset
+         // medias and comments do not have nested rules, but should be treated like rulesets anyway
+         // some directives and anonymous nodes are ruleset like, others are not
+         if (typeof rule.isRulesetLike === "boolean")
+         {
+             return rule.isRulesetLike;
+         } else if (typeof rule.isRulesetLike === "function")
+         {
+             return rule.isRulesetLike(root);
+         }
+
+         //anything else is assumed to be a rule
+         return false;
+    }
 
-        for (i = 0; i < this.rules.length; i++) {
-            rule = this.rules[i];
-            if (isRulesetLikeNode(rule, this.root)) {
-                rulesetNodes.push(rule);
+    for (i = 0; i < this.rules.length; i++) {
+        rule = this.rules[i];
+        if (isRulesetLikeNode(rule, this.root)) {
+            rulesetNodes.push(rule);
+        } else {
+            //charsets should float on top of everything
+            if (rule.isCharset && rule.isCharset()) {
+                charsetRuleNodes.push(rule);
             } else {
-                //charsets should float on top of everything
-                if (rule.isCharset && rule.isCharset()) {
-                    charsetRuleNodes.push(rule);
-                } else {
-                    ruleNodes.push(rule);
-                }
+                ruleNodes.push(rule);
             }
         }
-        ruleNodes = charsetRuleNodes.concat(ruleNodes);
+    }
+    ruleNodes = charsetRuleNodes.concat(ruleNodes);
 
-        // If this is the root node, we don't render
-        // a selector, or {}.
-        if (!this.root) {
-            debugInfo = tree.debugInfo(env, this, tabSetStr);
+    // If this is the root node, we don't render
+    // a selector, or {}.
+    if (!this.root) {
+        debugInfo = getDebugInfo(env, this, tabSetStr);
 
-            if (debugInfo) {
-                output.add(debugInfo);
-                output.add(tabSetStr);
-            }
+        if (debugInfo) {
+            output.add(debugInfo);
+            output.add(tabSetStr);
+        }
 
-            var paths = this.paths, pathCnt = paths.length,
-                pathSubCnt;
+        var paths = this.paths, pathCnt = paths.length,
+            pathSubCnt;
 
-            sep = env.compress ? ',' : (',\n' + tabSetStr);
+        sep = env.compress ? ',' : (',\n' + tabSetStr);
 
-            for (i = 0; i < pathCnt; i++) {
-                path = paths[i];
-                if (!(pathSubCnt = path.length)) { continue; }
-                if (i > 0) { output.add(sep); }
+        for (i = 0; i < pathCnt; i++) {
+            path = paths[i];
+            if (!(pathSubCnt = path.length)) { continue; }
+            if (i > 0) { output.add(sep); }
 
-                env.firstSelector = true;
-                path[0].genCSS(env, output);
+            env.firstSelector = true;
+            path[0].genCSS(env, output);
 
-                env.firstSelector = false;
-                for (j = 1; j < pathSubCnt; j++) {
-                    path[j].genCSS(env, output);
-                }
+            env.firstSelector = false;
+            for (j = 1; j < pathSubCnt; j++) {
+                path[j].genCSS(env, output);
             }
-
-            output.add((env.compress ? '{' : ' {\n') + tabRuleStr);
         }
 
-        // Compile rules and rulesets
-        for (i = 0; i < ruleNodes.length; i++) {
-            rule = ruleNodes[i];
+        output.add((env.compress ? '{' : ' {\n') + tabRuleStr);
+    }
 
-            // @page{ directive ends up with root elements inside it, a mix of rules and rulesets
-            // In this instance we do not know whether it is the last property
-            if (i + 1 === ruleNodes.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) {
-                env.lastRule = true;
-            }
+    // Compile rules and rulesets
+    for (i = 0; i < ruleNodes.length; i++) {
+        rule = ruleNodes[i];
 
-            if (rule.genCSS) {
-                rule.genCSS(env, output);
-            } else if (rule.value) {
-                output.add(rule.value.toString());
-            }
-
-            if (!env.lastRule) {
-                output.add(env.compress ? '' : ('\n' + tabRuleStr));
-            } else {
-                env.lastRule = false;
-            }
+        // @page{ directive ends up with root elements inside it, a mix of rules and rulesets
+        // In this instance we do not know whether it is the last property
+        if (i + 1 === ruleNodes.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) {
+            env.lastRule = true;
         }
 
-        if (!this.root) {
-            output.add((env.compress ? '}' : '\n' + tabSetStr + '}'));
-            env.tabLevel--;
+        if (rule.genCSS) {
+            rule.genCSS(env, output);
+        } else if (rule.value) {
+            output.add(rule.value.toString());
         }
 
-        sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr);
-        rulesetNodeCnt = rulesetNodes.length;
-        if (rulesetNodeCnt) {
-            if (ruleNodes.length && sep) { output.add(sep); }
-            rulesetNodes[0].genCSS(env, output);
-            for (i = 1; i < rulesetNodeCnt; i++) {
-                if (sep) { output.add(sep); }
-                rulesetNodes[i].genCSS(env, output);
-            }
+        if (!env.lastRule) {
+            output.add(env.compress ? '' : ('\n' + tabRuleStr));
+        } else {
+            env.lastRule = false;
         }
+    }
 
-        if (!output.isEmpty() && !env.compress && this.firstRoot) {
-            output.add('\n');
+    if (!this.root) {
+        output.add((env.compress ? '}' : '\n' + tabSetStr + '}'));
+        env.tabLevel--;
+    }
+
+    sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr);
+    rulesetNodeCnt = rulesetNodes.length;
+    if (rulesetNodeCnt) {
+        if (ruleNodes.length && sep) { output.add(sep); }
+        rulesetNodes[0].genCSS(env, output);
+        for (i = 1; i < rulesetNodeCnt; i++) {
+            if (sep) { output.add(sep); }
+            rulesetNodes[i].genCSS(env, output);
         }
-    },
+    }
 
-    toCSS: tree.toCSS,
+    if (!output.isEmpty() && !env.compress && this.firstRoot) {
+        output.add('\n');
+    }
+};
+Ruleset.prototype.markReferenced = function () {
+    if (!this.selectors) {
+        return;
+    }
+    for (var s = 0; s < this.selectors.length; s++) {
+        this.selectors[s].markReferenced();
+    }
+};
+Ruleset.prototype.joinSelectors = function (paths, context, selectors) {
+    for (var s = 0; s < selectors.length; s++) {
+        this.joinSelector(paths, context, selectors[s]);
+    }
+};
+Ruleset.prototype.joinSelector = function (paths, context, selector) {
 
-    markReferenced: function () {
-        if (!this.selectors) {
-            return;
-        }
-        for (var s = 0; s < this.selectors.length; s++) {
-            this.selectors[s].markReferenced();
-        }
-    },
+    var i, j, k,
+        hasParentSelector, newSelectors, el, sel, parentSel,
+        newSelectorPath, afterParentJoin, newJoinedSelector,
+        newJoinedSelectorEmpty, lastSelector, currentElements,
+        selectorsMultiplied;
 
-    joinSelectors: function (paths, context, selectors) {
-        for (var s = 0; s < selectors.length; s++) {
-            this.joinSelector(paths, context, selectors[s]);
+    for (i = 0; i < selector.elements.length; i++) {
+        el = selector.elements[i];
+        if (el.value === '&') {
+            hasParentSelector = true;
         }
-    },
+    }
 
-    joinSelector: function (paths, context, selector) {
+    if (!hasParentSelector) {
+        if (context.length > 0) {
+            for (i = 0; i < context.length; i++) {
+                paths.push(context[i].concat(selector));
+            }
+        }
+        else {
+            paths.push([selector]);
+        }
+        return;
+    }
 
-        var i, j, k,
-            hasParentSelector, newSelectors, el, sel, parentSel,
-            newSelectorPath, afterParentJoin, newJoinedSelector,
-            newJoinedSelectorEmpty, lastSelector, currentElements,
-            selectorsMultiplied;
+    // The paths are [[Selector]]
+    // The first list is a list of comma seperated selectors
+    // The inner list is a list of inheritance seperated selectors
+    // e.g.
+    // .a, .b {
+    //   .c {
+    //   }
+    // }
+    // == [[.a] [.c]] [[.b] [.c]]
+    //
+
+    // the elements from the current selector so far
+    currentElements = [];
+    // the current list of new selectors to add to the path.
+    // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
+    // by the parents
+    newSelectors = [[]];
+
+    for (i = 0; i < selector.elements.length; i++) {
+        el = selector.elements[i];
+        // non parent reference elements just get added
+        if (el.value !== "&") {
+            currentElements.push(el);
+        } else {
+            // the new list of selectors to add
+            selectorsMultiplied = [];
 
-        for (i = 0; i < selector.elements.length; i++) {
-            el = selector.elements[i];
-            if (el.value === '&') {
-                hasParentSelector = true;
+            // merge the current list of non parent selector elements
+            // on to the current list of selectors to add
+            if (currentElements.length > 0) {
+                this.mergeElementsOnToSelectors(currentElements, newSelectors);
             }
-        }
 
-        if (!hasParentSelector) {
-            if (context.length > 0) {
-                for (i = 0; i < context.length; i++) {
-                    paths.push(context[i].concat(selector));
+            // loop through our current selectors
+            for (j = 0; j < newSelectors.length; j++) {
+                sel = newSelectors[j];
+                // if we don't have any parent paths, the & might be in a mixin so that it can be used
+                // whether there are parents or not
+                if (context.length === 0) {
+                    // the combinator used on el should now be applied to the next element instead so that
+                    // it is not lost
+                    if (sel.length > 0) {
+                        sel[0].elements = sel[0].elements.slice(0);
+                        sel[0].elements.push(new(Element)(el.combinator, '', el.index, el.currentFileInfo));
+                    }
+                    selectorsMultiplied.push(sel);
                 }
-            }
-            else {
-                paths.push([selector]);
-            }
-            return;
-        }
+                else {
+                    // and the parent selectors
+                    for (k = 0; k < context.length; k++) {
+                        parentSel = context[k];
+                        // We need to put the current selectors
+                        // then join the last selector's elements on to the parents selectors
+
+                        // our new selector path
+                        newSelectorPath = [];
+                        // selectors from the parent after the join
+                        afterParentJoin = [];
+                        newJoinedSelectorEmpty = true;
+
+                        //construct the joined selector - if & is the first thing this will be empty,
+                        // if not newJoinedSelector will be the last set of elements in the selector
+                        if (sel.length > 0) {
+                            newSelectorPath = sel.slice(0);
+                            lastSelector = newSelectorPath.pop();
+                            newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0));
+                            newJoinedSelectorEmpty = false;
+                        }
+                        else {
+                            newJoinedSelector = selector.createDerived([]);
+                        }
 
-        // The paths are [[Selector]]
-        // The first list is a list of comma seperated selectors
-        // The inner list is a list of inheritance seperated selectors
-        // e.g.
-        // .a, .b {
-        //   .c {
-        //   }
-        // }
-        // == [[.a] [.c]] [[.b] [.c]]
-        //
-
-        // the elements from the current selector so far
-        currentElements = [];
-        // the current list of new selectors to add to the path.
-        // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
-        // by the parents
-        newSelectors = [[]];
-
-        for (i = 0; i < selector.elements.length; i++) {
-            el = selector.elements[i];
-            // non parent reference elements just get added
-            if (el.value !== "&") {
-                currentElements.push(el);
-            } else {
-                // the new list of selectors to add
-                selectorsMultiplied = [];
+                        //put together the parent selectors after the join
+                        if (parentSel.length > 1) {
+                            afterParentJoin = afterParentJoin.concat(parentSel.slice(1));
+                        }
 
-                // merge the current list of non parent selector elements
-                // on to the current list of selectors to add
-                if (currentElements.length > 0) {
-                    this.mergeElementsOnToSelectors(currentElements, newSelectors);
-                }
+                        if (parentSel.length > 0) {
+                            newJoinedSelectorEmpty = false;
 
-                // loop through our current selectors
-                for (j = 0; j < newSelectors.length; j++) {
-                    sel = newSelectors[j];
-                    // if we don't have any parent paths, the & might be in a mixin so that it can be used
-                    // whether there are parents or not
-                    if (context.length === 0) {
-                        // the combinator used on el should now be applied to the next element instead so that
-                        // it is not lost
-                        if (sel.length > 0) {
-                            sel[0].elements = sel[0].elements.slice(0);
-                            sel[0].elements.push(new(tree.Element)(el.combinator, '', el.index, el.currentFileInfo));
+                            // join the elements so far with the first part of the parent
+                            newJoinedSelector.elements.push(new(Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo));
+                            newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1));
                         }
-                        selectorsMultiplied.push(sel);
-                    }
-                    else {
-                        // and the parent selectors
-                        for (k = 0; k < context.length; k++) {
-                            parentSel = context[k];
-                            // We need to put the current selectors
-                            // then join the last selector's elements on to the parents selectors
-
-                            // our new selector path
-                            newSelectorPath = [];
-                            // selectors from the parent after the join
-                            afterParentJoin = [];
-                            newJoinedSelectorEmpty = true;
-
-                            //construct the joined selector - if & is the first thing this will be empty,
-                            // if not newJoinedSelector will be the last set of elements in the selector
-                            if (sel.length > 0) {
-                                newSelectorPath = sel.slice(0);
-                                lastSelector = newSelectorPath.pop();
-                                newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0));
-                                newJoinedSelectorEmpty = false;
-                            }
-                            else {
-                                newJoinedSelector = selector.createDerived([]);
-                            }
-
-                            //put together the parent selectors after the join
-                            if (parentSel.length > 1) {
-                                afterParentJoin = afterParentJoin.concat(parentSel.slice(1));
-                            }
-
-                            if (parentSel.length > 0) {
-                                newJoinedSelectorEmpty = false;
-
-                                // join the elements so far with the first part of the parent
-                                newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo));
-                                newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1));
-                            }
-
-                            if (!newJoinedSelectorEmpty) {
-                                // now add the joined selector
-                                newSelectorPath.push(newJoinedSelector);
-                            }
-
-                            // and the rest of the parent
-                            newSelectorPath = newSelectorPath.concat(afterParentJoin);
-
-                            // add that to our new set of selectors
-                            selectorsMultiplied.push(newSelectorPath);
+
+                        if (!newJoinedSelectorEmpty) {
+                            // now add the joined selector
+                            newSelectorPath.push(newJoinedSelector);
                         }
+
+                        // and the rest of the parent
+                        newSelectorPath = newSelectorPath.concat(afterParentJoin);
+
+                        // add that to our new set of selectors
+                        selectorsMultiplied.push(newSelectorPath);
                     }
                 }
-
-                // our new selectors has been multiplied, so reset the state
-                newSelectors = selectorsMultiplied;
-                currentElements = [];
             }
-        }
 
-        // if we have any elements left over (e.g. .a& .b == .b)
-        // add them on to all the current selectors
-        if (currentElements.length > 0) {
-            this.mergeElementsOnToSelectors(currentElements, newSelectors);
+            // our new selectors has been multiplied, so reset the state
+            newSelectors = selectorsMultiplied;
+            currentElements = [];
         }
+    }
 
-        for (i = 0; i < newSelectors.length; i++) {
-            if (newSelectors[i].length > 0) {
-                paths.push(newSelectors[i]);
-            }
-        }
-    },
-
-    mergeElementsOnToSelectors: function(elements, selectors) {
-        var i, sel;
+    // if we have any elements left over (e.g. .a& .b == .b)
+    // add them on to all the current selectors
+    if (currentElements.length > 0) {
+        this.mergeElementsOnToSelectors(currentElements, newSelectors);
+    }
 
-        if (selectors.length === 0) {
-            selectors.push([ new(tree.Selector)(elements) ]);
-            return;
+    for (i = 0; i < newSelectors.length; i++) {
+        if (newSelectors[i].length > 0) {
+            paths.push(newSelectors[i]);
         }
+    }
+};
+Ruleset.prototype.mergeElementsOnToSelectors = function(elements, selectors) {
+    var i, sel;
 
-        for (i = 0; i < selectors.length; i++) {
-            sel = selectors[i];
+    if (selectors.length === 0) {
+        selectors.push([ new(Selector)(elements) ]);
+        return;
+    }
 
-            // if the previous thing in sel is a parent this needs to join on to it
-            if (sel.length > 0) {
-                sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements));
-            }
-            else {
-                sel.push(new(tree.Selector)(elements));
-            }
+    for (i = 0; i < selectors.length; i++) {
+        sel = selectors[i];
+
+        // if the previous thing in sel is a parent this needs to join on to it
+        if (sel.length > 0) {
+            sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements));
+        }
+        else {
+            sel.push(new(Selector)(elements));
         }
     }
 };
-return Ruleset;
-};
+module.exports = Ruleset;
diff --git a/lib/less/tree/selector.js b/lib/less/tree/selector.js
index 75d10e3..ef39ece 100644
--- a/lib/less/tree/selector.js
+++ b/lib/less/tree/selector.js
@@ -1,4 +1,4 @@
-module.exports = function (tree) {
+var Node = require("./node.js");
 
 var Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) {
     this.elements = elements;
@@ -10,120 +10,117 @@ var Selector = function (elements, extendList, condition, index, currentFileInfo
         this.evaldCondition = true;
     }
 };
-Selector.prototype = {
-    type: "Selector",
-    accept: function (visitor) {
-        if (this.elements) {
-            this.elements = visitor.visitArray(this.elements);
-        }
-        if (this.extendList) {
-            this.extendList = visitor.visitArray(this.extendList);
-        }
-        if (this.condition) {
-            this.condition = visitor.visit(this.condition);
-        }
-    },
-    createDerived: function(elements, extendList, evaldCondition) {
-        evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition;
-        var newSelector = new(Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced);
-        newSelector.evaldCondition = evaldCondition;
-        newSelector.mediaEmpty = this.mediaEmpty;
-        return newSelector;
-    },
-    match: function (other) {
-        var elements = this.elements,
-            len = elements.length,
-            olen, i;
+Selector.prototype = new Node();
+Selector.prototype.type = "Selector";
+Selector.prototype.accept = function (visitor) {
+    if (this.elements) {
+        this.elements = visitor.visitArray(this.elements);
+    }
+    if (this.extendList) {
+        this.extendList = visitor.visitArray(this.extendList);
+    }
+    if (this.condition) {
+        this.condition = visitor.visit(this.condition);
+    }
+};
+Selector.prototype.createDerived = function(elements, extendList, evaldCondition) {
+    evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition;
+    var newSelector = new(Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced);
+    newSelector.evaldCondition = evaldCondition;
+    newSelector.mediaEmpty = this.mediaEmpty;
+    return newSelector;
+};
+Selector.prototype.match = function (other) {
+    var elements = this.elements,
+        len = elements.length,
+        olen, i;
 
-        other.CacheElements();
+    other.CacheElements();
 
-        olen = other._elements.length;
-        if (olen === 0 || len < olen) {
-            return 0;
-        } else {
-            for (i = 0; i < olen; i++) {
-                if (elements[i].value !== other._elements[i]) {
-                    return 0;
-                }
+    olen = other._elements.length;
+    if (olen === 0 || len < olen) {
+        return 0;
+    } else {
+        for (i = 0; i < olen; i++) {
+            if (elements[i].value !== other._elements[i]) {
+                return 0;
             }
         }
+    }
 
-        return olen; // return number of matched elements
-    },
-    CacheElements: function(){
-        var css = '', len, v, i;
-
-        if( !this._elements ){
+    return olen; // return number of matched elements
+};
+Selector.prototype.CacheElements = function(){
+    var css = '', len, v, i;
 
-            len = this.elements.length;
-            for(i = 0; i < len; i++){
+    if( !this._elements ){
 
-                v = this.elements[i];
-                css += v.combinator.value;
+        len = this.elements.length;
+        for(i = 0; i < len; i++){
 
-                if( !v.value.value ){
-                    css += v.value;
-                    continue;
-                }
+            v = this.elements[i];
+            css += v.combinator.value;
 
-                if( typeof v.value.value !== "string" ){
-                    css = '';
-                    break;
-                }
-                css += v.value.value;
+            if( !v.value.value ){
+                css += v.value;
+                continue;
             }
 
-            this._elements = css.match(/[,&#\*\.\w-]([\w-]|(\\.))*/g);
+            if( typeof v.value.value !== "string" ){
+                css = '';
+                break;
+            }
+            css += v.value.value;
+        }
 
-            if (this._elements) {
-                if (this._elements[0] === "&") {
-                    this._elements.shift();
-                }
+        this._elements = css.match(/[,&#\*\.\w-]([\w-]|(\\.))*/g);
 
-            } else {
-                this._elements = [];
+        if (this._elements) {
+            if (this._elements[0] === "&") {
+                this._elements.shift();
             }
 
+        } else {
+            this._elements = [];
         }
-    },
-    isJustParentSelector: function() {
-        return !this.mediaEmpty &&
-            this.elements.length === 1 &&
-            this.elements[0].value === '&' &&
-            (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === '');
-    },
-    eval: function (env) {
-        var evaldCondition = this.condition && this.condition.eval(env),
-            elements = this.elements, extendList = this.extendList;
 
-        elements = elements && elements.map(function (e) { return e.eval(env); });
-        extendList = extendList && extendList.map(function(extend) { return extend.eval(env); });
+    }
+};
+Selector.prototype.isJustParentSelector = function() {
+    return !this.mediaEmpty &&
+        this.elements.length === 1 &&
+        this.elements[0].value === '&' &&
+        (this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === '');
+};
+Selector.prototype.eval = function (env) {
+    var evaldCondition = this.condition && this.condition.eval(env),
+        elements = this.elements, extendList = this.extendList;
 
-        return this.createDerived(elements, extendList, evaldCondition);
-    },
-    genCSS: function (env, output) {
-        var i, element;
-        if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") {
-            output.add(' ', this.currentFileInfo, this.index);
-        }
-        if (!this._css) {
-            //TODO caching? speed comparison?
-            for(i = 0; i < this.elements.length; i++) {
-                element = this.elements[i];
-                element.genCSS(env, output);
-            }
+    elements = elements && elements.map(function (e) { return e.eval(env); });
+    extendList = extendList && extendList.map(function(extend) { return extend.eval(env); });
+
+    return this.createDerived(elements, extendList, evaldCondition);
+};
+Selector.prototype.genCSS = function (env, output) {
+    var i, element;
+    if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") {
+        output.add(' ', this.currentFileInfo, this.index);
+    }
+    if (!this._css) {
+        //TODO caching? speed comparison?
+        for(i = 0; i < this.elements.length; i++) {
+            element = this.elements[i];
+            element.genCSS(env, output);
         }
-    },
-    toCSS: tree.toCSS,
-    markReferenced: function () {
-        this.isReferenced = true;
-    },
-    getIsReferenced: function() {
-        return !this.currentFileInfo.reference || this.isReferenced;
-    },
-    getIsOutput: function() {
-        return this.evaldCondition;
     }
 };
-return Selector;
+Selector.prototype.markReferenced = function () {
+    this.isReferenced = true;
+};
+Selector.prototype.getIsReferenced = function() {
+    return !this.currentFileInfo.reference || this.isReferenced;
+};
+Selector.prototype.getIsOutput = function() {
+    return this.evaldCondition;
 };
+module.exports = Selector;
diff --git a/lib/less/tree/unicode-descriptor.js b/lib/less/tree/unicode-descriptor.js
index 674ac98..72f33ea 100644
--- a/lib/less/tree/unicode-descriptor.js
+++ b/lib/less/tree/unicode-descriptor.js
@@ -1,15 +1,9 @@
-module.exports = function (tree) {
+var Node = require("./node.js");
 
 var UnicodeDescriptor = function (value) {
     this.value = value;
 };
-UnicodeDescriptor.prototype = {
-    type: "UnicodeDescriptor",
-    genCSS: function (env, output) {
-        output.add(this.value);
-    },
-    toCSS: tree.toCSS,
-    eval: function () { return this; }
-};
-return UnicodeDescriptor;
-};
+UnicodeDescriptor.prototype = new Node();
+UnicodeDescriptor.prototype.type = "UnicodeDescriptor";
+
+module.exports = UnicodeDescriptor;
diff --git a/lib/less/tree/unit.js b/lib/less/tree/unit.js
index c25a717..9589498 100644
--- a/lib/less/tree/unit.js
+++ b/lib/less/tree/unit.js
@@ -1,4 +1,5 @@
-module.exports = function(tree, unitConversions) {
+var Node = require("./node.js"),
+    unitConversions = require("../data/unit-conversions.js");
 
 var Unit = function (numerator, denominator, backupUnit) {
     this.numerator = numerator ? numerator.slice(0).sort() : [];
@@ -6,132 +7,120 @@ var Unit = function (numerator, denominator, backupUnit) {
     this.backupUnit = backupUnit;
 };
 
-Unit.prototype = {
-    type: "Unit",
-    clone: function () {
-        return new Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit);
-    },
-    genCSS: function (env, output) {
-        if (this.numerator.length >= 1) {
-            output.add(this.numerator[0]);
-        } else
-        if (this.denominator.length >= 1) {
-            output.add(this.denominator[0]);
-        } else
-        if ((!env || !env.strictUnits) && this.backupUnit) {
-            output.add(this.backupUnit);
-        }
-    },
-    toCSS: tree.toCSS,
-
-    toString: function () {
-        var i, returnStr = this.numerator.join("*");
-        for (i = 0; i < this.denominator.length; i++) {
-            returnStr += "/" + this.denominator[i];
-        }
-        return returnStr;
-    },
-
-    compare: function (other) {
-        return this.is(other.toString()) ? 0 : -1;
-    },
-
-    is: function (unitString) {
-        return this.toString() === unitString;
-    },
-
-    isLength: function () {
-        return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/));
-    },
-
-    isEmpty: function () {
-        return this.numerator.length === 0 && this.denominator.length === 0;
-    },
-
-    isSingular: function() {
-        return this.numerator.length <= 1 && this.denominator.length === 0;
-    },
+Unit.prototype = new Node();
+Unit.prototype.type = "Unit";
+Unit.prototype.clone = function () {
+    return new Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit);
+};
+Unit.prototype.genCSS = function (env, output) {
+    if (this.numerator.length >= 1) {
+        output.add(this.numerator[0]);
+    } else
+    if (this.denominator.length >= 1) {
+        output.add(this.denominator[0]);
+    } else
+    if ((!env || !env.strictUnits) && this.backupUnit) {
+        output.add(this.backupUnit);
+    }
+};
+Unit.prototype.toString = function () {
+    var i, returnStr = this.numerator.join("*");
+    for (i = 0; i < this.denominator.length; i++) {
+        returnStr += "/" + this.denominator[i];
+    }
+    return returnStr;
+};
+Unit.prototype.compare = function (other) {
+    return this.is(other.toString()) ? 0 : -1;
+};
+Unit.prototype.is = function (unitString) {
+    return this.toString() === unitString;
+};
+Unit.prototype.isLength = function () {
+    return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/));
+};
+Unit.prototype.isEmpty = function () {
+    return this.numerator.length === 0 && this.denominator.length === 0;
+};
+Unit.prototype.isSingular = function() {
+    return this.numerator.length <= 1 && this.denominator.length === 0;
+};
+Unit.prototype.map = function(callback) {
+    var i;
 
-    map: function(callback) {
-        var i;
+    for (i = 0; i < this.numerator.length; i++) {
+        this.numerator[i] = callback(this.numerator[i], false);
+    }
 
-        for (i = 0; i < this.numerator.length; i++) {
-            this.numerator[i] = callback(this.numerator[i], false);
-        }
+    for (i = 0; i < this.denominator.length; i++) {
+        this.denominator[i] = callback(this.denominator[i], true);
+    }
+};
+Unit.prototype.usedUnits = function() {
+    var group, result = {}, mapUnit;
 
-        for (i = 0; i < this.denominator.length; i++) {
-            this.denominator[i] = callback(this.denominator[i], true);
+    mapUnit = function (atomicUnit) {
+        /*jshint loopfunc:true */
+        if (group.hasOwnProperty(atomicUnit) && !result[groupName]) {
+            result[groupName] = atomicUnit;
         }
-    },
-
-    usedUnits: function() {
-        var group, result = {}, mapUnit;
-
-        mapUnit = function (atomicUnit) {
-            /*jshint loopfunc:true */
-            if (group.hasOwnProperty(atomicUnit) && !result[groupName]) {
-                result[groupName] = atomicUnit;
-            }
 
-            return atomicUnit;
-        };
+        return atomicUnit;
+    };
 
-        for (var groupName in unitConversions) {
-            if (unitConversions.hasOwnProperty(groupName)) {
-                group = unitConversions[groupName];
+    for (var groupName in unitConversions) {
+        if (unitConversions.hasOwnProperty(groupName)) {
+            group = unitConversions[groupName];
 
-                this.map(mapUnit);
-            }
+            this.map(mapUnit);
         }
+    }
 
-        return result;
-    },
-
-    cancel: function () {
-        var counter = {}, atomicUnit, i, backup;
+    return result;
+};
+Unit.prototype.cancel = function () {
+    var counter = {}, atomicUnit, i, backup;
 
-        for (i = 0; i < this.numerator.length; i++) {
-            atomicUnit = this.numerator[i];
-            if (!backup) {
-                backup = atomicUnit;
-            }
-            counter[atomicUnit] = (counter[atomicUnit] || 0) + 1;
+    for (i = 0; i < this.numerator.length; i++) {
+        atomicUnit = this.numerator[i];
+        if (!backup) {
+            backup = atomicUnit;
         }
+        counter[atomicUnit] = (counter[atomicUnit] || 0) + 1;
+    }
 
-        for (i = 0; i < this.denominator.length; i++) {
-            atomicUnit = this.denominator[i];
-            if (!backup) {
-                backup = atomicUnit;
-            }
-            counter[atomicUnit] = (counter[atomicUnit] || 0) - 1;
+    for (i = 0; i < this.denominator.length; i++) {
+        atomicUnit = this.denominator[i];
+        if (!backup) {
+            backup = atomicUnit;
         }
+        counter[atomicUnit] = (counter[atomicUnit] || 0) - 1;
+    }
+
+    this.numerator = [];
+    this.denominator = [];
+
+    for (atomicUnit in counter) {
+        if (counter.hasOwnProperty(atomicUnit)) {
+            var count = counter[atomicUnit];
 
-        this.numerator = [];
-        this.denominator = [];
-
-        for (atomicUnit in counter) {
-            if (counter.hasOwnProperty(atomicUnit)) {
-                var count = counter[atomicUnit];
-
-                if (count > 0) {
-                    for (i = 0; i < count; i++) {
-                        this.numerator.push(atomicUnit);
-                    }
-                } else if (count < 0) {
-                    for (i = 0; i < -count; i++) {
-                        this.denominator.push(atomicUnit);
-                    }
+            if (count > 0) {
+                for (i = 0; i < count; i++) {
+                    this.numerator.push(atomicUnit);
+                }
+            } else if (count < 0) {
+                for (i = 0; i < -count; i++) {
+                    this.denominator.push(atomicUnit);
                 }
             }
         }
+    }
 
-        if (this.numerator.length === 0 && this.denominator.length === 0 && backup) {
-            this.backupUnit = backup;
-        }
-
-        this.numerator.sort();
-        this.denominator.sort();
+    if (this.numerator.length === 0 && this.denominator.length === 0 && backup) {
+        this.backupUnit = backup;
     }
+
+    this.numerator.sort();
+    this.denominator.sort();
 };
-return Unit;
-};
\ No newline at end of file
+module.exports = Unit;
diff --git a/lib/less/tree/url.js b/lib/less/tree/url.js
index 811c407..603e4b7 100644
--- a/lib/less/tree/url.js
+++ b/lib/less/tree/url.js
@@ -1,53 +1,50 @@
-module.exports = function (tree) {
+var Node = require("./node.js");
 
 var URL = function (val, currentFileInfo, isEvald) {
     this.value = val;
     this.currentFileInfo = currentFileInfo;
     this.isEvald = isEvald;
 };
-URL.prototype = {
-    type: "Url",
-    accept: function (visitor) {
-        this.value = visitor.visit(this.value);
-    },
-    genCSS: function (env, output) {
-        output.add("url(");
-        this.value.genCSS(env, output);
-        output.add(")");
-    },
-    toCSS: tree.toCSS,
-    eval: function (ctx) {
-        var val = this.value.eval(ctx),
-            rootpath;
+URL.prototype = new Node();
+URL.prototype.type = "Url";
+URL.prototype.accept = function (visitor) {
+    this.value = visitor.visit(this.value);
+};
+URL.prototype.genCSS = function (env, output) {
+    output.add("url(");
+    this.value.genCSS(env, output);
+    output.add(")");
+};
+URL.prototype.eval = function (ctx) {
+    var val = this.value.eval(ctx),
+        rootpath;
 
-        if (!this.isEvald) {
-            // Add the base path if the URL is relative
-            rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
-            if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) {
-                if (!val.quote) {
-                    rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; });
-                }
-                val.value = rootpath + val.value;
+    if (!this.isEvald) {
+        // Add the base path if the URL is relative
+        rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
+        if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) {
+            if (!val.quote) {
+                rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; });
             }
+            val.value = rootpath + val.value;
+        }
 
-            val.value = ctx.normalizePath(val.value);
+        val.value = ctx.normalizePath(val.value);
 
-            // Add url args if enabled
-            if (ctx.urlArgs) {
-                if (!val.value.match(/^\s*data:/)) {
-                    var delimiter = val.value.indexOf('?') === -1 ? '?' : '&';
-                    var urlArgs = delimiter + ctx.urlArgs;
-                    if (val.value.indexOf('#') !== -1) {
-                        val.value = val.value.replace('#', urlArgs + '#');
-                    } else {
-                        val.value += urlArgs;
-                    }
+        // Add url args if enabled
+        if (ctx.urlArgs) {
+            if (!val.value.match(/^\s*data:/)) {
+                var delimiter = val.value.indexOf('?') === -1 ? '?' : '&';
+                var urlArgs = delimiter + ctx.urlArgs;
+                if (val.value.indexOf('#') !== -1) {
+                    val.value = val.value.replace('#', urlArgs + '#');
+                } else {
+                    val.value += urlArgs;
                 }
             }
         }
-
-        return new(URL)(val, this.currentFileInfo, true);
     }
+
+    return new(URL)(val, this.currentFileInfo, true);
 };
-return URL;
-};
+module.exports = URL;
diff --git a/lib/less/tree/value.js b/lib/less/tree/value.js
index ea07367..fb1c7f8 100644
--- a/lib/less/tree/value.js
+++ b/lib/less/tree/value.js
@@ -1,34 +1,31 @@
-module.exports = function (tree) {
+var Node = require("./node.js");
 
 var Value = function (value) {
     this.value = value;
 };
-Value.prototype = {
-    type: "Value",
-    accept: function (visitor) {
-        if (this.value) {
-            this.value = visitor.visitArray(this.value);
-        }
-    },
-    eval: function (env) {
-        if (this.value.length === 1) {
-            return this.value[0].eval(env);
-        } else {
-            return new(Value)(this.value.map(function (v) {
-                return v.eval(env);
-            }));
-        }
-    },
-    genCSS: function (env, output) {
-        var i;
-        for(i = 0; i < this.value.length; i++) {
-            this.value[i].genCSS(env, output);
-            if (i+1 < this.value.length) {
-                output.add((env && env.compress) ? ',' : ', ');
-            }
-        }
-    },
-    toCSS: tree.toCSS
+Value.prototype = new Node();
+Value.prototype.type = "Value";
+Value.prototype.accept = function (visitor) {
+    if (this.value) {
+        this.value = visitor.visitArray(this.value);
+    }
 };
-return Value;
+Value.prototype.eval = function (env) {
+    if (this.value.length === 1) {
+        return this.value[0].eval(env);
+    } else {
+        return new(Value)(this.value.map(function (v) {
+            return v.eval(env);
+        }));
+    }
+};
+Value.prototype.genCSS = function (env, output) {
+    var i;
+    for(i = 0; i < this.value.length; i++) {
+        this.value[i].genCSS(env, output);
+        if (i+1 < this.value.length) {
+            output.add((env && env.compress) ? ',' : ', ');
+        }
+    }
 };
+module.exports = Value;
diff --git a/lib/less/tree/variable.js b/lib/less/tree/variable.js
index 71c373f..85641a7 100644
--- a/lib/less/tree/variable.js
+++ b/lib/less/tree/variable.js
@@ -1,44 +1,49 @@
-module.exports = function (tree) {
+var Node = require("./node.js");
 
 var Variable = function (name, index, currentFileInfo) {
     this.name = name;
     this.index = index;
     this.currentFileInfo = currentFileInfo || {};
 };
-Variable.prototype = {
-    type: "Variable",
-    eval: function (env) {
-        var variable, name = this.name;
+Variable.prototype = new Node();
+Variable.prototype.type = "Variable";
+Variable.prototype.eval = function (env) {
+    var variable, name = this.name;
 
-        if (name.indexOf('@@') === 0) {
-            name = '@' + new(Variable)(name.slice(1)).eval(env).value;
-        }
+    if (name.indexOf('@@') === 0) {
+        name = '@' + new(Variable)(name.slice(1)).eval(env).value;
+    }
 
-        if (this.evaluating) {
-            throw { type: 'Name',
-                    message: "Recursive variable definition for " + name,
-                    filename: this.currentFileInfo.file,
-                    index: this.index };
-        }
+    if (this.evaluating) {
+        throw { type: 'Name',
+                message: "Recursive variable definition for " + name,
+                filename: this.currentFileInfo.file,
+                index: this.index };
+    }
 
-        this.evaluating = true;
+    this.evaluating = true;
 
-        variable = tree.find(env.frames, function (frame) {
-            var v = frame.variable(name);
-            if (v) {
-                return v.value.eval(env);
-            }
-        });
-        if (variable) {
-            this.evaluating = false;
-            return variable;
-        } else {
-            throw { type: 'Name',
-                    message: "variable " + name + " is undefined",
-                    filename: this.currentFileInfo.filename,
-                    index: this.index };
+    variable = this.find(env.frames, function (frame) {
+        var v = frame.variable(name);
+        if (v) {
+            return v.value.eval(env);
         }
+    });
+    if (variable) {
+        this.evaluating = false;
+        return variable;
+    } else {
+        throw { type: 'Name',
+                message: "variable " + name + " is undefined",
+                filename: this.currentFileInfo.filename,
+                index: this.index };
     }
 };
-return Variable;
+Variable.prototype.find = function (obj, fun) {
+    for (var i = 0, r; i < obj.length; i++) {
+        r = fun.call(obj, obj[i]);
+        if (r) { return r; }
+    }
+    return null;
 };
+module.exports = Variable;
diff --git a/lib/less/visitor/import-visitor.js b/lib/less/visitor/import-visitor.js
index 1dc19f2..2f1c24b 100644
--- a/lib/less/visitor/import-visitor.js
+++ b/lib/less/visitor/import-visitor.js
@@ -1,9 +1,10 @@
+var contexts = require("../env.js");
 module.exports = function (visitor, tree) {
     var ImportVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) {
         this._visitor = new visitor(this);
         this._importer = importer;
         this._finish = finish;
-        this.env = evalEnv || new tree.evalEnv();
+        this.env = evalEnv || new contexts.evalEnv();
         this.importCount = 0;
         this.onceFileDetectionMap = onceFileDetectionMap || {};
         this.recursionDetector = {};
@@ -54,7 +55,7 @@ module.exports = function (visitor, tree) {
                 if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) {
                     importNode = evaldImportNode;
                     this.importCount++;
-                    var env = new tree.evalEnv(this.env, this.env.frames.slice(0));
+                    var env = new contexts.evalEnv(this.env, this.env.frames.slice(0));
 
                     if (importNode.options.multiple) {
                         env.importMultiple = true;

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/less.js.git



More information about the Pkg-javascript-commits mailing list