diff --git a/Gruntfile.js b/Gruntfile.js index 3fba33ea..b47a0dc4 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -47,6 +47,14 @@ module.exports = function(grunt) { 'node node_modules/http-server/bin/http-server test/sourcemaps -p 8084'].join('&&') } }, + + browserify: { + dist: { + files: { + 'dist/less.js': ['lib/less/non-node-index.js', 'lib/less/browser.js', 'lib/less/environment/browser.js'] + } + } + }, concat: { options: { stripBanners: 'all', diff --git a/build/build.yml b/build/build.yml index 2dd328e1..098901e4 100644 --- a/build/build.yml +++ b/build/build.yml @@ -137,7 +137,7 @@ tree: - <%= build.lib %>/tree/javascript.js - <%= build.lib %>/tree/keyword.js - <%= build.lib %>/tree/media.js - - <%= build.lib %>/tree/mixin.js + - <%= build.lib %>/tree/mixin-call.js - <%= build.lib %>/tree/negative.js - <%= build.lib %>/tree/operation.js - <%= build.lib %>/tree/paren.js diff --git a/lib/less/colors.js b/lib/less/colors.js index e509b602..dd213f2c 100644 --- a/lib/less/colors.js +++ b/lib/less/colors.js @@ -1,4 +1,4 @@ -(function (tree) { +module.exports = function (tree) { tree.colors = { 'aliceblue':'#f0f8ff', 'antiquewhite':'#faebd7', @@ -148,4 +148,4 @@ 'yellow':'#ffff00', 'yellowgreen':'#9acd32' }; -})(require('./tree')); +}; diff --git a/lib/less/encoder.js b/lib/less/encoder.js deleted file mode 100644 index 65902f7a..00000000 --- a/lib/less/encoder.js +++ /dev/null @@ -1,4 +0,0 @@ -// base64 encoder implementation for node -exports.encodeBase64 = function(str) { - return new Buffer(str).toString('base64'); -}; diff --git a/lib/less/env.js b/lib/less/env.js index 41eabe50..80192941 100644 --- a/lib/less/env.js +++ b/lib/less/env.js @@ -1,4 +1,4 @@ -(function (tree) { +module.exports = function (tree) { var parseCopyProperties = [ 'paths', // option - unmodified - paths to search for imports on @@ -132,4 +132,4 @@ } }; -})(require('./tree')); +}; diff --git a/lib/less/environments/node.js b/lib/less/environments/node.js index 7aab3174..a4cae9be 100644 --- a/lib/less/environments/node.js +++ b/lib/less/environments/node.js @@ -8,6 +8,9 @@ module.exports = { warn: function(env, msg) { console.warn(msg); }, + encodeBase64: function encodeBase64(env, str) { + return new Buffer(str).toString('base64'); + }, getPath: function (env, filename) { var j = filename.lastIndexOf('/'); if (j < 0) { diff --git a/lib/less/extend-visitor.js b/lib/less/extend-visitor.js index 4b397a98..211e6add 100644 --- a/lib/less/extend-visitor.js +++ b/lib/less/extend-visitor.js @@ -1,4 +1,4 @@ -(function (tree) { +module.exports = function (tree) { /*jshint loopfunc:true */ tree.extendFinderVisitor = function() { @@ -413,4 +413,4 @@ } }; -})(require('./tree')); +}; diff --git a/lib/less/functions.js b/lib/less/functions.js index 5d4c0ebf..8fac47aa 100644 --- a/lib/less/functions.js +++ b/lib/less/functions.js @@ -1,6 +1,6 @@ -(function (tree) { +module.exports = function (less, tree) { -tree.functions = { +var functions = { rgb: function (r, g, b) { return this.rgba(r, g, b, 1.0); }, @@ -530,7 +530,7 @@ tree.functions = { if (useBase64) { try { - returner = require('./encoder').encodeBase64(returner); // TODO browser implementation + returner = less.Parser.environment.encodeBase64(this.env, returner); } catch(e) { useBase64 = false; } @@ -687,30 +687,30 @@ tree.defaultFunc = { }; function initFunctions() { - var f, tf = tree.functions; + var f; // math for (f in mathFunctions) { if (mathFunctions.hasOwnProperty(f)) { - tf[f] = _math.bind(null, Math[f], mathFunctions[f]); + functions[f] = _math.bind(null, Math[f], mathFunctions[f]); } } // color blending for (f in colorBlendMode) { if (colorBlendMode.hasOwnProperty(f)) { - tf[f] = colorBlend.bind(null, colorBlendMode[f]); + functions[f] = colorBlend.bind(null, colorBlendMode[f]); } } // default f = tree.defaultFunc; - tf["default"] = f.eval.bind(f); + functions["default"] = f.eval.bind(f); } initFunctions(); function hsla(color) { - return tree.functions.hsla(color.h, color.s, color.l, color.a); + return functions.hsla(color.h, color.s, color.l, color.a); } function scaled(n, size) { @@ -748,11 +748,14 @@ tree.fround = function(env, value) { } }; -tree.functionCall = function(env, currentFileInfo) { +tree.functionCall = function(env, currentFileInfo, environment) { this.env = env; + this.environment = environment; this.currentFileInfo = currentFileInfo; }; -tree.functionCall.prototype = tree.functions; +tree.functionCall.prototype = functions; -})(require('./tree')); +return functions; + +}; diff --git a/lib/less/import-visitor.js b/lib/less/import-visitor.js index 0d773ca0..7b1cbccf 100644 --- a/lib/less/import-visitor.js +++ b/lib/less/import-visitor.js @@ -1,4 +1,4 @@ -(function (tree) { +module.exports = function (tree) { tree.importVisitor = function(importer, finish, evalEnv) { this._visitor = new tree.visitor(this); this._importer = importer; @@ -115,4 +115,4 @@ } }; -})(require('./tree')); \ No newline at end of file +}; diff --git a/lib/less/index.js b/lib/less/index.js index 0138cd3d..a2c69175 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -1,137 +1,95 @@ -var less = { - version: [1, 6, 3], - Parser: require('./parser').Parser, - tree: require('./tree'), - render: function (input, options, callback) { - options = options || {}; +var less = require("./non-node-index.js"); - if (typeof(options) === 'function') { - callback = options; - options = {}; - } +less.render = function (input, options, callback) { + options = options || {}; - var parser = new(less.Parser)(options), - ee; - - if (callback) { - parser.parse(input, function (e, root) { - if (e) { callback(e); return; } - var css; - try { - css = root && root.toCSS && root.toCSS(options); - } - catch (err) { callback(err); return; } - callback(null, css); - }); - } else { - ee = new (require('events').EventEmitter)(); - - process.nextTick(function () { - parser.parse(input, function (e, root) { - if (e) { return ee.emit('error', e); } - try { ee.emit('success', root.toCSS(options)); } - catch (err) { ee.emit('error', err); } - }); - }); - return ee; - } - }, - formatError: function(ctx, options) { - options = options || {}; - - var message = ""; - var extract = ctx.extract; - var error = []; - var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; }; - - // only output a stack if it isn't a less error - if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } - - if (!ctx.hasOwnProperty('index') || !extract) { - return ctx.stack || ctx.message; - } - - if (typeof(extract[0]) === 'string') { - error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey')); - } - - if (typeof(extract[1]) === 'string') { - var errorTxt = ctx.line + ' '; - if (extract[1]) { - errorTxt += extract[1].slice(0, ctx.column) + - stylize(stylize(stylize(extract[1][ctx.column], 'bold') + - extract[1].slice(ctx.column + 1), 'red'), 'inverse'); - } - error.push(errorTxt); - } - - if (typeof(extract[2]) === 'string') { - error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey')); - } - error = error.join('\n') + stylize('', 'reset') + '\n'; - - message += stylize(ctx.type + 'Error: ' + ctx.message, 'red'); - if (ctx.filename) { - message += stylize(' in ', 'red') + ctx.filename + - stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey'); - } - - message += '\n' + error; - - if (ctx.callLine) { - message += stylize('from ', 'red') + (ctx.filename || '') + '/n'; - message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n'; - } - - return message; - }, - writeError: function (ctx, options) { - options = options || {}; - if (options.silent) { return; } - console.error(less.formatError(ctx, options)); + if (typeof(options) === 'function') { + callback = options; + options = {}; } + + var parser = new(less.Parser)(options), + ee; + + if (callback) { + parser.parse(input, function (e, root) { + if (e) { callback(e); return; } + var css; + try { + css = root && root.toCSS && root.toCSS(options); + } + catch (err) { callback(err); return; } + callback(null, css); + }); + } else { + ee = new (require('events').EventEmitter)(); + + process.nextTick(function () { + parser.parse(input, function (e, root) { + if (e) { return ee.emit('error', e); } + try { ee.emit('success', root.toCSS(options)); } + catch (err) { ee.emit('error', err); } + }); + }); + return ee; + } +}; +less.formatError = function(ctx, options) { + options = options || {}; + + var message = ""; + var extract = ctx.extract; + var error = []; + var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; }; + + // only output a stack if it isn't a less error + if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); } + + if (!ctx.hasOwnProperty('index') || !extract) { + return ctx.stack || ctx.message; + } + + if (typeof(extract[0]) === 'string') { + error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey')); + } + + if (typeof(extract[1]) === 'string') { + var errorTxt = ctx.line + ' '; + if (extract[1]) { + errorTxt += extract[1].slice(0, ctx.column) + + stylize(stylize(stylize(extract[1][ctx.column], 'bold') + + extract[1].slice(ctx.column + 1), 'red'), 'inverse'); + } + error.push(errorTxt); + } + + if (typeof(extract[2]) === 'string') { + error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey')); + } + error = error.join('\n') + stylize('', 'reset') + '\n'; + + message += stylize(ctx.type + 'Error: ' + ctx.message, 'red'); + if (ctx.filename) { + message += stylize(' in ', 'red') + ctx.filename + + stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey'); + } + + message += '\n' + error; + + if (ctx.callLine) { + message += stylize('from ', 'red') + (ctx.filename || '') + '/n'; + message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n'; + } + + return message; +}; + +less.writeError = function (ctx, options) { + options = options || {}; + if (options.silent) { return; } + console.error(less.formatError(ctx, options)); }; less.Parser.environment = require("./environments/node"); -require('./tree/color'); -require('./tree/directive'); -require('./tree/detached-ruleset'); -require('./tree/operation'); -require('./tree/dimension'); -require('./tree/keyword'); -require('./tree/variable'); -require('./tree/ruleset'); -require('./tree/element'); -require('./tree/selector'); -require('./tree/quoted'); -require('./tree/expression'); -require('./tree/rule'); -require('./tree/call'); -require('./tree/url'); -require('./tree/alpha'); -require('./tree/import'); -require('./tree/mixin'); -require('./tree/comment'); -require('./tree/anonymous'); -require('./tree/value'); -require('./tree/javascript'); -require('./tree/assignment'); -require('./tree/condition'); -require('./tree/paren'); -require('./tree/media'); -require('./tree/unicode-descriptor'); -require('./tree/negative'); -require('./tree/extend'); -require('./tree/ruleset-call'); -require('./env'); -require('./functions'); -require('./colors'); -require('./visitor.js'); -require('./import-visitor.js'); -require('./extend-visitor.js'); -require('./join-selector-visitor.js'); -require('./to-css-visitor.js'); -require('./source-map-output.js'); - module.exports = less; diff --git a/lib/less/join-selector-visitor.js b/lib/less/join-selector-visitor.js index 75a61d6e..13b3a590 100644 --- a/lib/less/join-selector-visitor.js +++ b/lib/less/join-selector-visitor.js @@ -1,4 +1,4 @@ -(function (tree) { +module.exports = function (tree) { tree.joinSelectorVisitor = function() { this.contexts = [[]]; this._visitor = new tree.visitor(this); @@ -41,4 +41,4 @@ } }; -})(require('./tree')); \ No newline at end of file +}; \ No newline at end of file diff --git a/lib/less/non-node-index.js b/lib/less/non-node-index.js new file mode 100644 index 00000000..5c3f8c7e --- /dev/null +++ b/lib/less/non-node-index.js @@ -0,0 +1,18 @@ +var less = { + version: [1, 6, 3] +}; + +less.tree = (require('./tree'))(less); +less.Parser = (require('./parser'))(less, less.tree); +less.tree.functions = (require('./functions'))(less, less.tree); +require('./env')(less.tree); + +require('./colors')(less.tree); +require('./visitor.js')(less.tree); +require('./import-visitor.js')(less.tree); +require('./extend-visitor.js')(less.tree); +require('./join-selector-visitor.js')(less.tree); +require('./to-css-visitor.js')(less.tree); +require('./source-map-output.js')(less.tree); + +module.exports = less; diff --git a/lib/less/parser.js b/lib/less/parser.js index f357b879..8c94f8bd 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -1,11 +1,4 @@ -var less, tree; - -// Node.js does not have a header file added which defines less -if (less === undefined) { - less = exports; - tree = require('./tree'); - less.mode = 'node'; -} +module.exports = function(less, tree) { // // less.js - parser // @@ -39,7 +32,7 @@ if (less === undefined) { // It also takes care of moving all the indices forwards. // // -less.Parser = function Parser(env) { +var Parser = function Parser(env) { var input, // LeSS input string i, // current index in `input` j, // current chunk @@ -2064,16 +2057,19 @@ less.Parser = function Parser(env) { }; return parser; }; -less.Parser.serializeVars = function(vars) { +Parser.serializeVars = function(vars) { var s = ''; for (var name in vars) { if (Object.hasOwnProperty.call(vars, name)) { var value = vars[name]; s += ((name[0] === '@') ? '' : '@') + name +': '+ value + - ((('' + value).slice(-1) === ';') ? '' : ';'); + ((('' + value).slice(-1) === ';') ? '' : ';'); } } return s; }; + +return Parser; +}; \ No newline at end of file diff --git a/lib/less/source-map-output.js b/lib/less/source-map-output.js index 9a090d4e..2fd10ec2 100644 --- a/lib/less/source-map-output.js +++ b/lib/less/source-map-output.js @@ -1,4 +1,4 @@ -(function (tree) { +module.exports = function (tree) { tree.sourceMapOutput = function (options) { this._css = []; @@ -138,4 +138,4 @@ return this._css.join(''); }; -})(require('./tree')); +}; diff --git a/lib/less/to-css-visitor.js b/lib/less/to-css-visitor.js index 53f4ee03..c58f11a6 100644 --- a/lib/less/to-css-visitor.js +++ b/lib/less/to-css-visitor.js @@ -1,4 +1,4 @@ -(function (tree) { +module.exports = function (tree) { tree.toCSSVisitor = function(env) { this._visitor = new tree.visitor(this); this._env = env; @@ -236,5 +236,4 @@ }); } }; - -})(require('./tree')); \ No newline at end of file +}; diff --git a/lib/less/tree.js b/lib/less/tree.js index cd1352f5..fac177b4 100644 --- a/lib/less/tree.js +++ b/lib/less/tree.js @@ -1,5 +1,7 @@ -(function (tree) { +module.exports = function (less) { +var tree = {}; + tree.debugInfo = function(env, ctx, lineSeperator) { var result=""; if (env.dumpLineNumbers && !env.compress) { @@ -94,4 +96,43 @@ tree.outputRuleset = function (env, output, rules) { env.tabLevel--; }; -})(require('./tree')); +tree.Alpha = require('./tree/alpha')(tree); +tree.Color = require('./tree/color')(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); +tree.URL = require('./tree/url')(tree); +tree.Import = require('./tree/import')(tree); +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); + +return tree; + +}; \ No newline at end of file diff --git a/lib/less/tree/alpha.js b/lib/less/tree/alpha.js index e827fd08..eb247adf 100644 --- a/lib/less/tree/alpha.js +++ b/lib/less/tree/alpha.js @@ -1,15 +1,15 @@ -(function (tree) { +module.exports = function (tree) { -tree.Alpha = function (val) { +var Alpha = function (val) { this.value = val; }; -tree.Alpha.prototype = { +Alpha.prototype = { type: "Alpha", accept: function (visitor) { this.value = visitor.visit(this.value); }, eval: function (env) { - if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); } + if (this.value.eval) { return new Alpha(this.value.eval(env)); } return this; }, genCSS: function (env, output) { @@ -25,5 +25,5 @@ tree.Alpha.prototype = { }, toCSS: tree.toCSS }; - -})(require('../tree')); +return Alpha; +}; diff --git a/lib/less/tree/anonymous.js b/lib/less/tree/anonymous.js index 3cfae771..e6be3f08 100644 --- a/lib/less/tree/anonymous.js +++ b/lib/less/tree/anonymous.js @@ -1,15 +1,15 @@ -(function (tree) { +module.exports = function (tree) { -tree.Anonymous = function (string, index, currentFileInfo, mapLines) { +var Anonymous = function (string, index, currentFileInfo, mapLines) { this.value = string.value || string; this.index = index; this.mapLines = mapLines; this.currentFileInfo = currentFileInfo; }; -tree.Anonymous.prototype = { +Anonymous.prototype = { type: "Anonymous", eval: function () { - return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); + return new Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines); }, compare: function (x) { if (!x.toCSS) { @@ -30,5 +30,5 @@ tree.Anonymous.prototype = { }, toCSS: tree.toCSS }; - -})(require('../tree')); +return Anonymous; +}; diff --git a/lib/less/tree/assignment.js b/lib/less/tree/assignment.js index 61d22fca..afe62933 100644 --- a/lib/less/tree/assignment.js +++ b/lib/less/tree/assignment.js @@ -1,17 +1,17 @@ -(function (tree) { +module.exports = function (tree) { -tree.Assignment = function (key, val) { +var Assignment = function (key, val) { this.key = key; this.value = val; }; -tree.Assignment.prototype = { +Assignment.prototype = { type: "Assignment", accept: function (visitor) { this.value = visitor.visit(this.value); }, eval: function (env) { if (this.value.eval) { - return new(tree.Assignment)(this.key, this.value.eval(env)); + return new(Assignment)(this.key, this.value.eval(env)); } return this; }, @@ -25,5 +25,5 @@ tree.Assignment.prototype = { }, toCSS: tree.toCSS }; - -})(require('../tree')); +return Assignment; +}; diff --git a/lib/less/tree/attribute.js b/lib/less/tree/attribute.js new file mode 100644 index 00000000..4103bfc4 --- /dev/null +++ b/lib/less/tree/attribute.js @@ -0,0 +1,29 @@ +module.exports = function (tree) { + +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); + } + + return '[' + value + ']'; + } +}; +return Attribute; +}; diff --git a/lib/less/tree/call.js b/lib/less/tree/call.js index aa6acb9c..e4a51013 100644 --- a/lib/less/tree/call.js +++ b/lib/less/tree/call.js @@ -1,15 +1,15 @@ -(function (tree) { +module.exports = function (tree) { // // A function call node. // -tree.Call = function (name, args, index, currentFileInfo) { +var Call = function (name, args, index, currentFileInfo) { this.name = name; this.args = args; this.index = index; this.currentFileInfo = currentFileInfo; }; -tree.Call.prototype = { +Call.prototype = { type: "Call", accept: function (visitor) { if (this.args) { @@ -49,7 +49,7 @@ tree.Call.prototype = { } } - return new tree.Call(this.name, args, this.index, this.currentFileInfo); + return new Call(this.name, args, this.index, this.currentFileInfo); }, genCSS: function (env, output) { @@ -67,5 +67,5 @@ tree.Call.prototype = { toCSS: tree.toCSS }; - -})(require('../tree')); +return Call; +}; diff --git a/lib/less/tree/color.js b/lib/less/tree/color.js index 3d841b6b..c902a139 100644 --- a/lib/less/tree/color.js +++ b/lib/less/tree/color.js @@ -1,8 +1,8 @@ -(function (tree) { +module.exports = function (tree) { // // RGB Colors - #ff0014, #eee // -tree.Color = function (rgb, a) { +var Color = function (rgb, a) { // // The end goal here, is to parse the arguments // into an integer triplet, such as `128, 255, 0` @@ -23,7 +23,7 @@ tree.Color = function (rgb, a) { this.alpha = typeof(a) === 'number' ? a : 1; }; -tree.Color.prototype = { +Color.prototype = { type: "Color", eval: function () { return this; }, luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); }, @@ -79,7 +79,7 @@ tree.Color.prototype = { for (var c = 0; c < 3; c++) { rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]); } - return new(tree.Color)(rgb, alpha); + return new(Color)(rgb, alpha); }, toRGB: function () { @@ -153,13 +153,13 @@ tree.Color.prototype = { } }; -tree.Color.fromKeyword = function(keyword) { +Color.fromKeyword = function(keyword) { var c, key = keyword.toLowerCase(); if (tree.colors.hasOwnProperty(key)) { - c = new(tree.Color)(tree.colors[key].slice(1)); + c = new(Color)(tree.colors[key].slice(1)); } else if (key === "transparent") { - c = new(tree.Color)([0, 0, 0], 0); + c = new(Color)([0, 0, 0], 0); } if (c) { @@ -178,5 +178,5 @@ function toHex(v) { function clamp(v, max) { return Math.min(Math.max(v, 0), max); } - -})(require('../tree')); +return Color; +}; diff --git a/lib/less/tree/combinator.js b/lib/less/tree/combinator.js new file mode 100644 index 00000000..0fd4dd87 --- /dev/null +++ b/lib/less/tree/combinator.js @@ -0,0 +1,40 @@ +module.exports = function (tree) { + +var Combinator = function (value) { + if (value === ' ') { + this.value = ' '; + } else { + this.value = value ? value.trim() : ""; + } +}; +Combinator.prototype = { + type: "Combinator", + _outputMap: { + '' : '', + ' ' : ' ', + ':' : ' :', + '+' : ' + ', + '~' : ' ~ ', + '>' : ' > ', + '|' : '|', + '^' : ' ^ ', + '^^' : ' ^^ ' + }, + _outputMapCompressed: { + '' : '', + ' ' : ' ', + ':' : ' :', + '+' : '+', + '~' : '~', + '>' : '>', + '|' : '|', + '^' : '^', + '^^' : '^^' + }, + genCSS: function (env, output) { + output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); + }, + toCSS: tree.toCSS +}; +return Combinator; +}; diff --git a/lib/less/tree/comment.js b/lib/less/tree/comment.js index c39606e0..05bc2207 100644 --- a/lib/less/tree/comment.js +++ b/lib/less/tree/comment.js @@ -1,11 +1,11 @@ -(function (tree) { +module.exports = function (tree) { -tree.Comment = function (value, silent, index, currentFileInfo) { +var Comment = function (value, silent, index, currentFileInfo) { this.value = value; this.silent = !!silent; this.currentFileInfo = currentFileInfo; }; -tree.Comment.prototype = { +Comment.prototype = { type: "Comment", genCSS: function (env, output) { if (this.debugInfo) { @@ -24,5 +24,5 @@ tree.Comment.prototype = { this.isReferenced = true; } }; - -})(require('../tree')); +return Comment; +}; diff --git a/lib/less/tree/condition.js b/lib/less/tree/condition.js index 56d221ef..fdc79812 100644 --- a/lib/less/tree/condition.js +++ b/lib/less/tree/condition.js @@ -1,13 +1,13 @@ -(function (tree) { +module.exports = function (tree) { -tree.Condition = function (op, l, r, i, negate) { +var Condition = function (op, l, r, i, negate) { this.op = op.trim(); this.lvalue = l; this.rvalue = r; this.index = i; this.negate = negate; }; -tree.Condition.prototype = { +Condition.prototype = { type: "Condition", accept: function (visitor) { this.lvalue = visitor.visit(this.lvalue); @@ -45,5 +45,5 @@ tree.Condition.prototype = { return this.negate ? !result : result; } }; - -})(require('../tree')); +return Condition; +}; diff --git a/lib/less/tree/detached-ruleset.js b/lib/less/tree/detached-ruleset.js index 0a5512aa..a58029ca 100644 --- a/lib/less/tree/detached-ruleset.js +++ b/lib/less/tree/detached-ruleset.js @@ -1,20 +1,21 @@ -(function (tree) { +module.exports = function (tree) { -tree.DetachedRuleset = function (ruleset, frames) { +var DetachedRuleset = function (ruleset, frames) { this.ruleset = ruleset; this.frames = frames; }; -tree.DetachedRuleset.prototype = { +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 tree.DetachedRuleset(this.ruleset, frames); + 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); } }; -})(require('../tree')); +return DetachedRuleset; +}; diff --git a/lib/less/tree/dimension.js b/lib/less/tree/dimension.js index aecec136..b74becc4 100644 --- a/lib/less/tree/dimension.js +++ b/lib/less/tree/dimension.js @@ -1,15 +1,15 @@ -(function (tree) { +module.exports = function (tree, unitConversions) { // // A number with a unit // -tree.Dimension = function (value, unit) { +var Dimension = function (value, unit) { this.value = parseFloat(value); this.unit = (unit && unit instanceof tree.Unit) ? unit : new(tree.Unit)(unit ? [unit] : undefined); }; -tree.Dimension.prototype = { +Dimension.prototype = { type: "Dimension", accept: function (visitor) { this.unit = visitor.visit(this.unit); @@ -84,11 +84,11 @@ tree.Dimension.prototype = { unit.denominator = unit.denominator.concat(other.unit.numerator).sort(); unit.cancel(); } - return new(tree.Dimension)(value, unit); + return new(Dimension)(value, unit); }, compare: function (other) { - if (other instanceof tree.Dimension) { + if (other instanceof Dimension) { var a, b, aValue, bValue; @@ -126,8 +126,8 @@ tree.Dimension.prototype = { i, groupName, group, targetUnit, derivedConversions = {}, applyUnit; if (typeof conversions === 'string') { - for(i in tree.UnitConversions) { - if (tree.UnitConversions[i].hasOwnProperty(conversions)) { + for(i in unitConversions) { + if (unitConversions[i].hasOwnProperty(conversions)) { derivedConversions = {}; derivedConversions[i] = conversions; } @@ -152,7 +152,7 @@ tree.Dimension.prototype = { for (groupName in conversions) { if (conversions.hasOwnProperty(groupName)) { targetUnit = conversions[groupName]; - group = tree.UnitConversions[groupName]; + group = unitConversions[groupName]; unit.map(applyUnit); } @@ -160,165 +160,8 @@ tree.Dimension.prototype = { unit.cancel(); - return new(tree.Dimension)(value, unit); + return new(Dimension)(value, unit); } }; - -// http://www.w3.org/TR/css3-values/#absolute-lengths -tree.UnitConversions = { - length: { - 'm': 1, - 'cm': 0.01, - 'mm': 0.001, - 'in': 0.0254, - 'px': 0.0254 / 96, - 'pt': 0.0254 / 72, - 'pc': 0.0254 / 72 * 12 - }, - duration: { - 's': 1, - 'ms': 0.001 - }, - angle: { - 'rad': 1/(2*Math.PI), - 'deg': 1/360, - 'grad': 1/400, - 'turn': 1 - } +return Dimension; }; - -tree.Unit = function (numerator, denominator, backupUnit) { - this.numerator = numerator ? numerator.slice(0).sort() : []; - this.denominator = denominator ? denominator.slice(0).sort() : []; - this.backupUnit = backupUnit; -}; - -tree.Unit.prototype = { - type: "Unit", - clone: function () { - return new tree.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; - }, - - 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.denominator.length; i++) { - this.denominator[i] = callback(this.denominator[i], true); - } - }, - - usedUnits: function() { - var group, result = {}, mapUnit; - - mapUnit = function (atomicUnit) { - /*jshint loopfunc:true */ - if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { - result[groupName] = atomicUnit; - } - - return atomicUnit; - }; - - for (var groupName in tree.UnitConversions) { - if (tree.UnitConversions.hasOwnProperty(groupName)) { - group = tree.UnitConversions[groupName]; - - this.map(mapUnit); - } - } - - return result; - }, - - 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.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]; - - 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(); - } -}; - -})(require('../tree')); diff --git a/lib/less/tree/directive.js b/lib/less/tree/directive.js index b59a16bd..1bb9b6cf 100644 --- a/lib/less/tree/directive.js +++ b/lib/less/tree/directive.js @@ -1,6 +1,6 @@ -(function (tree) { +module.exports = function (tree) { -tree.Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { +var Directive = function (name, value, rules, index, currentFileInfo, debugInfo) { this.name = name; this.value = value; if (rules) { @@ -12,7 +12,7 @@ tree.Directive = function (name, value, rules, index, currentFileInfo, debugInfo this.debugInfo = debugInfo; }; -tree.Directive.prototype = { +Directive.prototype = { type: "Directive", accept: function (visitor) { var value = this.value, rules = this.rules; @@ -46,7 +46,7 @@ tree.Directive.prototype = { rules = rules.eval(env); rules.root = true; } - return new(tree.Directive)(this.name, value, rules, + 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); }, @@ -65,5 +65,5 @@ tree.Directive.prototype = { } } }; - -})(require('../tree')); +return Directive; +}; diff --git a/lib/less/tree/element.js b/lib/less/tree/element.js index 3869f6c1..925b4b93 100644 --- a/lib/less/tree/element.js +++ b/lib/less/tree/element.js @@ -1,6 +1,6 @@ -(function (tree) { +module.exports = function (tree) { -tree.Element = function (combinator, value, index, currentFileInfo) { +var Element = function (combinator, value, index, currentFileInfo) { this.combinator = combinator instanceof tree.Combinator ? combinator : new(tree.Combinator)(combinator); @@ -14,7 +14,7 @@ tree.Element = function (combinator, value, index, currentFileInfo) { this.index = index; this.currentFileInfo = currentFileInfo; }; -tree.Element.prototype = { +Element.prototype = { type: "Element", accept: function (visitor) { var value = this.value; @@ -24,7 +24,7 @@ tree.Element.prototype = { } }, eval: function (env) { - return new(tree.Element)(this.combinator, + return new(Element)(this.combinator, this.value.eval ? this.value.eval(env) : this.value, this.index, this.currentFileInfo); @@ -41,68 +41,5 @@ tree.Element.prototype = { } } }; - -tree.Attribute = function (key, op, value) { - this.key = key; - this.op = op; - this.value = value; +return Element; }; -tree.Attribute.prototype = { - type: "Attribute", - eval: function (env) { - return new(tree.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); - } - - return '[' + value + ']'; - } -}; - -tree.Combinator = function (value) { - if (value === ' ') { - this.value = ' '; - } else { - this.value = value ? value.trim() : ""; - } -}; -tree.Combinator.prototype = { - type: "Combinator", - _outputMap: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : ' + ', - '~' : ' ~ ', - '>' : ' > ', - '|' : '|', - '^' : ' ^ ', - '^^' : ' ^^ ' - }, - _outputMapCompressed: { - '' : '', - ' ' : ' ', - ':' : ' :', - '+' : '+', - '~' : '~', - '>' : '>', - '|' : '|', - '^' : '^', - '^^' : '^^' - }, - genCSS: function (env, output) { - output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]); - }, - toCSS: tree.toCSS -}; - -})(require('../tree')); diff --git a/lib/less/tree/expression.js b/lib/less/tree/expression.js index a3d1e4a6..4614e155 100644 --- a/lib/less/tree/expression.js +++ b/lib/less/tree/expression.js @@ -1,7 +1,7 @@ -(function (tree) { +module.exports = function (tree) { -tree.Expression = function (value) { this.value = value; }; -tree.Expression.prototype = { +var Expression = function (value) { this.value = value; }; +Expression.prototype = { type: "Expression", accept: function (visitor) { if (this.value) { @@ -16,7 +16,7 @@ tree.Expression.prototype = { env.inParenthesis(); } if (this.value.length > 1) { - returnValue = new(tree.Expression)(this.value.map(function (e) { + returnValue = new(Expression)(this.value.map(function (e) { return e.eval(env); })); } else if (this.value.length === 1) { @@ -50,5 +50,5 @@ tree.Expression.prototype = { }); } }; - -})(require('../tree')); +return Expression; +}; diff --git a/lib/less/tree/extend.js b/lib/less/tree/extend.js index ef9d88e4..746bdeb3 100644 --- a/lib/less/tree/extend.js +++ b/lib/less/tree/extend.js @@ -1,10 +1,10 @@ -(function (tree) { +module.exports = function (tree) { -tree.Extend = function Extend(selector, option, index) { +var Extend = function Extend(selector, option, index) { this.selector = selector; this.option = option; this.index = index; - this.object_id = tree.Extend.next_id++; + this.object_id = Extend.next_id++; this.parent_ids = [this.object_id]; switch(option) { @@ -18,18 +18,18 @@ tree.Extend = function Extend(selector, option, index) { break; } }; -tree.Extend.next_id = 0; +Extend.next_id = 0; -tree.Extend.prototype = { +Extend.prototype = { type: "Extend", accept: function (visitor) { this.selector = visitor.visit(this.selector); }, eval: function (env) { - return new(tree.Extend)(this.selector.eval(env), this.option, this.index); + return new(Extend)(this.selector.eval(env), this.option, this.index); }, clone: function (env) { - return new(tree.Extend)(this.selector, this.option, this.index); + return new(Extend)(this.selector, this.option, this.index); }, findSelfSelectors: function (selectors) { var selfElements = [], @@ -49,5 +49,5 @@ tree.Extend.prototype = { this.selfSelectors = [{ elements: selfElements }]; } }; - -})(require('../tree')); +return Extend; +}; diff --git a/lib/less/tree/import.js b/lib/less/tree/import.js index 910fc11a..4f586ad1 100644 --- a/lib/less/tree/import.js +++ b/lib/less/tree/import.js @@ -1,4 +1,4 @@ -(function (tree) { +module.exports = function (tree) { // // CSS @import node // @@ -11,7 +11,7 @@ // `import,push`, we also pass it a callback, which it'll call once // the file has been fetched, and parsed. // -tree.Import = function (path, features, options, index, currentFileInfo) { +var Import = function (path, features, options, index, currentFileInfo) { this.options = options; this.index = index; this.path = path; @@ -37,7 +37,7 @@ tree.Import = function (path, features, options, index, currentFileInfo) { // we end up with a flat structure, which can easily be imported in the parent // ruleset. // -tree.Import.prototype = { +Import.prototype = { type: "Import", accept: function (visitor) { if (this.features) { @@ -70,7 +70,7 @@ tree.Import.prototype = { return null; }, evalForImport: function (env) { - return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); + return new(Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo); }, evalPath: function (env) { var path = this.path.eval(env); @@ -99,7 +99,7 @@ tree.Import.prototype = { var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true); return this.features ? new(tree.Media)([contents], this.features.value) : [contents]; } else if (this.css) { - var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index); + var newImport = new(Import)(this.evalPath(env), features, this.options, this.index); if (!newImport.css && this.error) { throw this.error; } @@ -113,5 +113,5 @@ tree.Import.prototype = { } } }; - -})(require('../tree')); +return Import; +}; diff --git a/lib/less/tree/javascript.js b/lib/less/tree/javascript.js index 30a8559d..fdca7bf8 100644 --- a/lib/less/tree/javascript.js +++ b/lib/less/tree/javascript.js @@ -1,11 +1,11 @@ -(function (tree) { +module.exports = function (tree) { -tree.JavaScript = function (string, index, escaped) { +var JavaScript = function (string, index, escaped) { this.escaped = escaped; this.expression = string; this.index = index; }; -tree.JavaScript.prototype = { +JavaScript.prototype = { type: "JavaScript", eval: function (env) { var result, @@ -53,6 +53,5 @@ tree.JavaScript.prototype = { } } }; - -})(require('../tree')); - +return JavaScript; +}; diff --git a/lib/less/tree/keyword.js b/lib/less/tree/keyword.js index 830594d8..46f56484 100644 --- a/lib/less/tree/keyword.js +++ b/lib/less/tree/keyword.js @@ -1,7 +1,7 @@ -(function (tree) { +module.exports = function (tree) { -tree.Keyword = function (value) { this.value = value; }; -tree.Keyword.prototype = { +var Keyword = function (value) { this.value = value; }; +Keyword.prototype = { type: "Keyword", eval: function () { return this; }, genCSS: function (env, output) { @@ -10,7 +10,7 @@ tree.Keyword.prototype = { }, toCSS: tree.toCSS, compare: function (other) { - if (other instanceof tree.Keyword) { + if (other instanceof Keyword) { return other.value === this.value ? 0 : 1; } else { return -1; @@ -18,7 +18,10 @@ tree.Keyword.prototype = { } }; -tree.True = new(tree.Keyword)('true'); -tree.False = new(tree.Keyword)('false'); +//TODO move? +tree.True = new(Keyword)('true'); +tree.False = new(Keyword)('false'); -})(require('../tree')); +return Keyword; + +}; diff --git a/lib/less/tree/media.js b/lib/less/tree/media.js index 095e43e8..34c07bb7 100644 --- a/lib/less/tree/media.js +++ b/lib/less/tree/media.js @@ -1,6 +1,6 @@ -(function (tree) { +module.exports = function (tree) { -tree.Media = function (value, features, index, currentFileInfo) { +var Media = function (value, features, index, currentFileInfo) { this.index = index; this.currentFileInfo = currentFileInfo; @@ -10,7 +10,7 @@ tree.Media = function (value, features, index, currentFileInfo) { this.rules = [new(tree.Ruleset)(selectors, value)]; this.rules[0].allowImports = true; }; -tree.Media.prototype = { +Media.prototype = { type: "Media", accept: function (visitor) { if (this.features) { @@ -32,7 +32,7 @@ tree.Media.prototype = { env.mediaPath = []; } - var media = new(tree.Media)(null, [], this.index, this.currentFileInfo); + var media = new(Media)(null, [], this.index, this.currentFileInfo); if(this.debugInfo) { this.rules[0].debugInfo = this.debugInfo; media.debugInfo = this.debugInfo; @@ -153,5 +153,5 @@ tree.Media.prototype = { this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])]; } }; - -})(require('../tree')); +return Media; +}; diff --git a/lib/less/tree/mixin-call.js b/lib/less/tree/mixin-call.js new file mode 100644 index 00000000..606600ae --- /dev/null +++ b/lib/less/tree/mixin-call.js @@ -0,0 +1,152 @@ +module.exports = function (tree) { + +var Call = function (elements, args, index, currentFileInfo, important) { + this.selector = new(tree.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; + + 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; + + // 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; + } + + 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 (conditionResult[0] || conditionResult[1]) { + if (conditionResult[0] != conditionResult[1]) { + candidate.group = conditionResult[1] ? + defTrue : defFalse; + } + + candidates.push(candidate); + } + } + else { + candidates.push(candidate); + } + + match = true; + } + } + + defaultFunc.reset(); + + 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 }; + } + } + + 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)) { + mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); + mixin.originalRuleset = mixins[m].originalRuleset || mixins[m]; + } + 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(); + } + } + } + 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(', ') : "") + ")"; + } +}; +return Call; +}; diff --git a/lib/less/tree/mixin-definition.js b/lib/less/tree/mixin-definition.js new file mode 100644 index 00000000..b661c310 --- /dev/null +++ b/lib/less/tree/mixin-definition.js @@ -0,0 +1,165 @@ +module.exports = function (tree) { + +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.params = params; + this.condition = condition; + this.variadic = variadic; + this.arity = params.length; + this.rules = rules; + this._lookups = {}; + this.required = params.reduce(function (count, p) { + 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' }; + } + } + } + } + argIndex = 0; + for (i = 0; i < params.length; i++) { + if (evaldArguments[i]) { continue; } + + 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))); + } 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; + } + } + + if (params[i].variadic && args) { + for (j = argIndex; j < argsLength; j++) { + evaldArguments[j] = args[j].value.eval(env); + } + } + 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; + + frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); + + rules = this.rules.slice(0); + + 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.concat(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; + + 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); + + 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 Definition; +}; diff --git a/lib/less/tree/mixin.js b/lib/less/tree/mixin.js deleted file mode 100644 index 506561ba..00000000 --- a/lib/less/tree/mixin.js +++ /dev/null @@ -1,315 +0,0 @@ -(function (tree) { - -tree.mixin = {}; -tree.mixin.Call = function (elements, args, index, currentFileInfo, important) { - this.selector = new(tree.Selector)(elements); - this.arguments = (args && args.length) ? args : null; - this.index = index; - this.currentFileInfo = currentFileInfo; - this.important = important; -}; -tree.mixin.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; - - 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; - - // 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; - } - - 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 (conditionResult[0] || conditionResult[1]) { - if (conditionResult[0] != conditionResult[1]) { - candidate.group = conditionResult[1] ? - defTrue : defFalse; - } - - candidates.push(candidate); - } - } - else { - candidates.push(candidate); - } - - match = true; - } - } - - defaultFunc.reset(); - - 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 }; - } - } - - 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)) { - mixin = new tree.mixin.Definition("", [], mixin.rules, null, false); - mixin.originalRuleset = mixins[m].originalRuleset || mixins[m]; - } - 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(); - } - } - } - 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(', ') : "") + ")"; - } -}; - -tree.mixin.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.params = params; - this.condition = condition; - this.variadic = variadic; - this.arity = params.length; - this.rules = rules; - this._lookups = {}; - this.required = params.reduce(function (count, p) { - if (!p.name || (p.name && !p.value)) { return count + 1; } - else { return count; } - }, 0); - this.parent = tree.Ruleset.prototype; - this.frames = frames; -}; -tree.mixin.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' }; - } - } - } - } - argIndex = 0; - for (i = 0; i < params.length; i++) { - if (evaldArguments[i]) { continue; } - - 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))); - } 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; - } - } - - if (params[i].variadic && args) { - for (j = argIndex; j < argsLength; j++) { - evaldArguments[j] = args[j].value.eval(env); - } - } - 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; - - frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env))); - - rules = this.rules.slice(0); - - 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.concat(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; - - 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); - - 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; - } -}; - -})(require('../tree')); diff --git a/lib/less/tree/negative.js b/lib/less/tree/negative.js index 12dbcc63..6f3e9af8 100644 --- a/lib/less/tree/negative.js +++ b/lib/less/tree/negative.js @@ -1,9 +1,9 @@ -(function (tree) { +module.exports = function (tree) { -tree.Negative = function (node) { +var Negative = function (node) { this.value = node; }; -tree.Negative.prototype = { +Negative.prototype = { type: "Negative", accept: function (visitor) { this.value = visitor.visit(this.value); @@ -17,8 +17,8 @@ tree.Negative.prototype = { if (env.isMathOn()) { return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env); } - return new(tree.Negative)(this.value.eval(env)); + return new(Negative)(this.value.eval(env)); } }; - -})(require('../tree')); +return Negative; +}; diff --git a/lib/less/tree/operation.js b/lib/less/tree/operation.js index 8f1dd651..53dc37ba 100644 --- a/lib/less/tree/operation.js +++ b/lib/less/tree/operation.js @@ -1,11 +1,11 @@ -(function (tree) { +module.exports = function (tree) { -tree.Operation = function (op, operands, isSpaced) { +var Operation = function (op, operands, isSpaced) { this.op = op.trim(); this.operands = operands; this.isSpaced = isSpaced; }; -tree.Operation.prototype = { +Operation.prototype = { type: "Operation", accept: function (visitor) { this.operands = visitor.visit(this.operands); @@ -28,7 +28,7 @@ tree.Operation.prototype = { return a.operate(env, this.op, b); } else { - return new(tree.Operation)(this.op, [a, b], this.isSpaced); + return new(Operation)(this.op, [a, b], this.isSpaced); } }, genCSS: function (env, output) { @@ -45,6 +45,7 @@ tree.Operation.prototype = { toCSS: tree.toCSS }; +// todo move! tree.operate = function (env, op, a, b) { switch (op) { case '+': return a + b; @@ -53,5 +54,7 @@ tree.operate = function (env, op, a, b) { case '/': return a / b; } }; + +return Operation; -})(require('../tree')); +}; diff --git a/lib/less/tree/paren.js b/lib/less/tree/paren.js index e27b8d66..5c7dca86 100644 --- a/lib/less/tree/paren.js +++ b/lib/less/tree/paren.js @@ -1,10 +1,9 @@ +module.exports = function (tree) { -(function (tree) { - -tree.Paren = function (node) { +var Paren = function (node) { this.value = node; }; -tree.Paren.prototype = { +Paren.prototype = { type: "Paren", accept: function (visitor) { this.value = visitor.visit(this.value); @@ -16,8 +15,8 @@ tree.Paren.prototype = { }, toCSS: tree.toCSS, eval: function (env) { - return new(tree.Paren)(this.value.eval(env)); + return new(Paren)(this.value.eval(env)); } }; - -})(require('../tree')); +return Paren; +}; diff --git a/lib/less/tree/quoted.js b/lib/less/tree/quoted.js index 131bfad9..738737a5 100644 --- a/lib/less/tree/quoted.js +++ b/lib/less/tree/quoted.js @@ -1,13 +1,13 @@ -(function (tree) { +module.exports = function (tree) { -tree.Quoted = function (str, content, escaped, index, currentFileInfo) { +var Quoted = function (str, content, escaped, index, currentFileInfo) { this.escaped = escaped; this.value = content || ''; this.quote = str.charAt(0); this.index = index; this.currentFileInfo = currentFileInfo; }; -tree.Quoted.prototype = { +Quoted.prototype = { type: "Quoted", genCSS: function (env, output) { if (!this.escaped) { @@ -44,5 +44,5 @@ tree.Quoted.prototype = { return left < right ? -1 : 1; } }; - -})(require('../tree')); +return Quoted; +}; diff --git a/lib/less/tree/rule.js b/lib/less/tree/rule.js index 4e1ccde6..f3cbcffb 100644 --- a/lib/less/tree/rule.js +++ b/lib/less/tree/rule.js @@ -1,6 +1,6 @@ -(function (tree) { +module.exports = function (tree) { -tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) { +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.important = important ? ' ' + important.trim() : ''; @@ -11,7 +11,7 @@ tree.Rule = function (name, value, important, merge, index, currentFileInfo, inl this.variable = name.charAt && (name.charAt(0) === '@'); }; -tree.Rule.prototype = { +Rule.prototype = { type: "Rule", accept: function (visitor) { this.value = visitor.visit(this.value); @@ -50,7 +50,7 @@ tree.Rule.prototype = { index: this.index, filename: this.currentFileInfo.filename }; } - return new(tree.Rule)(name, + return new(Rule)(name, evaldValue, this.important, this.merge, @@ -70,7 +70,7 @@ tree.Rule.prototype = { } }, makeImportant: function () { - return new(tree.Rule)(this.name, + return new(Rule)(this.name, this.value, "!important", this.merge, @@ -86,5 +86,7 @@ function evalName(env, name) { } return value; } + +return Rule; -})(require('../tree')); +}; diff --git a/lib/less/tree/ruleset-call.js b/lib/less/tree/ruleset-call.js index a543c55e..e7d998c4 100644 --- a/lib/less/tree/ruleset-call.js +++ b/lib/less/tree/ruleset-call.js @@ -1,9 +1,9 @@ -(function (tree) { +module.exports = function (tree) { -tree.RulesetCall = function (variable) { +var RulesetCall = function (variable) { this.variable = variable; }; -tree.RulesetCall.prototype = { +RulesetCall.prototype = { type: "RulesetCall", accept: function (visitor) { }, @@ -12,5 +12,5 @@ tree.RulesetCall.prototype = { return detachedRuleset.callEval(env); } }; - -})(require('../tree')); +return RulesetCall; +}; diff --git a/lib/less/tree/ruleset.js b/lib/less/tree/ruleset.js index 19023004..76a8d2a7 100644 --- a/lib/less/tree/ruleset.js +++ b/lib/less/tree/ruleset.js @@ -1,12 +1,12 @@ -(function (tree) { +module.exports = function (tree) { -tree.Ruleset = function (selectors, rules, strictImports) { +var Ruleset = function (selectors, rules, strictImports) { this.selectors = selectors; this.rules = rules; this._lookups = {}; this.strictImports = strictImports; }; -tree.Ruleset.prototype = { +Ruleset.prototype = { type: "Ruleset", accept: function (visitor) { if (this.paths) { @@ -41,7 +41,7 @@ tree.Ruleset.prototype = { } var rules = this.rules ? this.rules.slice(0) : null, - ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports), + ruleset = new(Ruleset)(selectors, rules, this.strictImports), rule, subRule; ruleset.originalRuleset = this; @@ -129,7 +129,7 @@ tree.Ruleset.prototype = { 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 tree.Ruleset && rule.selectors && rule.selectors.length === 1) { + 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); @@ -174,7 +174,7 @@ tree.Ruleset.prototype = { } }, makeImportant: function() { - return new tree.Ruleset(this.selectors, this.rules.map(function (r) { + return new Ruleset(this.selectors, this.rules.map(function (r) { if (r.makeImportant) { return r.makeImportant(); } else { @@ -221,13 +221,13 @@ tree.Ruleset.prototype = { rulesets: function () { if (!this.rules) { return null; } - var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition, + 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)) { + if ((rule instanceof Ruleset) || (rule instanceof _MixinDefinition)) { filtRules.push(rule); } } @@ -551,4 +551,5 @@ tree.Ruleset.prototype = { } } }; -})(require('../tree')); +return Ruleset; +}; diff --git a/lib/less/tree/selector.js b/lib/less/tree/selector.js index f38934e0..ffcc8e83 100644 --- a/lib/less/tree/selector.js +++ b/lib/less/tree/selector.js @@ -1,6 +1,6 @@ -(function (tree) { +module.exports = function (tree) { -tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { +var Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) { this.elements = elements; this.extendList = extendList; this.condition = condition; @@ -10,7 +10,7 @@ tree.Selector = function (elements, extendList, condition, index, currentFileInf this.evaldCondition = true; } }; -tree.Selector.prototype = { +Selector.prototype = { type: "Selector", accept: function (visitor) { if (this.elements) { @@ -25,7 +25,7 @@ tree.Selector.prototype = { }, createDerived: function(elements, extendList, evaldCondition) { evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition; - var newSelector = new(tree.Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); + var newSelector = new(Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced); newSelector.evaldCondition = evaldCondition; newSelector.mediaEmpty = this.mediaEmpty; return newSelector; @@ -125,5 +125,5 @@ tree.Selector.prototype = { return this.evaldCondition; } }; - -})(require('../tree')); +return Selector; +}; diff --git a/lib/less/tree/unicode-descriptor.js b/lib/less/tree/unicode-descriptor.js index 7bf98ea5..674ac983 100644 --- a/lib/less/tree/unicode-descriptor.js +++ b/lib/less/tree/unicode-descriptor.js @@ -1,9 +1,9 @@ -(function (tree) { +module.exports = function (tree) { -tree.UnicodeDescriptor = function (value) { +var UnicodeDescriptor = function (value) { this.value = value; }; -tree.UnicodeDescriptor.prototype = { +UnicodeDescriptor.prototype = { type: "UnicodeDescriptor", genCSS: function (env, output) { output.add(this.value); @@ -11,5 +11,5 @@ tree.UnicodeDescriptor.prototype = { toCSS: tree.toCSS, eval: function () { return this; } }; - -})(require('../tree')); +return UnicodeDescriptor; +}; diff --git a/lib/less/tree/unit-conversions.js b/lib/less/tree/unit-conversions.js new file mode 100644 index 00000000..628f13df --- /dev/null +++ b/lib/less/tree/unit-conversions.js @@ -0,0 +1,21 @@ +module.exports = { + length: { + 'm': 1, + 'cm': 0.01, + 'mm': 0.001, + 'in': 0.0254, + 'px': 0.0254 / 96, + 'pt': 0.0254 / 72, + 'pc': 0.0254 / 72 * 12 + }, + duration: { + 's': 1, + 'ms': 0.001 + }, + angle: { + 'rad': 1/(2*Math.PI), + 'deg': 1/360, + 'grad': 1/400, + 'turn': 1 + } +}; \ No newline at end of file diff --git a/lib/less/tree/unit.js b/lib/less/tree/unit.js new file mode 100644 index 00000000..c25a7173 --- /dev/null +++ b/lib/less/tree/unit.js @@ -0,0 +1,137 @@ +module.exports = function(tree, unitConversions) { + +var Unit = function (numerator, denominator, backupUnit) { + this.numerator = numerator ? numerator.slice(0).sort() : []; + this.denominator = denominator ? denominator.slice(0).sort() : []; + 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; + }, + + 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.denominator.length; i++) { + this.denominator[i] = callback(this.denominator[i], true); + } + }, + + usedUnits: function() { + var group, result = {}, mapUnit; + + mapUnit = function (atomicUnit) { + /*jshint loopfunc:true */ + if (group.hasOwnProperty(atomicUnit) && !result[groupName]) { + result[groupName] = atomicUnit; + } + + return atomicUnit; + }; + + for (var groupName in unitConversions) { + if (unitConversions.hasOwnProperty(groupName)) { + group = unitConversions[groupName]; + + this.map(mapUnit); + } + } + + return result; + }, + + 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.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]; + + 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(); + } +}; +return Unit; +}; \ No newline at end of file diff --git a/lib/less/tree/url.js b/lib/less/tree/url.js index 90562685..28e37d2a 100644 --- a/lib/less/tree/url.js +++ b/lib/less/tree/url.js @@ -1,11 +1,11 @@ -(function (tree) { +module.exports = function (tree) { -tree.URL = function (val, currentFileInfo, isEvald) { +var URL = function (val, currentFileInfo, isEvald) { this.value = val; this.currentFileInfo = currentFileInfo; this.isEvald = isEvald; }; -tree.URL.prototype = { +URL.prototype = { type: "Url", accept: function (visitor) { this.value = visitor.visit(this.value); @@ -46,8 +46,8 @@ tree.URL.prototype = { } } - return new(tree.URL)(val, this.currentFileInfo, true); + return new(URL)(val, this.currentFileInfo, true); } }; - -})(require('../tree')); +return URL; +}; diff --git a/lib/less/tree/value.js b/lib/less/tree/value.js index f95e7b58..ea07367b 100644 --- a/lib/less/tree/value.js +++ b/lib/less/tree/value.js @@ -1,9 +1,9 @@ -(function (tree) { +module.exports = function (tree) { -tree.Value = function (value) { +var Value = function (value) { this.value = value; }; -tree.Value.prototype = { +Value.prototype = { type: "Value", accept: function (visitor) { if (this.value) { @@ -14,7 +14,7 @@ tree.Value.prototype = { if (this.value.length === 1) { return this.value[0].eval(env); } else { - return new(tree.Value)(this.value.map(function (v) { + return new(Value)(this.value.map(function (v) { return v.eval(env); })); } @@ -30,5 +30,5 @@ tree.Value.prototype = { }, toCSS: tree.toCSS }; - -})(require('../tree')); +return Value; +}; diff --git a/lib/less/tree/variable.js b/lib/less/tree/variable.js index 90d86271..1e152e4c 100644 --- a/lib/less/tree/variable.js +++ b/lib/less/tree/variable.js @@ -1,17 +1,17 @@ -(function (tree) { +module.exports = function (tree) { -tree.Variable = function (name, index, currentFileInfo) { +var Variable = function (name, index, currentFileInfo) { this.name = name; this.index = index; this.currentFileInfo = currentFileInfo || {}; }; -tree.Variable.prototype = { +Variable.prototype = { type: "Variable", eval: function (env) { var variable, name = this.name; if (name.indexOf('@@') === 0) { - name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value; + name = '@' + new(Variable)(name.slice(1)).eval(env).value; } if (this.evaluating) { @@ -40,5 +40,5 @@ tree.Variable.prototype = { } } }; - -})(require('../tree')); +return Variable; +}; diff --git a/lib/less/visitor.js b/lib/less/visitor.js index 75b9975a..d4696e72 100644 --- a/lib/less/visitor.js +++ b/lib/less/visitor.js @@ -1,4 +1,4 @@ -(function (tree) { +module.exports = function (tree) { var _visitArgs = { visitDeeper: true }, _hasIndexed = false; @@ -143,4 +143,4 @@ } }; -})(require('./tree')); \ No newline at end of file +}; diff --git a/package.json b/package.json index 8e91bbfa..95ccd88d 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,8 @@ "http-server": "~0.6.1", "matchdep": "~0.3.0", "time-grunt": "~0.2.9", - "grunt-contrib-copy": "~0.4.1" + "browserify": "~3.30.2", + "grunt-browserify": "~1.3.1" }, "keywords": [ "compile less",