add browserify (not yet working) and refactor tree nodes to not be dependent on their parent (currently breaks browser build)

This commit is contained in:
Luke Page
2014-02-24 21:22:52 +00:00
parent 208b32aac7
commit 08bd23dc2a
54 changed files with 933 additions and 893 deletions

View File

@@ -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'));
};

View File

@@ -1,4 +0,0 @@
// base64 encoder implementation for node
exports.encodeBase64 = function(str) {
return new Buffer(str).toString('base64');
};

View File

@@ -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'));
};

View File

@@ -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) {

View File

@@ -1,4 +1,4 @@
(function (tree) {
module.exports = function (tree) {
/*jshint loopfunc:true */
tree.extendFinderVisitor = function() {
@@ -413,4 +413,4 @@
}
};
})(require('./tree'));
};

View File

@@ -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;
};

View File

@@ -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'));
};

View File

@@ -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;

View File

@@ -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'));
};

View File

@@ -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;

View File

@@ -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;
};

View File

@@ -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'));
};

View File

@@ -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'));
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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'));

View File

@@ -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;
};

View File

@@ -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'));

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

152
lib/less/tree/mixin-call.js Normal file
View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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'));

View File

@@ -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;
};

View File

@@ -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'));
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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'));
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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
}
};

137
lib/less/tree/unit.js Normal file
View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -1,4 +1,4 @@
(function (tree) {
module.exports = function (tree) {
var _visitArgs = { visitDeeper: true },
_hasIndexed = false;
@@ -143,4 +143,4 @@
}
};
})(require('./tree'));
};