mirror of
https://github.com/less/less.js.git
synced 2026-05-01 03:00:22 -04:00
untangle the mess of dependencies and remove all circular dependencies. Remove un-necessary dependency injection.
This commit is contained in:
@@ -309,7 +309,7 @@ function loadStyles(modifyVars) {
|
||||
for (var i = 0; i < styles.length; i++) {
|
||||
style = styles[i];
|
||||
if (style.type.match(typePattern)) {
|
||||
var env = new less.tree.parseEnv(options),
|
||||
var env = new less.contexts.parseEnv(options),
|
||||
lessText = style.innerHTML || '';
|
||||
env.filename = document.location.href.replace(/#.*$/, '');
|
||||
|
||||
@@ -340,7 +340,7 @@ function loadStyles(modifyVars) {
|
||||
|
||||
function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) {
|
||||
|
||||
var env = new less.tree.parseEnv(options);
|
||||
var env = new less.contexts.parseEnv(options);
|
||||
env.mime = sheet.type;
|
||||
|
||||
if (modifyVars || options.globalVars) {
|
||||
|
||||
267
lib/less/env.js
267
lib/less/env.js
@@ -1,138 +1,139 @@
|
||||
module.exports = function (tree) {
|
||||
var contexts = {};
|
||||
module.exports = contexts;
|
||||
|
||||
var parseCopyProperties = [
|
||||
'paths', // option - unmodified - paths to search for imports on
|
||||
'files', // list of files that have been imported, used for import-once
|
||||
'contents', // map - filename to contents of all the files
|
||||
'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore
|
||||
'relativeUrls', // option - whether to adjust URL's to be relative
|
||||
'rootpath', // option - rootpath to append to URL's
|
||||
'strictImports', // option -
|
||||
'insecure', // option - whether to allow imports from insecure ssl hosts
|
||||
'dumpLineNumbers', // option - whether to dump line numbers
|
||||
'compress', // option - whether to compress
|
||||
'processImports', // option - whether to process imports. if false then imports will not be imported
|
||||
'syncImport', // option - whether to import synchronously
|
||||
'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true
|
||||
'chunkInput', // option - whether to chunk input. more performant but causes parse issues.
|
||||
'mime', // browser only - mime type for sheet import
|
||||
'useFileCache', // browser only - whether to use the per file session cache
|
||||
'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc.
|
||||
var copyFromOriginal = function copyFromOriginal(original, destination, propertiesToCopy) {
|
||||
if (!original) { return; }
|
||||
|
||||
for(var i = 0; i < propertiesToCopy.length; i++) {
|
||||
if (original.hasOwnProperty(propertiesToCopy[i])) {
|
||||
destination[propertiesToCopy[i]] = original[propertiesToCopy[i]];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var parseCopyProperties = [
|
||||
'paths', // option - unmodified - paths to search for imports on
|
||||
'files', // list of files that have been imported, used for import-once
|
||||
'contents', // map - filename to contents of all the files
|
||||
'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore
|
||||
'relativeUrls', // option - whether to adjust URL's to be relative
|
||||
'rootpath', // option - rootpath to append to URL's
|
||||
'strictImports', // option -
|
||||
'insecure', // option - whether to allow imports from insecure ssl hosts
|
||||
'dumpLineNumbers', // option - whether to dump line numbers
|
||||
'compress', // option - whether to compress
|
||||
'processImports', // option - whether to process imports. if false then imports will not be imported
|
||||
'syncImport', // option - whether to import synchronously
|
||||
'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true
|
||||
'chunkInput', // option - whether to chunk input. more performant but causes parse issues.
|
||||
'mime', // browser only - mime type for sheet import
|
||||
'useFileCache', // browser only - whether to use the per file session cache
|
||||
'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc.
|
||||
];
|
||||
|
||||
//currentFileInfo = {
|
||||
// 'relativeUrls' - option - whether to adjust URL's to be relative
|
||||
// 'filename' - full resolved filename of current file
|
||||
// 'rootpath' - path to append to normal URLs for this node
|
||||
// 'currentDirectory' - path to the current file, absolute
|
||||
// 'rootFilename' - filename of the base file
|
||||
// 'entryPath' - absolute path to the entry file
|
||||
// 'reference' - whether the file should not be output and only output parts that are referenced
|
||||
|
||||
contexts.parseEnv = function(options) {
|
||||
copyFromOriginal(options, this, parseCopyProperties);
|
||||
|
||||
if (!this.contents) { this.contents = {}; }
|
||||
if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; }
|
||||
if (!this.files) { this.files = {}; }
|
||||
|
||||
if (typeof this.paths === "string") { this.paths = [this.paths]; }
|
||||
|
||||
if (!this.currentFileInfo) {
|
||||
var filename = (options && options.filename) || "input";
|
||||
var entryPath = filename.replace(/[^\/\\]*$/, "");
|
||||
if (options) {
|
||||
options.filename = null;
|
||||
}
|
||||
this.currentFileInfo = {
|
||||
filename: filename,
|
||||
relativeUrls: this.relativeUrls,
|
||||
rootpath: (options && options.rootpath) || "",
|
||||
currentDirectory: entryPath,
|
||||
entryPath: entryPath,
|
||||
rootFilename: filename
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
var evalCopyProperties = [
|
||||
'silent', // whether to swallow errors and warnings
|
||||
'verbose', // whether to log more activity
|
||||
'compress', // whether to compress
|
||||
'yuicompress', // whether to compress with the outside tool yui compressor
|
||||
'ieCompat', // whether to enforce IE compatibility (IE8 data-uri)
|
||||
'strictMath', // whether math has to be within parenthesis
|
||||
'strictUnits', // whether units need to evaluate correctly
|
||||
'cleancss', // whether to compress with clean-css
|
||||
'sourceMap', // whether to output a source map
|
||||
'importMultiple', // whether we are currently importing multiple copies
|
||||
'urlArgs' // whether to add args into url tokens
|
||||
];
|
||||
|
||||
//currentFileInfo = {
|
||||
// 'relativeUrls' - option - whether to adjust URL's to be relative
|
||||
// 'filename' - full resolved filename of current file
|
||||
// 'rootpath' - path to append to normal URLs for this node
|
||||
// 'currentDirectory' - path to the current file, absolute
|
||||
// 'rootFilename' - filename of the base file
|
||||
// 'entryPath' - absolute path to the entry file
|
||||
// 'reference' - whether the file should not be output and only output parts that are referenced
|
||||
|
||||
tree.parseEnv = function(options) {
|
||||
copyFromOriginal(options, this, parseCopyProperties);
|
||||
|
||||
if (!this.contents) { this.contents = {}; }
|
||||
if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; }
|
||||
if (!this.files) { this.files = {}; }
|
||||
|
||||
if (typeof this.paths === "string") { this.paths = [this.paths]; }
|
||||
|
||||
if (!this.currentFileInfo) {
|
||||
var filename = (options && options.filename) || "input";
|
||||
var entryPath = filename.replace(/[^\/\\]*$/, "");
|
||||
if (options) {
|
||||
options.filename = null;
|
||||
}
|
||||
this.currentFileInfo = {
|
||||
filename: filename,
|
||||
relativeUrls: this.relativeUrls,
|
||||
rootpath: (options && options.rootpath) || "",
|
||||
currentDirectory: entryPath,
|
||||
entryPath: entryPath,
|
||||
rootFilename: filename
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
var evalCopyProperties = [
|
||||
'silent', // whether to swallow errors and warnings
|
||||
'verbose', // whether to log more activity
|
||||
'compress', // whether to compress
|
||||
'yuicompress', // whether to compress with the outside tool yui compressor
|
||||
'ieCompat', // whether to enforce IE compatibility (IE8 data-uri)
|
||||
'strictMath', // whether math has to be within parenthesis
|
||||
'strictUnits', // whether units need to evaluate correctly
|
||||
'cleancss', // whether to compress with clean-css
|
||||
'sourceMap', // whether to output a source map
|
||||
'importMultiple', // whether we are currently importing multiple copies
|
||||
'urlArgs' // whether to add args into url tokens
|
||||
];
|
||||
|
||||
tree.evalEnv = function(options, frames) {
|
||||
copyFromOriginal(options, this, evalCopyProperties);
|
||||
|
||||
this.frames = frames || [];
|
||||
};
|
||||
|
||||
tree.evalEnv.prototype.inParenthesis = function () {
|
||||
if (!this.parensStack) {
|
||||
this.parensStack = [];
|
||||
}
|
||||
this.parensStack.push(true);
|
||||
};
|
||||
|
||||
tree.evalEnv.prototype.outOfParenthesis = function () {
|
||||
this.parensStack.pop();
|
||||
};
|
||||
|
||||
tree.evalEnv.prototype.isMathOn = function () {
|
||||
return this.strictMath ? (this.parensStack && this.parensStack.length) : true;
|
||||
};
|
||||
|
||||
tree.evalEnv.prototype.isPathRelative = function (path) {
|
||||
return !/^(?:[a-z-]+:|\/)/.test(path);
|
||||
};
|
||||
|
||||
tree.evalEnv.prototype.normalizePath = function( path ) {
|
||||
var
|
||||
segments = path.split("/").reverse(),
|
||||
segment;
|
||||
|
||||
path = [];
|
||||
while (segments.length !== 0 ) {
|
||||
segment = segments.pop();
|
||||
switch( segment ) {
|
||||
case ".":
|
||||
break;
|
||||
case "..":
|
||||
if ((path.length === 0) || (path[path.length - 1] === "..")) {
|
||||
path.push( segment );
|
||||
} else {
|
||||
path.pop();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
path.push( segment );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return path.join("/");
|
||||
};
|
||||
|
||||
//todo - do the same for the toCSS env
|
||||
//tree.toCSSEnv = function (options) {
|
||||
//};
|
||||
|
||||
var copyFromOriginal = function(original, destination, propertiesToCopy) {
|
||||
if (!original) { return; }
|
||||
|
||||
for(var i = 0; i < propertiesToCopy.length; i++) {
|
||||
if (original.hasOwnProperty(propertiesToCopy[i])) {
|
||||
destination[propertiesToCopy[i]] = original[propertiesToCopy[i]];
|
||||
}
|
||||
}
|
||||
};
|
||||
contexts.evalEnv = function(options, frames) {
|
||||
copyFromOriginal(options, this, evalCopyProperties);
|
||||
|
||||
this.frames = frames || [];
|
||||
};
|
||||
|
||||
contexts.evalEnv.prototype.inParenthesis = function () {
|
||||
if (!this.parensStack) {
|
||||
this.parensStack = [];
|
||||
}
|
||||
this.parensStack.push(true);
|
||||
};
|
||||
|
||||
contexts.evalEnv.prototype.outOfParenthesis = function () {
|
||||
this.parensStack.pop();
|
||||
};
|
||||
|
||||
contexts.evalEnv.prototype.isMathOn = function () {
|
||||
return this.strictMath ? (this.parensStack && this.parensStack.length) : true;
|
||||
};
|
||||
|
||||
contexts.evalEnv.prototype.isPathRelative = function (path) {
|
||||
return !/^(?:[a-z-]+:|\/)/.test(path);
|
||||
};
|
||||
|
||||
contexts.evalEnv.prototype.normalizePath = function( path ) {
|
||||
var
|
||||
segments = path.split("/").reverse(),
|
||||
segment;
|
||||
|
||||
path = [];
|
||||
while (segments.length !== 0 ) {
|
||||
segment = segments.pop();
|
||||
switch( segment ) {
|
||||
case ".":
|
||||
break;
|
||||
case "..":
|
||||
if ((path.length === 0) || (path[path.length - 1] === "..")) {
|
||||
path.push( segment );
|
||||
} else {
|
||||
path.pop();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
path.push( segment );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return path.join("/");
|
||||
};
|
||||
|
||||
//todo - do the same for the toCSS env
|
||||
//tree.toCSSEnv = function (options) {
|
||||
//};
|
||||
|
||||
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
module.exports = function(functions, tree) {
|
||||
var defaultFunc = {
|
||||
eval: function () {
|
||||
var v = this.value_, e = this.error_;
|
||||
if (e) {
|
||||
throw e;
|
||||
}
|
||||
if (v != null) {
|
||||
return v ? tree.True : tree.False;
|
||||
}
|
||||
},
|
||||
value: function (v) {
|
||||
this.value_ = v;
|
||||
},
|
||||
error: function (e) {
|
||||
this.error_ = e;
|
||||
},
|
||||
reset: function () {
|
||||
this.value_ = this.error_ = null;
|
||||
}
|
||||
};
|
||||
var Keyword = require("../tree/keyword.js");
|
||||
|
||||
functions.functionRegistry.add("default", defaultFunc.eval.bind(defaultFunc));
|
||||
tree.defaultFunc = defaultFunc;
|
||||
var defaultFunc = {
|
||||
eval: function () {
|
||||
var v = this.value_, e = this.error_;
|
||||
if (e) {
|
||||
throw e;
|
||||
}
|
||||
if (v != null) {
|
||||
return v ? Keyword.True : Keyword.False;
|
||||
}
|
||||
},
|
||||
value: function (v) {
|
||||
this.value_ = v;
|
||||
},
|
||||
error: function (e) {
|
||||
this.error_ = e;
|
||||
},
|
||||
reset: function () {
|
||||
this.value_ = this.error_ = null;
|
||||
}
|
||||
};
|
||||
module.exports = defaultFunc;
|
||||
|
||||
@@ -7,7 +7,11 @@ module.exports = function(less, tree) {
|
||||
require("./color.js")(functions, tree);
|
||||
require("./color-blending.js")(functions, tree);
|
||||
require("./data-uri.js")(functions, tree, less);
|
||||
require("./default.js")(functions, tree);
|
||||
|
||||
var defaultFunc = require("./default.js");
|
||||
functions.functionRegistry.add("default", defaultFunc.eval.bind(defaultFunc));
|
||||
tree.defaultFunc = defaultFunc;
|
||||
|
||||
require("./math.js")(functions, tree);
|
||||
require("./number.js")(functions, tree);
|
||||
require("./string.js")(functions, tree);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
var Keyword = require("../tree/keyword.js");
|
||||
module.exports = function(functions, tree) {
|
||||
var isa = function (n, Type) {
|
||||
return (n instanceof Type) ? tree.True : tree.False;
|
||||
return (n instanceof Type) ? Keyword.True : Keyword.False;
|
||||
},
|
||||
isunit = function (n, unit) {
|
||||
return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False;
|
||||
return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? Keyword.True : Keyword.False;
|
||||
};
|
||||
functions.functionRegistry.addMultiple({
|
||||
iscolor: function (n) {
|
||||
|
||||
@@ -5,11 +5,11 @@ var less = {
|
||||
}
|
||||
};
|
||||
|
||||
less.tree = (require('./tree'))(less, less.data);
|
||||
less.tree = require('./tree');
|
||||
less.visitor = require('./visitor/index.js')(less, less.tree);
|
||||
less.Parser = (require('./parser'))(less, less.tree, less.visitor);
|
||||
less.functions = (require('./functions/index.js'))(less, less.tree);
|
||||
require('./env')(less.tree);
|
||||
less.contexts = require("./env.js");
|
||||
|
||||
less.tree.sourceMapOutput = require('./source-map-output.js')(less);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
var chunker = require('./chunker.js');
|
||||
var LessError = require('./less-error.js');
|
||||
var contexts = require("./env.js");
|
||||
|
||||
module.exports = function(less, tree, visitor) {
|
||||
//
|
||||
@@ -51,8 +52,8 @@ var Parser = function Parser(env) {
|
||||
|
||||
// Top parser on an import tree must be sure there is one "env"
|
||||
// which will then be passed around by reference.
|
||||
if (!(env instanceof tree.parseEnv)) {
|
||||
env = new tree.parseEnv(env);
|
||||
if (!(env instanceof contexts.parseEnv)) {
|
||||
env = new contexts.parseEnv(env);
|
||||
}
|
||||
|
||||
var imports = this.imports = {
|
||||
@@ -109,7 +110,7 @@ var Parser = function Parser(env) {
|
||||
}
|
||||
newFileInfo.filename = resolvedFilename;
|
||||
|
||||
var newEnv = new tree.parseEnv(env);
|
||||
var newEnv = new contexts.parseEnv(env);
|
||||
|
||||
newEnv.currentFileInfo = newFileInfo;
|
||||
newEnv.processImports = false;
|
||||
@@ -451,7 +452,7 @@ var Parser = function Parser(env) {
|
||||
options = options || {};
|
||||
var evaldRoot,
|
||||
css,
|
||||
evalEnv = new tree.evalEnv(options);
|
||||
evalEnv = new contexts.evalEnv(options);
|
||||
|
||||
//
|
||||
// Allows setting variables with a hash, so:
|
||||
|
||||
174
lib/less/tree.js
174
lib/less/tree.js
@@ -1,144 +1,40 @@
|
||||
module.exports = function (less, data) {
|
||||
|
||||
var tree = {};
|
||||
|
||||
tree.debugInfo = function(env, ctx, lineSeperator) {
|
||||
var result="";
|
||||
if (env.dumpLineNumbers && !env.compress) {
|
||||
switch(env.dumpLineNumbers) {
|
||||
case 'comments':
|
||||
result = tree.debugInfo.asComment(ctx);
|
||||
break;
|
||||
case 'mediaquery':
|
||||
result = tree.debugInfo.asMediaQuery(ctx);
|
||||
break;
|
||||
case 'all':
|
||||
result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
tree.debugInfo.asComment = function(ctx) {
|
||||
return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n';
|
||||
};
|
||||
|
||||
tree.debugInfo.asMediaQuery = function(ctx) {
|
||||
return '@media -sass-debug-info{filename{font-family:' +
|
||||
('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) {
|
||||
if (a == '\\') {
|
||||
a = '\/';
|
||||
}
|
||||
return '\\' + a;
|
||||
}) +
|
||||
'}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n';
|
||||
};
|
||||
|
||||
tree.find = function (obj, fun) {
|
||||
for (var i = 0, r; i < obj.length; i++) {
|
||||
r = fun.call(obj, obj[i]);
|
||||
if (r) { return r; }
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
tree.jsify = function (obj) {
|
||||
if (Array.isArray(obj.value) && (obj.value.length > 1)) {
|
||||
return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']';
|
||||
} else {
|
||||
return obj.toCSS();
|
||||
}
|
||||
};
|
||||
|
||||
tree.toCSS = function (env) {
|
||||
var strs = [];
|
||||
this.genCSS(env, {
|
||||
add: function(chunk, fileInfo, index) {
|
||||
strs.push(chunk);
|
||||
},
|
||||
isEmpty: function () {
|
||||
return strs.length === 0;
|
||||
}
|
||||
});
|
||||
return strs.join('');
|
||||
};
|
||||
|
||||
tree.outputRuleset = function (env, output, rules) {
|
||||
var ruleCnt = rules.length, i;
|
||||
env.tabLevel = (env.tabLevel | 0) + 1;
|
||||
|
||||
// Compressed
|
||||
if (env.compress) {
|
||||
output.add('{');
|
||||
for (i = 0; i < ruleCnt; i++) {
|
||||
rules[i].genCSS(env, output);
|
||||
}
|
||||
output.add('}');
|
||||
env.tabLevel--;
|
||||
return;
|
||||
}
|
||||
|
||||
// Non-compressed
|
||||
var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " ";
|
||||
if (!ruleCnt) {
|
||||
output.add(" {" + tabSetStr + '}');
|
||||
} else {
|
||||
output.add(" {" + tabRuleStr);
|
||||
rules[0].genCSS(env, output);
|
||||
for (i = 1; i < ruleCnt; i++) {
|
||||
output.add(tabRuleStr);
|
||||
rules[i].genCSS(env, output);
|
||||
}
|
||||
output.add(tabSetStr + '}');
|
||||
}
|
||||
|
||||
env.tabLevel--;
|
||||
};
|
||||
|
||||
tree.Alpha = require('./tree/alpha')(tree);
|
||||
tree.Color = require('./tree/color')(data, tree);
|
||||
tree.Directive = require('./tree/directive')(tree);
|
||||
tree.DetachedRuleset = require('./tree/detached-ruleset')(tree);
|
||||
tree.Operation = require('./tree/operation')(tree);
|
||||
tree.Dimension = require('./tree/dimension')(tree, require('./tree/unit-conversions')); //todo move conversions
|
||||
tree.Unit = require('./tree/unit')(tree, require('./tree/unit-conversions'));
|
||||
tree.Keyword = require('./tree/keyword')(tree);
|
||||
tree.Variable = require('./tree/variable')(tree);
|
||||
tree.Ruleset = require('./tree/ruleset')(tree);
|
||||
tree.Element = require('./tree/element')(tree);
|
||||
tree.Attribute = require('./tree/attribute')(tree);
|
||||
tree.Combinator = require('./tree/combinator')(tree);
|
||||
tree.Selector = require('./tree/selector')(tree);
|
||||
tree.Quoted = require('./tree/quoted')(tree);
|
||||
tree.Expression = require('./tree/expression')(tree);
|
||||
tree.Rule = require('./tree/rule')(tree);
|
||||
tree.Call = require('./tree/call')(tree, less);
|
||||
tree.URL = require('./tree/url')(tree);
|
||||
tree.Import = require('./tree/import')(tree);
|
||||
tree.Alpha = require('./tree/alpha');
|
||||
tree.Color = require('./tree/color');
|
||||
tree.Directive = require('./tree/directive');
|
||||
tree.DetachedRuleset = require('./tree/detached-ruleset');
|
||||
tree.Operation = require('./tree/operation');
|
||||
tree.Dimension = require('./tree/dimension');
|
||||
tree.Unit = require('./tree/unit');
|
||||
tree.Keyword = require('./tree/keyword');
|
||||
tree.Variable = require('./tree/variable');
|
||||
tree.Ruleset = require('./tree/ruleset');
|
||||
tree.Element = require('./tree/element');
|
||||
tree.Attribute = require('./tree/attribute');
|
||||
tree.Combinator = require('./tree/combinator');
|
||||
tree.Selector = require('./tree/selector');
|
||||
tree.Quoted = require('./tree/quoted');
|
||||
tree.Expression = require('./tree/expression');
|
||||
tree.Rule = require('./tree/rule');
|
||||
tree.Call = require('./tree/call');
|
||||
tree.URL = require('./tree/url');
|
||||
tree.Import = require('./tree/import');
|
||||
tree.mixin = {
|
||||
Call: require('./tree/mixin-call')(tree),
|
||||
Definition: require('./tree/mixin-definition')(tree)
|
||||
Call: require('./tree/mixin-call'),
|
||||
Definition: require('./tree/mixin-definition')
|
||||
};
|
||||
tree.Comment = require('./tree/comment')(tree);
|
||||
tree.Anonymous = require('./tree/anonymous')(tree);
|
||||
tree.Value = require('./tree/value')(tree);
|
||||
tree.JavaScript = require('./tree/javascript')(tree);
|
||||
tree.Assignment = require('./tree/assignment')(tree);
|
||||
tree.Condition = require('./tree/condition')(tree);
|
||||
tree.Paren = require('./tree/paren')(tree);
|
||||
tree.Media = require('./tree/media')(tree);
|
||||
tree.UnicodeDescriptor = require('./tree/unicode-descriptor')(tree);
|
||||
tree.Negative = require('./tree/negative')(tree);
|
||||
tree.Extend = require('./tree/extend')(tree);
|
||||
tree.RulesetCall = require('./tree/ruleset-call')(tree);
|
||||
tree.Comment = require('./tree/comment');
|
||||
tree.Anonymous = require('./tree/anonymous');
|
||||
tree.Value = require('./tree/value');
|
||||
tree.JavaScript = require('./tree/javascript');
|
||||
tree.Assignment = require('./tree/assignment');
|
||||
tree.Condition = require('./tree/condition');
|
||||
tree.Paren = require('./tree/paren');
|
||||
tree.Media = require('./tree/media');
|
||||
tree.UnicodeDescriptor = require('./tree/unicode-descriptor');
|
||||
tree.Negative = require('./tree/negative');
|
||||
tree.Extend = require('./tree/extend');
|
||||
tree.RulesetCall = require('./tree/ruleset-call');
|
||||
|
||||
tree.fround = function(env, value) {
|
||||
var precision = env && env.numPrecision;
|
||||
//add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded...
|
||||
return (precision == null) ? value : Number((value + 2e-16).toFixed(precision));
|
||||
};
|
||||
|
||||
return tree;
|
||||
|
||||
};
|
||||
module.exports = tree;
|
||||
|
||||
@@ -1,29 +1,28 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js");
|
||||
|
||||
var Alpha = function (val) {
|
||||
this.value = val;
|
||||
};
|
||||
Alpha.prototype = {
|
||||
type: "Alpha",
|
||||
accept: function (visitor) {
|
||||
this.value = visitor.visit(this.value);
|
||||
},
|
||||
eval: function (env) {
|
||||
if (this.value.eval) { return new Alpha(this.value.eval(env)); }
|
||||
return this;
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
output.add("alpha(opacity=");
|
||||
Alpha.prototype = new Node();
|
||||
Alpha.prototype.type = "Alpha";
|
||||
|
||||
if (this.value.genCSS) {
|
||||
this.value.genCSS(env, output);
|
||||
} else {
|
||||
output.add(this.value);
|
||||
}
|
||||
Alpha.prototype.accept = function (visitor) {
|
||||
this.value = visitor.visit(this.value);
|
||||
};
|
||||
Alpha.prototype.eval = function (env) {
|
||||
if (this.value.eval) { return new Alpha(this.value.eval(env)); }
|
||||
return this;
|
||||
};
|
||||
Alpha.prototype.genCSS = function (env, output) {
|
||||
output.add("alpha(opacity=");
|
||||
|
||||
output.add(")");
|
||||
},
|
||||
toCSS: tree.toCSS
|
||||
};
|
||||
return Alpha;
|
||||
if (this.value.genCSS) {
|
||||
this.value.genCSS(env, output);
|
||||
} else {
|
||||
output.add(this.value);
|
||||
}
|
||||
|
||||
output.add(")");
|
||||
};
|
||||
|
||||
module.exports = Alpha;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js");
|
||||
|
||||
var Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike) {
|
||||
this.value = value;
|
||||
@@ -7,32 +7,29 @@ var Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike)
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
this.rulesetLike = (typeof rulesetLike === 'undefined')? false : rulesetLike;
|
||||
};
|
||||
Anonymous.prototype = {
|
||||
type: "Anonymous",
|
||||
eval: function () {
|
||||
return new Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike);
|
||||
},
|
||||
compare: function (x) {
|
||||
if (!x.toCSS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
var left = this.toCSS(),
|
||||
right = x.toCSS();
|
||||
|
||||
if (left === right) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return left < right ? -1 : 1;
|
||||
},
|
||||
isRulesetLike: function() {
|
||||
return this.rulesetLike;
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
output.add(this.value, this.currentFileInfo, this.index, this.mapLines);
|
||||
},
|
||||
toCSS: tree.toCSS
|
||||
Anonymous.prototype = new Node();
|
||||
Anonymous.prototype.type = "Anonymous";
|
||||
Anonymous.prototype.eval = function () {
|
||||
return new Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike);
|
||||
};
|
||||
return Anonymous;
|
||||
Anonymous.prototype.compare = function (x) {
|
||||
if (!x.toCSS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
var left = this.toCSS(),
|
||||
right = x.toCSS();
|
||||
|
||||
if (left === right) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return left < right ? -1 : 1;
|
||||
};
|
||||
Anonymous.prototype.isRulesetLike = function() {
|
||||
return this.rulesetLike;
|
||||
};
|
||||
Anonymous.prototype.genCSS = function (env, output) {
|
||||
output.add(this.value, this.currentFileInfo, this.index, this.mapLines);
|
||||
};
|
||||
module.exports = Anonymous;
|
||||
|
||||
@@ -1,29 +1,28 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js");
|
||||
|
||||
var Assignment = function (key, val) {
|
||||
this.key = key;
|
||||
this.value = val;
|
||||
};
|
||||
Assignment.prototype = {
|
||||
type: "Assignment",
|
||||
accept: function (visitor) {
|
||||
this.value = visitor.visit(this.value);
|
||||
},
|
||||
eval: function (env) {
|
||||
if (this.value.eval) {
|
||||
return new(Assignment)(this.key, this.value.eval(env));
|
||||
}
|
||||
return this;
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
output.add(this.key + '=');
|
||||
if (this.value.genCSS) {
|
||||
this.value.genCSS(env, output);
|
||||
} else {
|
||||
output.add(this.value);
|
||||
}
|
||||
},
|
||||
toCSS: tree.toCSS
|
||||
|
||||
Assignment.prototype = new Node();
|
||||
Assignment.prototype.type = "Assignment";
|
||||
Assignment.prototype.accept = function (visitor) {
|
||||
this.value = visitor.visit(this.value);
|
||||
};
|
||||
return Assignment;
|
||||
Assignment.prototype.eval = function (env) {
|
||||
if (this.value.eval) {
|
||||
return new(Assignment)(this.key, this.value.eval(env));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
Assignment.prototype.genCSS = function (env, output) {
|
||||
output.add(this.key + '=');
|
||||
if (this.value.genCSS) {
|
||||
this.value.genCSS(env, output);
|
||||
} else {
|
||||
output.add(this.value);
|
||||
}
|
||||
};
|
||||
module.exports = Assignment;
|
||||
|
||||
|
||||
@@ -1,29 +1,27 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js");
|
||||
|
||||
var Attribute = function (key, op, value) {
|
||||
this.key = key;
|
||||
this.op = op;
|
||||
this.value = value;
|
||||
};
|
||||
Attribute.prototype = {
|
||||
type: "Attribute",
|
||||
eval: function (env) {
|
||||
return new(Attribute)(this.key.eval ? this.key.eval(env) : this.key,
|
||||
this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value);
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
output.add(this.toCSS(env));
|
||||
},
|
||||
toCSS: function (env) {
|
||||
var value = this.key.toCSS ? this.key.toCSS(env) : this.key;
|
||||
Attribute.prototype = new Node();
|
||||
Attribute.prototype.type = "Attribute";
|
||||
Attribute.prototype.eval = function (env) {
|
||||
return new(Attribute)(this.key.eval ? this.key.eval(env) : this.key,
|
||||
this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value);
|
||||
};
|
||||
Attribute.prototype.genCSS = function (env, output) {
|
||||
output.add(this.toCSS(env));
|
||||
};
|
||||
Attribute.prototype.toCSS = function (env) {
|
||||
var value = this.key.toCSS ? this.key.toCSS(env) : this.key;
|
||||
|
||||
if (this.op) {
|
||||
value += this.op;
|
||||
value += (this.value.toCSS ? this.value.toCSS(env) : this.value);
|
||||
}
|
||||
|
||||
return '[' + value + ']';
|
||||
if (this.op) {
|
||||
value += this.op;
|
||||
value += (this.value.toCSS ? this.value.toCSS(env) : this.value);
|
||||
}
|
||||
|
||||
return '[' + value + ']';
|
||||
};
|
||||
return Attribute;
|
||||
};
|
||||
module.exports = Attribute;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
module.exports = function (tree, less) {
|
||||
|
||||
var Node = require("./node.js");
|
||||
//
|
||||
// A function call node.
|
||||
//
|
||||
@@ -9,61 +8,57 @@ var Call = function (name, args, index, currentFileInfo) {
|
||||
this.index = index;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
};
|
||||
Call.prototype = {
|
||||
type: "Call",
|
||||
accept: function (visitor) {
|
||||
if (this.args) {
|
||||
this.args = visitor.visitArray(this.args);
|
||||
}
|
||||
},
|
||||
//
|
||||
// When evaluating a function call,
|
||||
// we either find the function in `less.functions` [1],
|
||||
// in which case we call it, passing the evaluated arguments,
|
||||
// if this returns null or we cannot find the function, we
|
||||
// simply print it out as it appeared originally [2].
|
||||
//
|
||||
// The *functions.js* file contains the built-in functions.
|
||||
//
|
||||
// The reason why we evaluate the arguments, is in the case where
|
||||
// we try to pass a variable to a function, like: `saturate(@color)`.
|
||||
// The function should receive the value, not the variable.
|
||||
//
|
||||
eval: function (env) {
|
||||
var args = this.args.map(function (a) { return a.eval(env); }),
|
||||
result, funcCaller = new less.functions.functionCaller(this.name, env, this.currentFileInfo);
|
||||
|
||||
if (funcCaller.isValid()) { // 1.
|
||||
try {
|
||||
result = funcCaller.call(args);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
} catch (e) {
|
||||
throw { type: e.type || "Runtime",
|
||||
message: "error evaluating function `" + this.name + "`" +
|
||||
(e.message ? ': ' + e.message : ''),
|
||||
index: this.index, filename: this.currentFileInfo.filename };
|
||||
}
|
||||
}
|
||||
|
||||
return new Call(this.name, args, this.index, this.currentFileInfo);
|
||||
},
|
||||
|
||||
genCSS: function (env, output) {
|
||||
output.add(this.name + "(", this.currentFileInfo, this.index);
|
||||
|
||||
for(var i = 0; i < this.args.length; i++) {
|
||||
this.args[i].genCSS(env, output);
|
||||
if (i + 1 < this.args.length) {
|
||||
output.add(", ");
|
||||
}
|
||||
}
|
||||
|
||||
output.add(")");
|
||||
},
|
||||
|
||||
toCSS: tree.toCSS
|
||||
Call.prototype = new Node();
|
||||
Call.prototype.type = "Call";
|
||||
Call.prototype.accept = function (visitor) {
|
||||
if (this.args) {
|
||||
this.args = visitor.visitArray(this.args);
|
||||
}
|
||||
};
|
||||
return Call;
|
||||
//
|
||||
// When evaluating a function call,
|
||||
// we either find the function in `less.functions` [1],
|
||||
// in which case we call it, passing the evaluated arguments,
|
||||
// if this returns null or we cannot find the function, we
|
||||
// simply print it out as it appeared originally [2].
|
||||
//
|
||||
// The *functions.js* file contains the built-in functions.
|
||||
//
|
||||
// The reason why we evaluate the arguments, is in the case where
|
||||
// we try to pass a variable to a function, like: `saturate(@color)`.
|
||||
// The function should receive the value, not the variable.
|
||||
//
|
||||
Call.prototype.eval = function (env) {
|
||||
var args = this.args.map(function (a) { return a.eval(env); }),
|
||||
FunctionCaller = require("../non-node-index.js").functions.functionCaller, //TODO! Move out
|
||||
result, funcCaller = new FunctionCaller(this.name, env, this.currentFileInfo);
|
||||
|
||||
if (funcCaller.isValid()) { // 1.
|
||||
try {
|
||||
result = funcCaller.call(args);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
} catch (e) {
|
||||
throw { type: e.type || "Runtime",
|
||||
message: "error evaluating function `" + this.name + "`" +
|
||||
(e.message ? ': ' + e.message : ''),
|
||||
index: this.index, filename: this.currentFileInfo.filename };
|
||||
}
|
||||
}
|
||||
|
||||
return new Call(this.name, args, this.index, this.currentFileInfo);
|
||||
};
|
||||
Call.prototype.genCSS = function (env, output) {
|
||||
output.add(this.name + "(", this.currentFileInfo, this.index);
|
||||
|
||||
for(var i = 0; i < this.args.length; i++) {
|
||||
this.args[i].genCSS(env, output);
|
||||
if (i + 1 < this.args.length) {
|
||||
output.add(", ");
|
||||
}
|
||||
}
|
||||
|
||||
output.add(")");
|
||||
};
|
||||
module.exports = Call;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
module.exports = function (data, tree) {
|
||||
var Node = require("./node.js"),
|
||||
colors = require("../data/colors.js");
|
||||
|
||||
//
|
||||
// RGB Colors - #ff0014, #eee
|
||||
//
|
||||
@@ -23,150 +25,157 @@ var Color = function (rgb, a) {
|
||||
this.alpha = typeof(a) === 'number' ? a : 1;
|
||||
};
|
||||
|
||||
Color.prototype = {
|
||||
type: "Color",
|
||||
eval: function () { return this; },
|
||||
luma: function () {
|
||||
var r = this.rgb[0] / 255,
|
||||
g = this.rgb[1] / 255,
|
||||
b = this.rgb[2] / 255;
|
||||
Color.prototype = new Node();
|
||||
Color.prototype.type = "Color";
|
||||
|
||||
r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4);
|
||||
g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4);
|
||||
b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4);
|
||||
function clamp(v, max) {
|
||||
return Math.min(Math.max(v, 0), max);
|
||||
}
|
||||
|
||||
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||
},
|
||||
function toHex(v) {
|
||||
return '#' + v.map(function (c) {
|
||||
c = clamp(Math.round(c), 255);
|
||||
return (c < 16 ? '0' : '') + c.toString(16);
|
||||
}).join('');
|
||||
}
|
||||
|
||||
genCSS: function (env, output) {
|
||||
output.add(this.toCSS(env));
|
||||
},
|
||||
toCSS: function (env, doNotCompress) {
|
||||
var compress = env && env.compress && !doNotCompress, color, alpha;
|
||||
Color.prototype.luma = function () {
|
||||
var r = this.rgb[0] / 255,
|
||||
g = this.rgb[1] / 255,
|
||||
b = this.rgb[2] / 255;
|
||||
|
||||
// `keyword` is set if this color was originally
|
||||
// converted from a named color string so we need
|
||||
// to respect this and try to output named color too.
|
||||
if (this.keyword) {
|
||||
return this.keyword;
|
||||
}
|
||||
r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4);
|
||||
g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4);
|
||||
b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4);
|
||||
|
||||
// If we have some transparency, the only way to represent it
|
||||
// is via `rgba`. Otherwise, we use the hex representation,
|
||||
// which has better compatibility with older browsers.
|
||||
// Values are capped between `0` and `255`, rounded and zero-padded.
|
||||
alpha = tree.fround(env, this.alpha);
|
||||
if (alpha < 1) {
|
||||
return "rgba(" + this.rgb.map(function (c) {
|
||||
return clamp(Math.round(c), 255);
|
||||
}).concat(clamp(alpha, 1))
|
||||
.join(',' + (compress ? '' : ' ')) + ")";
|
||||
}
|
||||
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||
};
|
||||
Color.prototype.genCSS = function (env, output) {
|
||||
output.add(this.toCSS(env));
|
||||
};
|
||||
Color.prototype.toCSS = function (env, doNotCompress) {
|
||||
var compress = env && env.compress && !doNotCompress, color, alpha;
|
||||
|
||||
color = this.toRGB();
|
||||
|
||||
if (compress) {
|
||||
var splitcolor = color.split('');
|
||||
|
||||
// Convert color to short format
|
||||
if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) {
|
||||
color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[5];
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
},
|
||||
|
||||
//
|
||||
// Operations have to be done per-channel, if not,
|
||||
// channels will spill onto each other. Once we have
|
||||
// our result, in the form of an integer triplet,
|
||||
// we create a new Color node to hold the result.
|
||||
//
|
||||
operate: function (env, op, other) {
|
||||
var rgb = [];
|
||||
var alpha = this.alpha * (1 - other.alpha) + other.alpha;
|
||||
for (var c = 0; c < 3; c++) {
|
||||
rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]);
|
||||
}
|
||||
return new(Color)(rgb, alpha);
|
||||
},
|
||||
|
||||
toRGB: function () {
|
||||
return toHex(this.rgb);
|
||||
},
|
||||
|
||||
toHSL: function () {
|
||||
var r = this.rgb[0] / 255,
|
||||
g = this.rgb[1] / 255,
|
||||
b = this.rgb[2] / 255,
|
||||
a = this.alpha;
|
||||
|
||||
var max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||||
var h, s, l = (max + min) / 2, d = max - min;
|
||||
|
||||
if (max === min) {
|
||||
h = s = 0;
|
||||
} else {
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
|
||||
switch (max) {
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
}
|
||||
h /= 6;
|
||||
}
|
||||
return { h: h * 360, s: s, l: l, a: a };
|
||||
},
|
||||
//Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
|
||||
toHSV: function () {
|
||||
var r = this.rgb[0] / 255,
|
||||
g = this.rgb[1] / 255,
|
||||
b = this.rgb[2] / 255,
|
||||
a = this.alpha;
|
||||
|
||||
var max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||||
var h, s, v = max;
|
||||
|
||||
var d = max - min;
|
||||
if (max === 0) {
|
||||
s = 0;
|
||||
} else {
|
||||
s = d / max;
|
||||
}
|
||||
|
||||
if (max === min) {
|
||||
h = 0;
|
||||
} else {
|
||||
switch(max){
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
}
|
||||
h /= 6;
|
||||
}
|
||||
return { h: h * 360, s: s, v: v, a: a };
|
||||
},
|
||||
toARGB: function () {
|
||||
return toHex([this.alpha * 255].concat(this.rgb));
|
||||
},
|
||||
compare: function (x) {
|
||||
if (!x.rgb) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (x.rgb[0] === this.rgb[0] &&
|
||||
x.rgb[1] === this.rgb[1] &&
|
||||
x.rgb[2] === this.rgb[2] &&
|
||||
x.alpha === this.alpha) ? 0 : -1;
|
||||
// `keyword` is set if this color was originally
|
||||
// converted from a named color string so we need
|
||||
// to respect this and try to output named color too.
|
||||
if (this.keyword) {
|
||||
return this.keyword;
|
||||
}
|
||||
|
||||
// If we have some transparency, the only way to represent it
|
||||
// is via `rgba`. Otherwise, we use the hex representation,
|
||||
// which has better compatibility with older browsers.
|
||||
// Values are capped between `0` and `255`, rounded and zero-padded.
|
||||
alpha = this.fround(env, this.alpha);
|
||||
if (alpha < 1) {
|
||||
return "rgba(" + this.rgb.map(function (c) {
|
||||
return clamp(Math.round(c), 255);
|
||||
}).concat(clamp(alpha, 1))
|
||||
.join(',' + (compress ? '' : ' ')) + ")";
|
||||
}
|
||||
|
||||
color = this.toRGB();
|
||||
|
||||
if (compress) {
|
||||
var splitcolor = color.split('');
|
||||
|
||||
// Convert color to short format
|
||||
if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) {
|
||||
color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[5];
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
};
|
||||
|
||||
//
|
||||
// Operations have to be done per-channel, if not,
|
||||
// channels will spill onto each other. Once we have
|
||||
// our result, in the form of an integer triplet,
|
||||
// we create a new Color node to hold the result.
|
||||
//
|
||||
Color.prototype.operate = function (env, op, other) {
|
||||
var rgb = [];
|
||||
var alpha = this.alpha * (1 - other.alpha) + other.alpha;
|
||||
for (var c = 0; c < 3; c++) {
|
||||
rgb[c] = this._operate(env, op, this.rgb[c], other.rgb[c]);
|
||||
}
|
||||
return new(Color)(rgb, alpha);
|
||||
};
|
||||
Color.prototype.toRGB = function () {
|
||||
return toHex(this.rgb);
|
||||
};
|
||||
Color.prototype.toHSL = function () {
|
||||
var r = this.rgb[0] / 255,
|
||||
g = this.rgb[1] / 255,
|
||||
b = this.rgb[2] / 255,
|
||||
a = this.alpha;
|
||||
|
||||
var max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||||
var h, s, l = (max + min) / 2, d = max - min;
|
||||
|
||||
if (max === min) {
|
||||
h = s = 0;
|
||||
} else {
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
|
||||
switch (max) {
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
}
|
||||
h /= 6;
|
||||
}
|
||||
return { h: h * 360, s: s, l: l, a: a };
|
||||
};
|
||||
//Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
|
||||
Color.prototype.toHSV = function () {
|
||||
var r = this.rgb[0] / 255,
|
||||
g = this.rgb[1] / 255,
|
||||
b = this.rgb[2] / 255,
|
||||
a = this.alpha;
|
||||
|
||||
var max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||||
var h, s, v = max;
|
||||
|
||||
var d = max - min;
|
||||
if (max === 0) {
|
||||
s = 0;
|
||||
} else {
|
||||
s = d / max;
|
||||
}
|
||||
|
||||
if (max === min) {
|
||||
h = 0;
|
||||
} else {
|
||||
switch(max){
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
}
|
||||
h /= 6;
|
||||
}
|
||||
return { h: h * 360, s: s, v: v, a: a };
|
||||
};
|
||||
Color.prototype.toARGB = function () {
|
||||
return toHex([this.alpha * 255].concat(this.rgb));
|
||||
};
|
||||
Color.prototype.compare = function (x) {
|
||||
if (!x.rgb) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (x.rgb[0] === this.rgb[0] &&
|
||||
x.rgb[1] === this.rgb[1] &&
|
||||
x.rgb[2] === this.rgb[2] &&
|
||||
x.alpha === this.alpha) ? 0 : -1;
|
||||
};
|
||||
|
||||
Color.fromKeyword = function(keyword) {
|
||||
var c, key = keyword.toLowerCase();
|
||||
if (data.colors.hasOwnProperty(key)) {
|
||||
c = new(Color)(data.colors[key].slice(1));
|
||||
if (colors.hasOwnProperty(key)) {
|
||||
c = new(Color)(colors[key].slice(1));
|
||||
}
|
||||
else if (key === "transparent") {
|
||||
c = new(Color)([0, 0, 0], 0);
|
||||
@@ -177,16 +186,4 @@ Color.fromKeyword = function(keyword) {
|
||||
return c;
|
||||
}
|
||||
};
|
||||
|
||||
function toHex(v) {
|
||||
return '#' + v.map(function (c) {
|
||||
c = clamp(Math.round(c), 255);
|
||||
return (c < 16 ? '0' : '') + c.toString(16);
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function clamp(v, max) {
|
||||
return Math.min(Math.max(v, 0), max);
|
||||
}
|
||||
return Color;
|
||||
};
|
||||
module.exports = Color;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js");
|
||||
|
||||
var Combinator = function (value) {
|
||||
if (value === ' ') {
|
||||
@@ -7,18 +7,15 @@ var Combinator = function (value) {
|
||||
this.value = value ? value.trim() : "";
|
||||
}
|
||||
};
|
||||
Combinator.prototype = {
|
||||
type: "Combinator",
|
||||
_noSpaceCombinators: {
|
||||
'': true,
|
||||
' ': true,
|
||||
'|': true
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
var spaceOrEmpty = (env.compress || this._noSpaceCombinators[this.value]) ? '' : ' ';
|
||||
output.add(spaceOrEmpty + this.value + spaceOrEmpty);
|
||||
},
|
||||
toCSS: tree.toCSS
|
||||
Combinator.prototype = new Node();
|
||||
Combinator.prototype.type = "Combinator";
|
||||
var _noSpaceCombinators = {
|
||||
'': true,
|
||||
' ': true,
|
||||
'|': true
|
||||
};
|
||||
return Combinator;
|
||||
Combinator.prototype.genCSS = function (env, output) {
|
||||
var spaceOrEmpty = (env.compress || _noSpaceCombinators[this.value]) ? '' : ' ';
|
||||
output.add(spaceOrEmpty + this.value + spaceOrEmpty);
|
||||
};
|
||||
module.exports = Combinator;
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js"),
|
||||
getDebugInfo = require("./debug-info.js");
|
||||
|
||||
var Comment = function (value, isLineComment, index, currentFileInfo) {
|
||||
this.value = value;
|
||||
this.isLineComment = isLineComment;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
};
|
||||
Comment.prototype = {
|
||||
type: "Comment",
|
||||
genCSS: function (env, output) {
|
||||
if (this.debugInfo) {
|
||||
output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index);
|
||||
}
|
||||
output.add(this.value);
|
||||
},
|
||||
toCSS: tree.toCSS,
|
||||
isSilent: function(env) {
|
||||
var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced),
|
||||
isCompressed = env.compress && this.value[2] !== "!";
|
||||
return this.isLineComment || isReference || isCompressed;
|
||||
},
|
||||
eval: function () { return this; },
|
||||
markReferenced: function () {
|
||||
this.isReferenced = true;
|
||||
Comment.prototype = new Node();
|
||||
Comment.prototype.type = "Comment";
|
||||
Comment.prototype.genCSS = function (env, output) {
|
||||
if (this.debugInfo) {
|
||||
output.add(getDebugInfo(env, this), this.currentFileInfo, this.index);
|
||||
}
|
||||
output.add(this.value);
|
||||
};
|
||||
return Comment;
|
||||
Comment.prototype.isSilent = function(env) {
|
||||
var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced),
|
||||
isCompressed = env.compress && this.value[2] !== "!";
|
||||
return this.isLineComment || isReference || isCompressed;
|
||||
};
|
||||
Comment.prototype.markReferenced = function () {
|
||||
this.isReferenced = true;
|
||||
};
|
||||
Comment.prototype.isRulesetLike = function(root) {
|
||||
return Boolean(root);
|
||||
};
|
||||
module.exports = Comment;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js");
|
||||
|
||||
var Condition = function (op, l, r, i, negate) {
|
||||
this.op = op.trim();
|
||||
@@ -7,43 +7,41 @@ var Condition = function (op, l, r, i, negate) {
|
||||
this.index = i;
|
||||
this.negate = negate;
|
||||
};
|
||||
Condition.prototype = {
|
||||
type: "Condition",
|
||||
accept: function (visitor) {
|
||||
this.lvalue = visitor.visit(this.lvalue);
|
||||
this.rvalue = visitor.visit(this.rvalue);
|
||||
},
|
||||
eval: function (env) {
|
||||
var a = this.lvalue.eval(env),
|
||||
b = this.rvalue.eval(env);
|
||||
|
||||
var i = this.index, result;
|
||||
|
||||
result = (function (op) {
|
||||
switch (op) {
|
||||
case 'and':
|
||||
return a && b;
|
||||
case 'or':
|
||||
return a || b;
|
||||
default:
|
||||
if (a.compare) {
|
||||
result = a.compare(b);
|
||||
} else if (b.compare) {
|
||||
result = b.compare(a);
|
||||
} else {
|
||||
throw { type: "Type",
|
||||
message: "Unable to perform comparison",
|
||||
index: i };
|
||||
}
|
||||
switch (result) {
|
||||
case -1: return op === '<' || op === '=<' || op === '<=';
|
||||
case 0: return op === '=' || op === '>=' || op === '=<' || op === '<=';
|
||||
case 1: return op === '>' || op === '>=';
|
||||
}
|
||||
}
|
||||
})(this.op);
|
||||
return this.negate ? !result : result;
|
||||
}
|
||||
Condition.prototype = new Node();
|
||||
Condition.prototype.type = "Condition";
|
||||
Condition.prototype.accept = function (visitor) {
|
||||
this.lvalue = visitor.visit(this.lvalue);
|
||||
this.rvalue = visitor.visit(this.rvalue);
|
||||
};
|
||||
return Condition;
|
||||
Condition.prototype.eval = function (env) {
|
||||
var a = this.lvalue.eval(env),
|
||||
b = this.rvalue.eval(env);
|
||||
|
||||
var i = this.index, result;
|
||||
|
||||
result = (function (op) {
|
||||
switch (op) {
|
||||
case 'and':
|
||||
return a && b;
|
||||
case 'or':
|
||||
return a || b;
|
||||
default:
|
||||
if (a.compare) {
|
||||
result = a.compare(b);
|
||||
} else if (b.compare) {
|
||||
result = b.compare(a);
|
||||
} else {
|
||||
throw { type: "Type",
|
||||
message: "Unable to perform comparison",
|
||||
index: i };
|
||||
}
|
||||
switch (result) {
|
||||
case -1: return op === '<' || op === '=<' || op === '<=';
|
||||
case 0: return op === '=' || op === '>=' || op === '=<' || op === '<=';
|
||||
case 1: return op === '>' || op === '>=';
|
||||
}
|
||||
}
|
||||
})(this.op);
|
||||
return this.negate ? !result : result;
|
||||
};
|
||||
module.exports = Condition;
|
||||
|
||||
34
lib/less/tree/debug-info.js
Normal file
34
lib/less/tree/debug-info.js
Normal file
@@ -0,0 +1,34 @@
|
||||
var debugInfo = function(env, ctx, lineSeperator) {
|
||||
var result="";
|
||||
if (env.dumpLineNumbers && !env.compress) {
|
||||
switch(env.dumpLineNumbers) {
|
||||
case 'comments':
|
||||
result = debugInfo.asComment(ctx);
|
||||
break;
|
||||
case 'mediaquery':
|
||||
result = debugInfo.asMediaQuery(ctx);
|
||||
break;
|
||||
case 'all':
|
||||
result = debugInfo.asComment(ctx) + (lineSeperator || "") + debugInfo.asMediaQuery(ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
debugInfo.asComment = function(ctx) {
|
||||
return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n';
|
||||
};
|
||||
|
||||
debugInfo.asMediaQuery = function(ctx) {
|
||||
return '@media -sass-debug-info{filename{font-family:' +
|
||||
('file://' + ctx.debugInfo.fileName).replace(/([.:\/\\])/g, function (a) {
|
||||
if (a == '\\') {
|
||||
a = '\/';
|
||||
}
|
||||
return '\\' + a;
|
||||
}) +
|
||||
'}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n';
|
||||
};
|
||||
|
||||
module.exports = debugInfo;
|
||||
@@ -1,21 +1,21 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js"),
|
||||
contexts = require("../env.js");
|
||||
|
||||
var DetachedRuleset = function (ruleset, frames) {
|
||||
this.ruleset = ruleset;
|
||||
this.frames = frames;
|
||||
};
|
||||
DetachedRuleset.prototype = {
|
||||
type: "DetachedRuleset",
|
||||
accept: function (visitor) {
|
||||
this.ruleset = visitor.visit(this.ruleset);
|
||||
},
|
||||
eval: function (env) {
|
||||
var frames = this.frames || env.frames.slice(0);
|
||||
return new DetachedRuleset(this.ruleset, frames);
|
||||
},
|
||||
callEval: function (env) {
|
||||
return this.ruleset.eval(this.frames ? new(tree.evalEnv)(env, this.frames.concat(env.frames)) : env);
|
||||
}
|
||||
DetachedRuleset.prototype = new Node();
|
||||
DetachedRuleset.prototype.type = "DetachedRuleset";
|
||||
DetachedRuleset.prototype.evalFirst = true;
|
||||
DetachedRuleset.prototype.accept = function (visitor) {
|
||||
this.ruleset = visitor.visit(this.ruleset);
|
||||
};
|
||||
return DetachedRuleset;
|
||||
DetachedRuleset.prototype.eval = function (env) {
|
||||
var frames = this.frames || env.frames.slice(0);
|
||||
return new DetachedRuleset(this.ruleset, frames);
|
||||
};
|
||||
DetachedRuleset.prototype.callEval = function (env) {
|
||||
return this.ruleset.eval(this.frames ? new(contexts.evalEnv)(env, this.frames.concat(env.frames)) : env);
|
||||
};
|
||||
module.exports = DetachedRuleset;
|
||||
|
||||
@@ -1,167 +1,164 @@
|
||||
module.exports = function (tree, unitConversions) {
|
||||
var Node = require("./node.js"),
|
||||
unitConversions = require("../data/unit-conversions.js"),
|
||||
Unit = require("./unit.js"),
|
||||
Color = require("./color.js");
|
||||
|
||||
//
|
||||
// A number with a unit
|
||||
//
|
||||
var Dimension = function (value, unit) {
|
||||
this.value = parseFloat(value);
|
||||
this.unit = (unit && unit instanceof tree.Unit) ? unit :
|
||||
new(tree.Unit)(unit ? [unit] : undefined);
|
||||
this.unit = (unit && unit instanceof Unit) ? unit :
|
||||
new(Unit)(unit ? [unit] : undefined);
|
||||
};
|
||||
|
||||
Dimension.prototype = {
|
||||
type: "Dimension",
|
||||
accept: function (visitor) {
|
||||
this.unit = visitor.visit(this.unit);
|
||||
},
|
||||
eval: function (env) {
|
||||
return this;
|
||||
},
|
||||
toColor: function () {
|
||||
return new(tree.Color)([this.value, this.value, this.value]);
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
if ((env && env.strictUnits) && !this.unit.isSingular()) {
|
||||
throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());
|
||||
Dimension.prototype = new Node();
|
||||
Dimension.prototype.type = "Dimension";
|
||||
Dimension.prototype.accept = function (visitor) {
|
||||
this.unit = visitor.visit(this.unit);
|
||||
};
|
||||
Dimension.prototype.eval = function (env) {
|
||||
return this;
|
||||
};
|
||||
Dimension.prototype.toColor = function () {
|
||||
return new(Color)([this.value, this.value, this.value]);
|
||||
};
|
||||
Dimension.prototype.genCSS = function (env, output) {
|
||||
if ((env && env.strictUnits) && !this.unit.isSingular()) {
|
||||
throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());
|
||||
}
|
||||
|
||||
var value = this.fround(env, this.value),
|
||||
strValue = String(value);
|
||||
|
||||
if (value !== 0 && value < 0.000001 && value > -0.000001) {
|
||||
// would be output 1e-6 etc.
|
||||
strValue = value.toFixed(20).replace(/0+$/, "");
|
||||
}
|
||||
|
||||
if (env && env.compress) {
|
||||
// Zero values doesn't need a unit
|
||||
if (value === 0 && this.unit.isLength()) {
|
||||
output.add(strValue);
|
||||
return;
|
||||
}
|
||||
|
||||
var value = tree.fround(env, this.value),
|
||||
strValue = String(value);
|
||||
|
||||
if (value !== 0 && value < 0.000001 && value > -0.000001) {
|
||||
// would be output 1e-6 etc.
|
||||
strValue = value.toFixed(20).replace(/0+$/, "");
|
||||
// Float values doesn't need a leading zero
|
||||
if (value > 0 && value < 1) {
|
||||
strValue = (strValue).substr(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (env && env.compress) {
|
||||
// Zero values doesn't need a unit
|
||||
if (value === 0 && this.unit.isLength()) {
|
||||
output.add(strValue);
|
||||
return;
|
||||
}
|
||||
output.add(strValue);
|
||||
this.unit.genCSS(env, output);
|
||||
};
|
||||
|
||||
// Float values doesn't need a leading zero
|
||||
if (value > 0 && value < 1) {
|
||||
strValue = (strValue).substr(1);
|
||||
}
|
||||
}
|
||||
// In an operation between two Dimensions,
|
||||
// we default to the first Dimension's unit,
|
||||
// so `1px + 2` will yield `3px`.
|
||||
Dimension.prototype.operate = function (env, op, other) {
|
||||
/*jshint noempty:false */
|
||||
var value = this._operate(env, op, this.value, other.value),
|
||||
unit = this.unit.clone();
|
||||
|
||||
output.add(strValue);
|
||||
this.unit.genCSS(env, output);
|
||||
},
|
||||
toCSS: tree.toCSS,
|
||||
|
||||
// In an operation between two Dimensions,
|
||||
// we default to the first Dimension's unit,
|
||||
// so `1px + 2` will yield `3px`.
|
||||
operate: function (env, op, other) {
|
||||
/*jshint noempty:false */
|
||||
var value = tree.operate(env, op, this.value, other.value),
|
||||
unit = this.unit.clone();
|
||||
|
||||
if (op === '+' || op === '-') {
|
||||
if (unit.numerator.length === 0 && unit.denominator.length === 0) {
|
||||
unit.numerator = other.unit.numerator.slice(0);
|
||||
unit.denominator = other.unit.denominator.slice(0);
|
||||
} else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) {
|
||||
// do nothing
|
||||
} else {
|
||||
other = other.convertTo(this.unit.usedUnits());
|
||||
|
||||
if(env.strictUnits && other.unit.toString() !== unit.toString()) {
|
||||
throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() +
|
||||
"' and '" + other.unit.toString() + "'.");
|
||||
}
|
||||
|
||||
value = tree.operate(env, op, this.value, other.value);
|
||||
}
|
||||
} else if (op === '*') {
|
||||
unit.numerator = unit.numerator.concat(other.unit.numerator).sort();
|
||||
unit.denominator = unit.denominator.concat(other.unit.denominator).sort();
|
||||
unit.cancel();
|
||||
} else if (op === '/') {
|
||||
unit.numerator = unit.numerator.concat(other.unit.denominator).sort();
|
||||
unit.denominator = unit.denominator.concat(other.unit.numerator).sort();
|
||||
unit.cancel();
|
||||
}
|
||||
return new(Dimension)(value, unit);
|
||||
},
|
||||
|
||||
compare: function (other) {
|
||||
if (other instanceof Dimension) {
|
||||
var a, b,
|
||||
aValue, bValue;
|
||||
|
||||
if (this.unit.isEmpty() || other.unit.isEmpty()) {
|
||||
a = this;
|
||||
b = other;
|
||||
} else {
|
||||
a = this.unify();
|
||||
b = other.unify();
|
||||
if (a.unit.compare(b.unit) !== 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
aValue = a.value;
|
||||
bValue = b.value;
|
||||
|
||||
if (bValue > aValue) {
|
||||
return -1;
|
||||
} else if (bValue < aValue) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
if (op === '+' || op === '-') {
|
||||
if (unit.numerator.length === 0 && unit.denominator.length === 0) {
|
||||
unit.numerator = other.unit.numerator.slice(0);
|
||||
unit.denominator = other.unit.denominator.slice(0);
|
||||
} else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) {
|
||||
// do nothing
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
},
|
||||
other = other.convertTo(this.unit.usedUnits());
|
||||
|
||||
unify: function () {
|
||||
return this.convertTo({ length: 'px', duration: 's', angle: 'rad' });
|
||||
},
|
||||
|
||||
convertTo: function (conversions) {
|
||||
var value = this.value, unit = this.unit.clone(),
|
||||
i, groupName, group, targetUnit, derivedConversions = {}, applyUnit;
|
||||
|
||||
if (typeof conversions === 'string') {
|
||||
for(i in unitConversions) {
|
||||
if (unitConversions[i].hasOwnProperty(conversions)) {
|
||||
derivedConversions = {};
|
||||
derivedConversions[i] = conversions;
|
||||
}
|
||||
}
|
||||
conversions = derivedConversions;
|
||||
}
|
||||
applyUnit = function (atomicUnit, denominator) {
|
||||
/*jshint loopfunc:true */
|
||||
if (group.hasOwnProperty(atomicUnit)) {
|
||||
if (denominator) {
|
||||
value = value / (group[atomicUnit] / group[targetUnit]);
|
||||
} else {
|
||||
value = value * (group[atomicUnit] / group[targetUnit]);
|
||||
}
|
||||
|
||||
return targetUnit;
|
||||
if(env.strictUnits && other.unit.toString() !== unit.toString()) {
|
||||
throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() +
|
||||
"' and '" + other.unit.toString() + "'.");
|
||||
}
|
||||
|
||||
return atomicUnit;
|
||||
};
|
||||
|
||||
for (groupName in conversions) {
|
||||
if (conversions.hasOwnProperty(groupName)) {
|
||||
targetUnit = conversions[groupName];
|
||||
group = unitConversions[groupName];
|
||||
|
||||
unit.map(applyUnit);
|
||||
}
|
||||
value = this._operate(env, op, this.value, other.value);
|
||||
}
|
||||
|
||||
} else if (op === '*') {
|
||||
unit.numerator = unit.numerator.concat(other.unit.numerator).sort();
|
||||
unit.denominator = unit.denominator.concat(other.unit.denominator).sort();
|
||||
unit.cancel();
|
||||
} else if (op === '/') {
|
||||
unit.numerator = unit.numerator.concat(other.unit.denominator).sort();
|
||||
unit.denominator = unit.denominator.concat(other.unit.numerator).sort();
|
||||
unit.cancel();
|
||||
}
|
||||
return new(Dimension)(value, unit);
|
||||
};
|
||||
Dimension.prototype.compare = function (other) {
|
||||
if (other instanceof Dimension) {
|
||||
var a, b,
|
||||
aValue, bValue;
|
||||
|
||||
return new(Dimension)(value, unit);
|
||||
if (this.unit.isEmpty() || other.unit.isEmpty()) {
|
||||
a = this;
|
||||
b = other;
|
||||
} else {
|
||||
a = this.unify();
|
||||
b = other.unify();
|
||||
if (a.unit.compare(b.unit) !== 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
aValue = a.value;
|
||||
bValue = b.value;
|
||||
|
||||
if (bValue > aValue) {
|
||||
return -1;
|
||||
} else if (bValue < aValue) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
return Dimension;
|
||||
Dimension.prototype.unify = function () {
|
||||
return this.convertTo({ length: 'px', duration: 's', angle: 'rad' });
|
||||
};
|
||||
Dimension.prototype.convertTo = function (conversions) {
|
||||
var value = this.value, unit = this.unit.clone(),
|
||||
i, groupName, group, targetUnit, derivedConversions = {}, applyUnit;
|
||||
|
||||
if (typeof conversions === 'string') {
|
||||
for(i in unitConversions) {
|
||||
if (unitConversions[i].hasOwnProperty(conversions)) {
|
||||
derivedConversions = {};
|
||||
derivedConversions[i] = conversions;
|
||||
}
|
||||
}
|
||||
conversions = derivedConversions;
|
||||
}
|
||||
applyUnit = function (atomicUnit, denominator) {
|
||||
/*jshint loopfunc:true */
|
||||
if (group.hasOwnProperty(atomicUnit)) {
|
||||
if (denominator) {
|
||||
value = value / (group[atomicUnit] / group[targetUnit]);
|
||||
} else {
|
||||
value = value * (group[atomicUnit] / group[targetUnit]);
|
||||
}
|
||||
|
||||
return targetUnit;
|
||||
}
|
||||
|
||||
return atomicUnit;
|
||||
};
|
||||
|
||||
for (groupName in conversions) {
|
||||
if (conversions.hasOwnProperty(groupName)) {
|
||||
targetUnit = conversions[groupName];
|
||||
group = unitConversions[groupName];
|
||||
|
||||
unit.map(applyUnit);
|
||||
}
|
||||
}
|
||||
|
||||
unit.cancel();
|
||||
|
||||
return new(Dimension)(value, unit);
|
||||
};
|
||||
module.exports = Dimension;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js"),
|
||||
Ruleset = require("./ruleset.js");
|
||||
|
||||
var Directive = function (name, value, rules, index, currentFileInfo, debugInfo) {
|
||||
this.name = name;
|
||||
@@ -12,64 +13,92 @@ var Directive = function (name, value, rules, index, currentFileInfo, debugInfo)
|
||||
this.debugInfo = debugInfo;
|
||||
};
|
||||
|
||||
Directive.prototype = {
|
||||
type: "Directive",
|
||||
accept: function (visitor) {
|
||||
var value = this.value, rules = this.rules;
|
||||
if (rules) {
|
||||
rules = visitor.visit(rules);
|
||||
}
|
||||
if (value) {
|
||||
value = visitor.visit(value);
|
||||
}
|
||||
},
|
||||
isRulesetLike: function() {
|
||||
return !this.isCharset();
|
||||
},
|
||||
isCharset: function() {
|
||||
return "@charset" === this.name;
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
var value = this.value, rules = this.rules;
|
||||
output.add(this.name, this.currentFileInfo, this.index);
|
||||
if (value) {
|
||||
output.add(' ');
|
||||
value.genCSS(env, output);
|
||||
}
|
||||
if (rules) {
|
||||
tree.outputRuleset(env, output, [rules]);
|
||||
} else {
|
||||
output.add(';');
|
||||
}
|
||||
},
|
||||
toCSS: tree.toCSS,
|
||||
eval: function (env) {
|
||||
var value = this.value, rules = this.rules;
|
||||
if (value) {
|
||||
value = value.eval(env);
|
||||
}
|
||||
if (rules) {
|
||||
rules = rules.eval(env);
|
||||
rules.root = true;
|
||||
}
|
||||
return new(Directive)(this.name, value, rules,
|
||||
this.index, this.currentFileInfo, this.debugInfo);
|
||||
},
|
||||
variable: function (name) { if (this.rules) return tree.Ruleset.prototype.variable.call(this.rules, name); },
|
||||
find: function () { if (this.rules) return tree.Ruleset.prototype.find.apply(this.rules, arguments); },
|
||||
rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); },
|
||||
markReferenced: function () {
|
||||
var i, rules;
|
||||
this.isReferenced = true;
|
||||
if (this.rules) {
|
||||
rules = this.rules.rules;
|
||||
for (i = 0; i < rules.length; i++) {
|
||||
if (rules[i].markReferenced) {
|
||||
rules[i].markReferenced();
|
||||
}
|
||||
Directive.prototype = new Node();
|
||||
Directive.prototype.type = "Directive";
|
||||
Directive.prototype.accept = function (visitor) {
|
||||
var value = this.value, rules = this.rules;
|
||||
if (rules) {
|
||||
rules = visitor.visit(rules);
|
||||
}
|
||||
if (value) {
|
||||
value = visitor.visit(value);
|
||||
}
|
||||
};
|
||||
Directive.prototype.isRulesetLike = function() {
|
||||
return this.rules || !this.isCharset();
|
||||
};
|
||||
Directive.prototype.isCharset = function() {
|
||||
return "@charset" === this.name;
|
||||
};
|
||||
Directive.prototype.genCSS = function (env, output) {
|
||||
var value = this.value, rules = this.rules;
|
||||
output.add(this.name, this.currentFileInfo, this.index);
|
||||
if (value) {
|
||||
output.add(' ');
|
||||
value.genCSS(env, output);
|
||||
}
|
||||
if (rules) {
|
||||
this.outputRuleset(env, output, [rules]);
|
||||
} else {
|
||||
output.add(';');
|
||||
}
|
||||
};
|
||||
Directive.prototype.eval = function (env) {
|
||||
var value = this.value, rules = this.rules;
|
||||
if (value) {
|
||||
value = value.eval(env);
|
||||
}
|
||||
if (rules) {
|
||||
rules = rules.eval(env);
|
||||
rules.root = true;
|
||||
}
|
||||
return new(Directive)(this.name, value, rules,
|
||||
this.index, this.currentFileInfo, this.debugInfo);
|
||||
};
|
||||
Directive.prototype.variable = function (name) { if (this.rules) return Ruleset.prototype.variable.call(this.rules, name); };
|
||||
Directive.prototype.find = function () { if (this.rules) return Ruleset.prototype.find.apply(this.rules, arguments); };
|
||||
Directive.prototype.rulesets = function () { if (this.rules) return Ruleset.prototype.rulesets.apply(this.rules); };
|
||||
Directive.prototype.markReferenced = function () {
|
||||
var i, rules;
|
||||
this.isReferenced = true;
|
||||
if (this.rules) {
|
||||
rules = this.rules.rules;
|
||||
for (i = 0; i < rules.length; i++) {
|
||||
if (rules[i].markReferenced) {
|
||||
rules[i].markReferenced();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return Directive;
|
||||
Directive.prototype.outputRuleset = function (env, output, rules) {
|
||||
var ruleCnt = rules.length, i;
|
||||
env.tabLevel = (env.tabLevel | 0) + 1;
|
||||
|
||||
// Compressed
|
||||
if (env.compress) {
|
||||
output.add('{');
|
||||
for (i = 0; i < ruleCnt; i++) {
|
||||
rules[i].genCSS(env, output);
|
||||
}
|
||||
output.add('}');
|
||||
env.tabLevel--;
|
||||
return;
|
||||
}
|
||||
|
||||
// Non-compressed
|
||||
var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " ";
|
||||
if (!ruleCnt) {
|
||||
output.add(" {" + tabSetStr + '}');
|
||||
} else {
|
||||
output.add(" {" + tabRuleStr);
|
||||
rules[0].genCSS(env, output);
|
||||
for (i = 1; i < ruleCnt; i++) {
|
||||
output.add(tabRuleStr);
|
||||
rules[i].genCSS(env, output);
|
||||
}
|
||||
output.add(tabSetStr + '}');
|
||||
}
|
||||
|
||||
env.tabLevel--;
|
||||
};
|
||||
module.exports = Directive;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js"),
|
||||
Combinator = require("./combinator.js");
|
||||
|
||||
var Element = function (combinator, value, index, currentFileInfo) {
|
||||
this.combinator = combinator instanceof tree.Combinator ?
|
||||
combinator : new(tree.Combinator)(combinator);
|
||||
this.combinator = combinator instanceof Combinator ?
|
||||
combinator : new(Combinator)(combinator);
|
||||
|
||||
if (typeof(value) === 'string') {
|
||||
this.value = value.trim();
|
||||
@@ -14,32 +15,30 @@ var Element = function (combinator, value, index, currentFileInfo) {
|
||||
this.index = index;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
};
|
||||
Element.prototype = {
|
||||
type: "Element",
|
||||
accept: function (visitor) {
|
||||
var value = this.value;
|
||||
this.combinator = visitor.visit(this.combinator);
|
||||
if (typeof value === "object") {
|
||||
this.value = visitor.visit(value);
|
||||
}
|
||||
},
|
||||
eval: function (env) {
|
||||
return new(Element)(this.combinator,
|
||||
this.value.eval ? this.value.eval(env) : this.value,
|
||||
this.index,
|
||||
this.currentFileInfo);
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
output.add(this.toCSS(env), this.currentFileInfo, this.index);
|
||||
},
|
||||
toCSS: function (env) {
|
||||
var value = (this.value.toCSS ? this.value.toCSS(env) : this.value);
|
||||
if (value === '' && this.combinator.value.charAt(0) === '&') {
|
||||
return '';
|
||||
} else {
|
||||
return this.combinator.toCSS(env || {}) + value;
|
||||
}
|
||||
Element.prototype = new Node();
|
||||
Element.prototype.type = "Element";
|
||||
Element.prototype.accept = function (visitor) {
|
||||
var value = this.value;
|
||||
this.combinator = visitor.visit(this.combinator);
|
||||
if (typeof value === "object") {
|
||||
this.value = visitor.visit(value);
|
||||
}
|
||||
};
|
||||
return Element;
|
||||
Element.prototype.eval = function (env) {
|
||||
return new(Element)(this.combinator,
|
||||
this.value.eval ? this.value.eval(env) : this.value,
|
||||
this.index,
|
||||
this.currentFileInfo);
|
||||
};
|
||||
Element.prototype.genCSS = function (env, output) {
|
||||
output.add(this.toCSS(env), this.currentFileInfo, this.index);
|
||||
};
|
||||
Element.prototype.toCSS = function (env) {
|
||||
var value = (this.value.toCSS ? this.value.toCSS(env) : this.value);
|
||||
if (value === '' && this.combinator.value.charAt(0) === '&') {
|
||||
return '';
|
||||
} else {
|
||||
return this.combinator.toCSS(env || {}) + value;
|
||||
}
|
||||
};
|
||||
module.exports = Element;
|
||||
|
||||
@@ -1,54 +1,53 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js"),
|
||||
Paren = require("./paren.js"),
|
||||
Comment = require("./comment.js");
|
||||
|
||||
var Expression = function (value) { this.value = value; };
|
||||
Expression.prototype = {
|
||||
type: "Expression",
|
||||
accept: function (visitor) {
|
||||
if (this.value) {
|
||||
this.value = visitor.visitArray(this.value);
|
||||
}
|
||||
},
|
||||
eval: function (env) {
|
||||
var returnValue,
|
||||
inParenthesis = this.parens && !this.parensInOp,
|
||||
doubleParen = false;
|
||||
if (inParenthesis) {
|
||||
env.inParenthesis();
|
||||
}
|
||||
if (this.value.length > 1) {
|
||||
returnValue = new(Expression)(this.value.map(function (e) {
|
||||
return e.eval(env);
|
||||
}));
|
||||
} else if (this.value.length === 1) {
|
||||
if (this.value[0].parens && !this.value[0].parensInOp) {
|
||||
doubleParen = true;
|
||||
}
|
||||
returnValue = this.value[0].eval(env);
|
||||
} else {
|
||||
returnValue = this;
|
||||
}
|
||||
if (inParenthesis) {
|
||||
env.outOfParenthesis();
|
||||
}
|
||||
if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) {
|
||||
returnValue = new(tree.Paren)(returnValue);
|
||||
}
|
||||
return returnValue;
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
for(var i = 0; i < this.value.length; i++) {
|
||||
this.value[i].genCSS(env, output);
|
||||
if (i + 1 < this.value.length) {
|
||||
output.add(" ");
|
||||
}
|
||||
}
|
||||
},
|
||||
toCSS: tree.toCSS,
|
||||
throwAwayComments: function () {
|
||||
this.value = this.value.filter(function(v) {
|
||||
return !(v instanceof tree.Comment);
|
||||
});
|
||||
Expression.prototype = new Node();
|
||||
Expression.prototype.type = "Expression";
|
||||
Expression.prototype.accept = function (visitor) {
|
||||
if (this.value) {
|
||||
this.value = visitor.visitArray(this.value);
|
||||
}
|
||||
};
|
||||
return Expression;
|
||||
Expression.prototype.eval = function (env) {
|
||||
var returnValue,
|
||||
inParenthesis = this.parens && !this.parensInOp,
|
||||
doubleParen = false;
|
||||
if (inParenthesis) {
|
||||
env.inParenthesis();
|
||||
}
|
||||
if (this.value.length > 1) {
|
||||
returnValue = new(Expression)(this.value.map(function (e) {
|
||||
return e.eval(env);
|
||||
}));
|
||||
} else if (this.value.length === 1) {
|
||||
if (this.value[0].parens && !this.value[0].parensInOp) {
|
||||
doubleParen = true;
|
||||
}
|
||||
returnValue = this.value[0].eval(env);
|
||||
} else {
|
||||
returnValue = this;
|
||||
}
|
||||
if (inParenthesis) {
|
||||
env.outOfParenthesis();
|
||||
}
|
||||
if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) {
|
||||
returnValue = new(Paren)(returnValue);
|
||||
}
|
||||
return returnValue;
|
||||
};
|
||||
Expression.prototype.genCSS = function (env, output) {
|
||||
for(var i = 0; i < this.value.length; i++) {
|
||||
this.value[i].genCSS(env, output);
|
||||
if (i + 1 < this.value.length) {
|
||||
output.add(" ");
|
||||
}
|
||||
}
|
||||
};
|
||||
Expression.prototype.throwAwayComments = function () {
|
||||
this.value = this.value.filter(function(v) {
|
||||
return !(v instanceof Comment);
|
||||
});
|
||||
};
|
||||
module.exports = Expression;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js");
|
||||
|
||||
var Extend = function Extend(selector, option, index) {
|
||||
this.selector = selector;
|
||||
@@ -20,34 +20,32 @@ var Extend = function Extend(selector, option, index) {
|
||||
};
|
||||
Extend.next_id = 0;
|
||||
|
||||
Extend.prototype = {
|
||||
type: "Extend",
|
||||
accept: function (visitor) {
|
||||
this.selector = visitor.visit(this.selector);
|
||||
},
|
||||
eval: function (env) {
|
||||
return new(Extend)(this.selector.eval(env), this.option, this.index);
|
||||
},
|
||||
clone: function (env) {
|
||||
return new(Extend)(this.selector, this.option, this.index);
|
||||
},
|
||||
findSelfSelectors: function (selectors) {
|
||||
var selfElements = [],
|
||||
i,
|
||||
selectorElements;
|
||||
Extend.prototype = new Node();
|
||||
Extend.prototype.type = "Extend";
|
||||
Extend.prototype.accept = function (visitor) {
|
||||
this.selector = visitor.visit(this.selector);
|
||||
};
|
||||
Extend.prototype.eval = function (env) {
|
||||
return new(Extend)(this.selector.eval(env), this.option, this.index);
|
||||
};
|
||||
Extend.prototype.clone = function (env) {
|
||||
return new(Extend)(this.selector, this.option, this.index);
|
||||
};
|
||||
Extend.prototype.findSelfSelectors = function (selectors) {
|
||||
var selfElements = [],
|
||||
i,
|
||||
selectorElements;
|
||||
|
||||
for(i = 0; i < selectors.length; i++) {
|
||||
selectorElements = selectors[i].elements;
|
||||
// duplicate the logic in genCSS function inside the selector node.
|
||||
// future TODO - move both logics into the selector joiner visitor
|
||||
if (i > 0 && selectorElements.length && selectorElements[0].combinator.value === "") {
|
||||
selectorElements[0].combinator.value = ' ';
|
||||
}
|
||||
selfElements = selfElements.concat(selectors[i].elements);
|
||||
for(i = 0; i < selectors.length; i++) {
|
||||
selectorElements = selectors[i].elements;
|
||||
// duplicate the logic in genCSS function inside the selector node.
|
||||
// future TODO - move both logics into the selector joiner visitor
|
||||
if (i > 0 && selectorElements.length && selectorElements[0].combinator.value === "") {
|
||||
selectorElements[0].combinator.value = ' ';
|
||||
}
|
||||
|
||||
this.selfSelectors = [{ elements: selfElements }];
|
||||
selfElements = selfElements.concat(selectors[i].elements);
|
||||
}
|
||||
|
||||
this.selfSelectors = [{ elements: selfElements }];
|
||||
};
|
||||
return Extend;
|
||||
};
|
||||
module.exports = Extend;
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js"),
|
||||
Media = require("./media.js"),
|
||||
URL = require("./url.js"),
|
||||
Quoted = require("./quoted.js"),
|
||||
Ruleset = require("./ruleset.js"),
|
||||
Anonymous = require("./anonymous.js");
|
||||
|
||||
//
|
||||
// CSS @import node
|
||||
//
|
||||
@@ -37,88 +43,85 @@ var Import = function (path, features, options, index, currentFileInfo) {
|
||||
// we end up with a flat structure, which can easily be imported in the parent
|
||||
// ruleset.
|
||||
//
|
||||
Import.prototype = {
|
||||
type: "Import",
|
||||
accept: function (visitor) {
|
||||
if (this.features) {
|
||||
this.features = visitor.visit(this.features);
|
||||
}
|
||||
this.path = visitor.visit(this.path);
|
||||
if (!this.options.inline && this.root) {
|
||||
this.root = visitor.visit(this.root);
|
||||
}
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
if (this.css) {
|
||||
output.add("@import ", this.currentFileInfo, this.index);
|
||||
this.path.genCSS(env, output);
|
||||
if (this.features) {
|
||||
output.add(" ");
|
||||
this.features.genCSS(env, output);
|
||||
}
|
||||
output.add(';');
|
||||
}
|
||||
},
|
||||
toCSS: tree.toCSS,
|
||||
getPath: function () {
|
||||
if (this.path instanceof tree.Quoted) {
|
||||
var path = this.path.value;
|
||||
return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less';
|
||||
} else if (this.path instanceof tree.URL) {
|
||||
return this.path.value.value;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
evalForImport: function (env) {
|
||||
return new(Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo);
|
||||
},
|
||||
evalPath: function (env) {
|
||||
var path = this.path.eval(env);
|
||||
var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
|
||||
|
||||
if (!(path instanceof tree.URL)) {
|
||||
if (rootpath) {
|
||||
var pathValue = path.value;
|
||||
// Add the base path if the import is relative
|
||||
if (pathValue && env.isPathRelative(pathValue)) {
|
||||
path.value = rootpath +pathValue;
|
||||
}
|
||||
}
|
||||
path.value = env.normalizePath(path.value);
|
||||
}
|
||||
|
||||
return path;
|
||||
},
|
||||
eval: function (env) {
|
||||
var ruleset, features = this.features && this.features.eval(env);
|
||||
|
||||
if (this.skip) {
|
||||
if (typeof this.skip === "function") {
|
||||
this.skip = this.skip();
|
||||
}
|
||||
if (this.skip) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.inline) {
|
||||
//todo needs to reference css file not import
|
||||
var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true, true);
|
||||
return this.features ? new(tree.Media)([contents], this.features.value) : [contents];
|
||||
} else if (this.css) {
|
||||
var newImport = new(Import)(this.evalPath(env), features, this.options, this.index);
|
||||
if (!newImport.css && this.error) {
|
||||
throw this.error;
|
||||
}
|
||||
return newImport;
|
||||
} else {
|
||||
ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0));
|
||||
|
||||
ruleset.evalImports(env);
|
||||
|
||||
return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules;
|
||||
}
|
||||
Import.prototype = new Node();
|
||||
Import.prototype.type = "Import";
|
||||
Import.prototype.accept = function (visitor) {
|
||||
if (this.features) {
|
||||
this.features = visitor.visit(this.features);
|
||||
}
|
||||
this.path = visitor.visit(this.path);
|
||||
if (!this.options.inline && this.root) {
|
||||
this.root = visitor.visit(this.root);
|
||||
}
|
||||
};
|
||||
return Import;
|
||||
Import.prototype.genCSS = function (env, output) {
|
||||
if (this.css) {
|
||||
output.add("@import ", this.currentFileInfo, this.index);
|
||||
this.path.genCSS(env, output);
|
||||
if (this.features) {
|
||||
output.add(" ");
|
||||
this.features.genCSS(env, output);
|
||||
}
|
||||
output.add(';');
|
||||
}
|
||||
};
|
||||
Import.prototype.getPath = function () {
|
||||
if (this.path instanceof Quoted) {
|
||||
var path = this.path.value;
|
||||
return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less';
|
||||
} else if (this.path instanceof URL) {
|
||||
return this.path.value.value;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
Import.prototype.evalForImport = function (env) {
|
||||
return new(Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo);
|
||||
};
|
||||
Import.prototype.evalPath = function (env) {
|
||||
var path = this.path.eval(env);
|
||||
var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
|
||||
|
||||
if (!(path instanceof URL)) {
|
||||
if (rootpath) {
|
||||
var pathValue = path.value;
|
||||
// Add the base path if the import is relative
|
||||
if (pathValue && env.isPathRelative(pathValue)) {
|
||||
path.value = rootpath +pathValue;
|
||||
}
|
||||
}
|
||||
path.value = env.normalizePath(path.value);
|
||||
}
|
||||
|
||||
return path;
|
||||
};
|
||||
Import.prototype.eval = function (env) {
|
||||
var ruleset, features = this.features && this.features.eval(env);
|
||||
|
||||
if (this.skip) {
|
||||
if (typeof this.skip === "function") {
|
||||
this.skip = this.skip();
|
||||
}
|
||||
if (this.skip) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.inline) {
|
||||
//todo needs to reference css file not import
|
||||
var contents = new(Anonymous)(this.root, 0, {filename: this.importedFilename}, true, true);
|
||||
return this.features ? new(Media)([contents], this.features.value) : [contents];
|
||||
} else if (this.css) {
|
||||
var newImport = new(Import)(this.evalPath(env), features, this.options, this.index);
|
||||
if (!newImport.css && this.error) {
|
||||
throw this.error;
|
||||
}
|
||||
return newImport;
|
||||
} else {
|
||||
ruleset = new(Ruleset)(null, this.root.rules.slice(0));
|
||||
|
||||
ruleset.evalImports(env);
|
||||
|
||||
return this.features ? new(Media)(ruleset.rules, this.features.value) : ruleset.rules;
|
||||
}
|
||||
};
|
||||
module.exports = Import;
|
||||
|
||||
@@ -1,57 +1,27 @@
|
||||
module.exports = function (tree) {
|
||||
var JsEvalNode = require("./js-eval-node.js"),
|
||||
Dimension = require("./dimension.js"),
|
||||
Quoted = require("./quoted.js"),
|
||||
Anonymous = require("./anonymous.js");
|
||||
|
||||
var JavaScript = function (string, index, escaped) {
|
||||
this.escaped = escaped;
|
||||
this.expression = string;
|
||||
this.index = index;
|
||||
};
|
||||
JavaScript.prototype = {
|
||||
type: "JavaScript",
|
||||
eval: function (env) {
|
||||
var result,
|
||||
that = this,
|
||||
context = {};
|
||||
JavaScript.prototype = new JsEvalNode();
|
||||
JavaScript.prototype.type = "JavaScript";
|
||||
JavaScript.prototype.eval = function(env) {
|
||||
var result = this.evaluateJavaScript(this.expression, env);
|
||||
|
||||
var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) {
|
||||
return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env));
|
||||
});
|
||||
|
||||
try {
|
||||
expression = new(Function)('return (' + expression + ')');
|
||||
} catch (e) {
|
||||
throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" ,
|
||||
index: this.index };
|
||||
}
|
||||
|
||||
var variables = env.frames[0].variables();
|
||||
for (var k in variables) {
|
||||
if (variables.hasOwnProperty(k)) {
|
||||
/*jshint loopfunc:true */
|
||||
context[k.slice(1)] = {
|
||||
value: variables[k].value,
|
||||
toJS: function () {
|
||||
return this.value.eval(env).toCSS();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
result = expression.call(context);
|
||||
} catch (e) {
|
||||
throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" ,
|
||||
index: this.index };
|
||||
}
|
||||
if (typeof(result) === 'number') {
|
||||
return new(tree.Dimension)(result);
|
||||
} else if (typeof(result) === 'string') {
|
||||
return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index);
|
||||
} else if (Array.isArray(result)) {
|
||||
return new(tree.Anonymous)(result.join(', '));
|
||||
} else {
|
||||
return new(tree.Anonymous)(result);
|
||||
}
|
||||
if (typeof(result) === 'number') {
|
||||
return new(Dimension)(result);
|
||||
} else if (typeof(result) === 'string') {
|
||||
return new(Quoted)('"' + result + '"', result, this.escaped, this.index);
|
||||
} else if (Array.isArray(result)) {
|
||||
return new(Anonymous)(result.join(', '));
|
||||
} else {
|
||||
return new(Anonymous)(result);
|
||||
}
|
||||
};
|
||||
return JavaScript;
|
||||
};
|
||||
|
||||
module.exports = JavaScript;
|
||||
|
||||
53
lib/less/tree/js-eval-node.js
Normal file
53
lib/less/tree/js-eval-node.js
Normal file
@@ -0,0 +1,53 @@
|
||||
var Node = require("./node.js"),
|
||||
Variable = require("./variable.js");
|
||||
|
||||
var jsEvalNode = function() {
|
||||
};
|
||||
jsEvalNode.prototype = new Node();
|
||||
|
||||
jsEvalNode.prototype.evaluateJavaScript = function (expression, env) {
|
||||
var result,
|
||||
that = this,
|
||||
context = {};
|
||||
|
||||
expression = expression.replace(/@\{([\w-]+)\}/g, function (_, name) {
|
||||
return that.jsify(new(Variable)('@' + name, that.index).eval(env));
|
||||
});
|
||||
|
||||
try {
|
||||
expression = new(Function)('return (' + expression + ')');
|
||||
} catch (e) {
|
||||
throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" ,
|
||||
index: this.index };
|
||||
}
|
||||
|
||||
var variables = env.frames[0].variables();
|
||||
for (var k in variables) {
|
||||
if (variables.hasOwnProperty(k)) {
|
||||
/*jshint loopfunc:true */
|
||||
context[k.slice(1)] = {
|
||||
value: variables[k].value,
|
||||
toJS: function () {
|
||||
return this.value.eval(env).toCSS();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
result = expression.call(context);
|
||||
} catch (e) {
|
||||
throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" ,
|
||||
index: this.index };
|
||||
}
|
||||
return result;
|
||||
};
|
||||
jsEvalNode.prototype.jsify = function (obj) {
|
||||
if (Array.isArray(obj.value) && (obj.value.length > 1)) {
|
||||
return '[' + obj.value.map(function (v) { return v.toCSS(); }).join(', ') + ']';
|
||||
} else {
|
||||
return obj.toCSS();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = jsEvalNode;
|
||||
@@ -1,27 +1,21 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js");
|
||||
|
||||
var Keyword = function (value) { this.value = value; };
|
||||
Keyword.prototype = {
|
||||
type: "Keyword",
|
||||
eval: function () { return this; },
|
||||
genCSS: function (env, output) {
|
||||
if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; }
|
||||
output.add(this.value);
|
||||
},
|
||||
toCSS: tree.toCSS,
|
||||
compare: function (other) {
|
||||
if (other instanceof Keyword) {
|
||||
return other.value === this.value ? 0 : 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
Keyword.prototype = new Node();
|
||||
Keyword.prototype.type = "Keyword";
|
||||
Keyword.prototype.genCSS = function (env, output) {
|
||||
if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; }
|
||||
output.add(this.value);
|
||||
};
|
||||
Keyword.prototype.compare = function (other) {
|
||||
if (other instanceof Keyword) {
|
||||
return other.value === this.value ? 0 : 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
//TODO move?
|
||||
tree.True = new(Keyword)('true');
|
||||
tree.False = new(Keyword)('false');
|
||||
Keyword.True = new(Keyword)('true');
|
||||
Keyword.False = new(Keyword)('false');
|
||||
|
||||
return Keyword;
|
||||
|
||||
};
|
||||
module.exports = Keyword;
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
module.exports = function (tree) {
|
||||
var Ruleset = require("./ruleset.js"),
|
||||
Value = require("./value.js"),
|
||||
Element = require("./element.js"),
|
||||
Selector = require("./selector.js"),
|
||||
Anonymous = require("./anonymous.js"),
|
||||
Expression = require("./expression.js"),
|
||||
Directive = require("./directive.js");
|
||||
|
||||
var Media = function (value, features, index, currentFileInfo) {
|
||||
this.index = index;
|
||||
@@ -6,152 +12,150 @@ var Media = function (value, features, index, currentFileInfo) {
|
||||
|
||||
var selectors = this.emptySelectors();
|
||||
|
||||
this.features = new(tree.Value)(features);
|
||||
this.rules = [new(tree.Ruleset)(selectors, value)];
|
||||
this.features = new(Value)(features);
|
||||
this.rules = [new(Ruleset)(selectors, value)];
|
||||
this.rules[0].allowImports = true;
|
||||
};
|
||||
Media.prototype = {
|
||||
type: "Media",
|
||||
accept: function (visitor) {
|
||||
if (this.features) {
|
||||
this.features = visitor.visit(this.features);
|
||||
}
|
||||
if (this.rules) {
|
||||
this.rules = visitor.visitArray(this.rules);
|
||||
}
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
output.add('@media ', this.currentFileInfo, this.index);
|
||||
this.features.genCSS(env, output);
|
||||
tree.outputRuleset(env, output, this.rules);
|
||||
},
|
||||
toCSS: tree.toCSS,
|
||||
eval: function (env) {
|
||||
if (!env.mediaBlocks) {
|
||||
env.mediaBlocks = [];
|
||||
env.mediaPath = [];
|
||||
}
|
||||
|
||||
var media = new(Media)(null, [], this.index, this.currentFileInfo);
|
||||
if(this.debugInfo) {
|
||||
this.rules[0].debugInfo = this.debugInfo;
|
||||
media.debugInfo = this.debugInfo;
|
||||
}
|
||||
var strictMathBypass = false;
|
||||
if (!env.strictMath) {
|
||||
strictMathBypass = true;
|
||||
env.strictMath = true;
|
||||
}
|
||||
try {
|
||||
media.features = this.features.eval(env);
|
||||
}
|
||||
finally {
|
||||
if (strictMathBypass) {
|
||||
env.strictMath = false;
|
||||
}
|
||||
}
|
||||
|
||||
env.mediaPath.push(media);
|
||||
env.mediaBlocks.push(media);
|
||||
|
||||
env.frames.unshift(this.rules[0]);
|
||||
media.rules = [this.rules[0].eval(env)];
|
||||
env.frames.shift();
|
||||
|
||||
env.mediaPath.pop();
|
||||
|
||||
return env.mediaPath.length === 0 ? media.evalTop(env) :
|
||||
media.evalNested(env);
|
||||
},
|
||||
variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); },
|
||||
find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); },
|
||||
rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); },
|
||||
emptySelectors: function() {
|
||||
var el = new(tree.Element)('', '&', this.index, this.currentFileInfo),
|
||||
sels = [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)];
|
||||
sels[0].mediaEmpty = true;
|
||||
return sels;
|
||||
},
|
||||
markReferenced: function () {
|
||||
var i, rules = this.rules[0].rules;
|
||||
this.rules[0].markReferenced();
|
||||
this.isReferenced = true;
|
||||
for (i = 0; i < rules.length; i++) {
|
||||
if (rules[i].markReferenced) {
|
||||
rules[i].markReferenced();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
evalTop: function (env) {
|
||||
var result = this;
|
||||
|
||||
// Render all dependent Media blocks.
|
||||
if (env.mediaBlocks.length > 1) {
|
||||
var selectors = this.emptySelectors();
|
||||
result = new(tree.Ruleset)(selectors, env.mediaBlocks);
|
||||
result.multiMedia = true;
|
||||
}
|
||||
|
||||
delete env.mediaBlocks;
|
||||
delete env.mediaPath;
|
||||
|
||||
return result;
|
||||
},
|
||||
evalNested: function (env) {
|
||||
var i, value,
|
||||
path = env.mediaPath.concat([this]);
|
||||
|
||||
// Extract the media-query conditions separated with `,` (OR).
|
||||
for (i = 0; i < path.length; i++) {
|
||||
value = path[i].features instanceof tree.Value ?
|
||||
path[i].features.value : path[i].features;
|
||||
path[i] = Array.isArray(value) ? value : [value];
|
||||
}
|
||||
|
||||
// Trace all permutations to generate the resulting media-query.
|
||||
//
|
||||
// (a, b and c) with nested (d, e) ->
|
||||
// a and d
|
||||
// a and e
|
||||
// b and c and d
|
||||
// b and c and e
|
||||
this.features = new(tree.Value)(this.permute(path).map(function (path) {
|
||||
path = path.map(function (fragment) {
|
||||
return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment);
|
||||
});
|
||||
|
||||
for(i = path.length - 1; i > 0; i--) {
|
||||
path.splice(i, 0, new(tree.Anonymous)("and"));
|
||||
}
|
||||
|
||||
return new(tree.Expression)(path);
|
||||
}));
|
||||
|
||||
// Fake a tree-node that doesn't output anything.
|
||||
return new(tree.Ruleset)([], []);
|
||||
},
|
||||
permute: function (arr) {
|
||||
if (arr.length === 0) {
|
||||
return [];
|
||||
} else if (arr.length === 1) {
|
||||
return arr[0];
|
||||
} else {
|
||||
var result = [];
|
||||
var rest = this.permute(arr.slice(1));
|
||||
for (var i = 0; i < rest.length; i++) {
|
||||
for (var j = 0; j < arr[0].length; j++) {
|
||||
result.push([arr[0][j]].concat(rest[i]));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
},
|
||||
bubbleSelectors: function (selectors) {
|
||||
if (!selectors)
|
||||
return;
|
||||
this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])];
|
||||
Media.prototype = new Directive();
|
||||
Media.prototype.type = "Media";
|
||||
Media.prototype.isRulesetLike = true;
|
||||
Media.prototype.accept = function (visitor) {
|
||||
if (this.features) {
|
||||
this.features = visitor.visit(this.features);
|
||||
}
|
||||
if (this.rules) {
|
||||
this.rules = visitor.visitArray(this.rules);
|
||||
}
|
||||
};
|
||||
return Media;
|
||||
Media.prototype.genCSS = function (env, output) {
|
||||
output.add('@media ', this.currentFileInfo, this.index);
|
||||
this.features.genCSS(env, output);
|
||||
this.outputRuleset(env, output, this.rules);
|
||||
};
|
||||
Media.prototype.eval = function (env) {
|
||||
if (!env.mediaBlocks) {
|
||||
env.mediaBlocks = [];
|
||||
env.mediaPath = [];
|
||||
}
|
||||
|
||||
var media = new(Media)(null, [], this.index, this.currentFileInfo);
|
||||
if(this.debugInfo) {
|
||||
this.rules[0].debugInfo = this.debugInfo;
|
||||
media.debugInfo = this.debugInfo;
|
||||
}
|
||||
var strictMathBypass = false;
|
||||
if (!env.strictMath) {
|
||||
strictMathBypass = true;
|
||||
env.strictMath = true;
|
||||
}
|
||||
try {
|
||||
media.features = this.features.eval(env);
|
||||
}
|
||||
finally {
|
||||
if (strictMathBypass) {
|
||||
env.strictMath = false;
|
||||
}
|
||||
}
|
||||
|
||||
env.mediaPath.push(media);
|
||||
env.mediaBlocks.push(media);
|
||||
|
||||
env.frames.unshift(this.rules[0]);
|
||||
media.rules = [this.rules[0].eval(env)];
|
||||
env.frames.shift();
|
||||
|
||||
env.mediaPath.pop();
|
||||
|
||||
return env.mediaPath.length === 0 ? media.evalTop(env) :
|
||||
media.evalNested(env);
|
||||
};
|
||||
//TODO merge with directive
|
||||
Media.prototype.variable = function (name) { return Ruleset.prototype.variable.call(this.rules[0], name); };
|
||||
Media.prototype.find = function () { return Ruleset.prototype.find.apply(this.rules[0], arguments); };
|
||||
Media.prototype.rulesets = function () { return Ruleset.prototype.rulesets.apply(this.rules[0]); };
|
||||
Media.prototype.emptySelectors = function() {
|
||||
var el = new(Element)('', '&', this.index, this.currentFileInfo),
|
||||
sels = [new(Selector)([el], null, null, this.index, this.currentFileInfo)];
|
||||
sels[0].mediaEmpty = true;
|
||||
return sels;
|
||||
};
|
||||
Media.prototype.markReferenced = function () {
|
||||
var i, rules = this.rules[0].rules;
|
||||
this.rules[0].markReferenced();
|
||||
this.isReferenced = true;
|
||||
for (i = 0; i < rules.length; i++) {
|
||||
if (rules[i].markReferenced) {
|
||||
rules[i].markReferenced();
|
||||
}
|
||||
}
|
||||
};
|
||||
Media.prototype.evalTop = function (env) {
|
||||
var result = this;
|
||||
|
||||
// Render all dependent Media blocks.
|
||||
if (env.mediaBlocks.length > 1) {
|
||||
var selectors = this.emptySelectors();
|
||||
result = new(Ruleset)(selectors, env.mediaBlocks);
|
||||
result.multiMedia = true;
|
||||
}
|
||||
|
||||
delete env.mediaBlocks;
|
||||
delete env.mediaPath;
|
||||
|
||||
return result;
|
||||
};
|
||||
Media.prototype.evalNested = function (env) {
|
||||
var i, value,
|
||||
path = env.mediaPath.concat([this]);
|
||||
|
||||
// Extract the media-query conditions separated with `,` (OR).
|
||||
for (i = 0; i < path.length; i++) {
|
||||
value = path[i].features instanceof Value ?
|
||||
path[i].features.value : path[i].features;
|
||||
path[i] = Array.isArray(value) ? value : [value];
|
||||
}
|
||||
|
||||
// Trace all permutations to generate the resulting media-query.
|
||||
//
|
||||
// (a, b and c) with nested (d, e) ->
|
||||
// a and d
|
||||
// a and e
|
||||
// b and c and d
|
||||
// b and c and e
|
||||
this.features = new(Value)(this.permute(path).map(function (path) {
|
||||
path = path.map(function (fragment) {
|
||||
return fragment.toCSS ? fragment : new(Anonymous)(fragment);
|
||||
});
|
||||
|
||||
for(i = path.length - 1; i > 0; i--) {
|
||||
path.splice(i, 0, new(Anonymous)("and"));
|
||||
}
|
||||
|
||||
return new(Expression)(path);
|
||||
}));
|
||||
|
||||
// Fake a tree-node that doesn't output anything.
|
||||
return new(Ruleset)([], []);
|
||||
};
|
||||
Media.prototype.permute = function (arr) {
|
||||
if (arr.length === 0) {
|
||||
return [];
|
||||
} else if (arr.length === 1) {
|
||||
return arr[0];
|
||||
} else {
|
||||
var result = [];
|
||||
var rest = this.permute(arr.slice(1));
|
||||
for (var i = 0; i < rest.length; i++) {
|
||||
for (var j = 0; j < arr[0].length; j++) {
|
||||
result.push([arr[0][j]].concat(rest[i]));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
Media.prototype.bubbleSelectors = function (selectors) {
|
||||
if (!selectors)
|
||||
return;
|
||||
this.rules = [new(Ruleset)(selectors.slice(0), [this.rules[0]])];
|
||||
};
|
||||
module.exports = Media;
|
||||
|
||||
@@ -1,153 +1,154 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js"),
|
||||
Selector = require("./selector.js"),
|
||||
MixinDefinition = require("./mixin-definition.js"),
|
||||
defaultFunc = require("../functions/default.js");
|
||||
|
||||
var Call = function (elements, args, index, currentFileInfo, important) {
|
||||
this.selector = new(tree.Selector)(elements);
|
||||
var MixinCall = function (elements, args, index, currentFileInfo, important) {
|
||||
this.selector = new(Selector)(elements);
|
||||
this.arguments = (args && args.length) ? args : null;
|
||||
this.index = index;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
this.important = important;
|
||||
};
|
||||
Call.prototype = {
|
||||
type: "MixinCall",
|
||||
accept: function (visitor) {
|
||||
if (this.selector) {
|
||||
this.selector = visitor.visit(this.selector);
|
||||
}
|
||||
if (this.arguments) {
|
||||
this.arguments = visitor.visitArray(this.arguments);
|
||||
}
|
||||
},
|
||||
eval: function (env) {
|
||||
var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule,
|
||||
candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc,
|
||||
defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset;
|
||||
|
||||
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)) {
|
||||
originalRuleset = mixin.originalRuleset || mixin;
|
||||
mixin = new tree.mixin.Definition("", [], mixin.rules, null, false);
|
||||
mixin.originalRuleset = originalRuleset;
|
||||
}
|
||||
Array.prototype.push.apply(
|
||||
rules, mixin.evalCall(env, args, this.important).rules);
|
||||
} catch (e) {
|
||||
throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(', ') : "") + ")";
|
||||
MixinCall.prototype = new Node();
|
||||
MixinCall.prototype.type = "MixinCall";
|
||||
MixinCall.prototype.accept = function (visitor) {
|
||||
if (this.selector) {
|
||||
this.selector = visitor.visit(this.selector);
|
||||
}
|
||||
if (this.arguments) {
|
||||
this.arguments = visitor.visitArray(this.arguments);
|
||||
}
|
||||
};
|
||||
return Call;
|
||||
MixinCall.prototype.eval = function (env) {
|
||||
var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule,
|
||||
candidates = [], candidate, conditionResult = [],
|
||||
defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset;
|
||||
|
||||
args = this.arguments && this.arguments.map(function (a) {
|
||||
return { name: a.name, value: a.value.eval(env) };
|
||||
});
|
||||
|
||||
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 MixinDefinition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) {
|
||||
isRecursive = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isRecursive) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mixin.matchArgs(args, env)) {
|
||||
candidate = {mixin: mixin, group: defNone};
|
||||
|
||||
if (mixin.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 MixinDefinition)) {
|
||||
originalRuleset = mixin.originalRuleset || mixin;
|
||||
mixin = new MixinDefinition("", [], mixin.rules, null, false);
|
||||
mixin.originalRuleset = originalRuleset;
|
||||
}
|
||||
Array.prototype.push.apply(
|
||||
rules, mixin.evalCall(env, args, this.important).rules);
|
||||
} catch (e) {
|
||||
throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
if (!this.currentFileInfo || !this.currentFileInfo.reference) {
|
||||
for (i = 0; i < rules.length; i++) {
|
||||
rule = rules[i];
|
||||
if (rule.markReferenced) {
|
||||
rule.markReferenced();
|
||||
}
|
||||
}
|
||||
}
|
||||
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 };
|
||||
}
|
||||
};
|
||||
MixinCall.prototype.format = function (args) {
|
||||
return this.selector.toCSS().trim() + '(' +
|
||||
(args ? args.map(function (a) {
|
||||
var argValue = "";
|
||||
if (a.name) {
|
||||
argValue += a.name + ":";
|
||||
}
|
||||
if (a.value.toCSS) {
|
||||
argValue += a.value.toCSS();
|
||||
} else {
|
||||
argValue += "???";
|
||||
}
|
||||
return argValue;
|
||||
}).join(', ') : "") + ")";
|
||||
};
|
||||
module.exports = MixinCall;
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
module.exports = function (tree) {
|
||||
var Selector = require("./selector.js"),
|
||||
Element = require("./element.js"),
|
||||
Ruleset = require("./ruleset.js"),
|
||||
Rule = require("./rule.js"),
|
||||
Expression = require("./expression.js"),
|
||||
contexts = require("../env.js");
|
||||
|
||||
var Definition = function (name, params, rules, condition, variadic, frames) {
|
||||
this.name = name;
|
||||
this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])];
|
||||
this.selectors = [new(Selector)([new(Element)(null, name, this.index, this.currentFileInfo)])];
|
||||
this.params = params;
|
||||
this.condition = condition;
|
||||
this.variadic = variadic;
|
||||
@@ -13,153 +18,146 @@ var Definition = function (name, params, rules, condition, variadic, frames) {
|
||||
if (!p.name || (p.name && !p.value)) { return count + 1; }
|
||||
else { return count; }
|
||||
}, 0);
|
||||
this.parent = tree.Ruleset.prototype;
|
||||
this.frames = frames;
|
||||
};
|
||||
Definition.prototype = {
|
||||
type: "MixinDefinition",
|
||||
accept: function (visitor) {
|
||||
if (this.params && this.params.length) {
|
||||
this.params = visitor.visitArray(this.params);
|
||||
}
|
||||
this.rules = visitor.visitArray(this.rules);
|
||||
if (this.condition) {
|
||||
this.condition = visitor.visit(this.condition);
|
||||
}
|
||||
},
|
||||
variable: function (name) { return this.parent.variable.call(this, name); },
|
||||
variables: function () { return this.parent.variables.call(this); },
|
||||
find: function () { return this.parent.find.apply(this, arguments); },
|
||||
rulesets: function () { return this.parent.rulesets.apply(this); },
|
||||
|
||||
evalParams: function (env, mixinEnv, args, evaldArguments) {
|
||||
/*jshint boss:true */
|
||||
var frame = new(tree.Ruleset)(null, null),
|
||||
varargs, arg,
|
||||
params = this.params.slice(0),
|
||||
i, j, val, name, isNamedFound, argIndex, argsLength = 0;
|
||||
|
||||
mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames));
|
||||
|
||||
if (args) {
|
||||
args = args.slice(0);
|
||||
argsLength = args.length;
|
||||
|
||||
for(i = 0; i < argsLength; i++) {
|
||||
arg = args[i];
|
||||
if (name = (arg && arg.name)) {
|
||||
isNamedFound = false;
|
||||
for(j = 0; j < params.length; j++) {
|
||||
if (!evaldArguments[j] && name === params[j].name) {
|
||||
evaldArguments[j] = arg.value.eval(env);
|
||||
frame.prependRule(new(tree.Rule)(name, arg.value.eval(env)));
|
||||
isNamedFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isNamedFound) {
|
||||
args.splice(i, 1);
|
||||
i--;
|
||||
continue;
|
||||
} else {
|
||||
throw { type: 'Runtime', message: "Named argument for " + this.name +
|
||||
' ' + args[i].name + ' not found' };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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 ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables
|
||||
.concat(this.frames) // the parent namespace/mixin frames
|
||||
.concat(env.frames)))) { // the current environment frames
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
matchArgs: function (args, env) {
|
||||
var argsLength = (args && args.length) || 0, len;
|
||||
|
||||
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;
|
||||
Definition.prototype = new Ruleset();
|
||||
Definition.prototype.type = "MixinDefinition";
|
||||
Definition.prototype.evalFirst = true;
|
||||
Definition.prototype.accept = function (visitor) {
|
||||
if (this.params && this.params.length) {
|
||||
this.params = visitor.visitArray(this.params);
|
||||
}
|
||||
this.rules = visitor.visitArray(this.rules);
|
||||
if (this.condition) {
|
||||
this.condition = visitor.visit(this.condition);
|
||||
}
|
||||
};
|
||||
return Definition;
|
||||
Definition.prototype.evalParams = function (env, mixinEnv, args, evaldArguments) {
|
||||
/*jshint boss:true */
|
||||
var frame = new(Ruleset)(null, null),
|
||||
varargs, arg,
|
||||
params = this.params.slice(0),
|
||||
i, j, val, name, isNamedFound, argIndex, argsLength = 0;
|
||||
|
||||
mixinEnv = new contexts.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames));
|
||||
|
||||
if (args) {
|
||||
args = args.slice(0);
|
||||
argsLength = args.length;
|
||||
|
||||
for(i = 0; i < argsLength; i++) {
|
||||
arg = args[i];
|
||||
if (name = (arg && arg.name)) {
|
||||
isNamedFound = false;
|
||||
for(j = 0; j < params.length; j++) {
|
||||
if (!evaldArguments[j] && name === params[j].name) {
|
||||
evaldArguments[j] = arg.value.eval(env);
|
||||
frame.prependRule(new(Rule)(name, arg.value.eval(env)));
|
||||
isNamedFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isNamedFound) {
|
||||
args.splice(i, 1);
|
||||
i--;
|
||||
continue;
|
||||
} else {
|
||||
throw { type: 'Runtime', message: "Named argument for " + this.name +
|
||||
' ' + args[i].name + ' not found' };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
argIndex = 0;
|
||||
for (i = 0; i < params.length; i++) {
|
||||
if (evaldArguments[i]) { continue; }
|
||||
|
||||
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(Rule)(name, new(Expression)(varargs).eval(env)));
|
||||
} else {
|
||||
val = arg && arg.value;
|
||||
if (val) {
|
||||
val = val.eval(env);
|
||||
} else if (params[i].value) {
|
||||
val = params[i].value.eval(mixinEnv);
|
||||
frame.resetCache();
|
||||
} else {
|
||||
throw { type: 'Runtime', message: "wrong number of arguments for " + this.name +
|
||||
' (' + argsLength + ' for ' + this.arity + ')' };
|
||||
}
|
||||
|
||||
frame.prependRule(new(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;
|
||||
};
|
||||
Definition.prototype.eval = function (env) {
|
||||
return new Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0));
|
||||
};
|
||||
Definition.prototype.evalCall = function (env, args, important) {
|
||||
var _arguments = [],
|
||||
mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames,
|
||||
frame = this.evalParams(env, new(contexts.evalEnv)(env, mixinFrames), args, _arguments),
|
||||
rules, ruleset;
|
||||
|
||||
frame.prependRule(new(Rule)('@arguments', new(Expression)(_arguments).eval(env)));
|
||||
|
||||
rules = this.rules.slice(0);
|
||||
|
||||
ruleset = new(Ruleset)(null, rules);
|
||||
ruleset.originalRuleset = this;
|
||||
ruleset = ruleset.eval(new(contexts.evalEnv)(env, [this, frame].concat(mixinFrames)));
|
||||
if (important) {
|
||||
ruleset = this.makeImportant.apply(ruleset);
|
||||
}
|
||||
return ruleset;
|
||||
};
|
||||
Definition.prototype.matchCondition = function (args, env) {
|
||||
if (this.condition && !this.condition.eval(
|
||||
new(contexts.evalEnv)(env,
|
||||
[this.evalParams(env, new(contexts.evalEnv)(env, this.frames ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables
|
||||
.concat(this.frames) // the parent namespace/mixin frames
|
||||
.concat(env.frames)))) { // the current environment frames
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
Definition.prototype.matchArgs = function (args, env) {
|
||||
var argsLength = (args && args.length) || 0, len;
|
||||
|
||||
if (! this.variadic) {
|
||||
if (argsLength < this.required) { return false; }
|
||||
if (argsLength > this.params.length) { return false; }
|
||||
} else {
|
||||
if (argsLength < (this.required - 1)) { return false; }
|
||||
}
|
||||
|
||||
len = Math.min(argsLength, this.arity);
|
||||
|
||||
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;
|
||||
};
|
||||
module.exports = Definition;
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js"),
|
||||
Operation = require("./operation.js"),
|
||||
Dimension = require("./dimension.js");
|
||||
|
||||
var Negative = function (node) {
|
||||
this.value = node;
|
||||
};
|
||||
Negative.prototype = {
|
||||
type: "Negative",
|
||||
accept: function (visitor) {
|
||||
this.value = visitor.visit(this.value);
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
output.add('-');
|
||||
this.value.genCSS(env, output);
|
||||
},
|
||||
toCSS: tree.toCSS,
|
||||
eval: function (env) {
|
||||
if (env.isMathOn()) {
|
||||
return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env);
|
||||
}
|
||||
return new(Negative)(this.value.eval(env));
|
||||
Negative.prototype = new Node();
|
||||
Negative.prototype.type = "Negative";
|
||||
Negative.prototype.genCSS = function (env, output) {
|
||||
output.add('-');
|
||||
this.value.genCSS(env, output);
|
||||
};
|
||||
Negative.prototype.eval = function (env) {
|
||||
if (env.isMathOn()) {
|
||||
return (new(Operation)('*', [new(Dimension)(-1), this.value])).eval(env);
|
||||
}
|
||||
return new(Negative)(this.value.eval(env));
|
||||
};
|
||||
return Negative;
|
||||
};
|
||||
module.exports = Negative;
|
||||
|
||||
35
lib/less/tree/node.js
Normal file
35
lib/less/tree/node.js
Normal file
@@ -0,0 +1,35 @@
|
||||
var Node = function() {
|
||||
};
|
||||
Node.prototype.toCSS = function (env) {
|
||||
var strs = [];
|
||||
this.genCSS(env, {
|
||||
add: function(chunk, fileInfo, index) {
|
||||
strs.push(chunk);
|
||||
},
|
||||
isEmpty: function () {
|
||||
return strs.length === 0;
|
||||
}
|
||||
});
|
||||
return strs.join('');
|
||||
};
|
||||
Node.prototype.genCSS = function (env, output) {
|
||||
output.add(this.value);
|
||||
};
|
||||
Node.prototype.accept = function (visitor) {
|
||||
this.value = visitor.visit(this.value);
|
||||
};
|
||||
Node.prototype.eval = function () { return this; };
|
||||
Node.prototype._operate = function (env, op, a, b) {
|
||||
switch (op) {
|
||||
case '+': return a + b;
|
||||
case '-': return a - b;
|
||||
case '*': return a * b;
|
||||
case '/': return a / b;
|
||||
}
|
||||
};
|
||||
Node.prototype.fround = function(env, value) {
|
||||
var precision = env && env.numPrecision;
|
||||
//add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded...
|
||||
return (precision == null) ? value : Number((value + 2e-16).toFixed(precision));
|
||||
};
|
||||
module.exports = Node;
|
||||
@@ -1,60 +1,48 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js"),
|
||||
Color = require("./color.js"),
|
||||
Dimension = require("./dimension.js");
|
||||
|
||||
var Operation = function (op, operands, isSpaced) {
|
||||
this.op = op.trim();
|
||||
this.operands = operands;
|
||||
this.isSpaced = isSpaced;
|
||||
};
|
||||
Operation.prototype = {
|
||||
type: "Operation",
|
||||
accept: function (visitor) {
|
||||
this.operands = visitor.visit(this.operands);
|
||||
},
|
||||
eval: function (env) {
|
||||
var a = this.operands[0].eval(env),
|
||||
b = this.operands[1].eval(env);
|
||||
|
||||
if (env.isMathOn()) {
|
||||
if (a instanceof tree.Dimension && b instanceof tree.Color) {
|
||||
a = a.toColor();
|
||||
}
|
||||
if (b instanceof tree.Dimension && a instanceof tree.Color) {
|
||||
b = b.toColor();
|
||||
}
|
||||
if (!a.operate) {
|
||||
throw { type: "Operation",
|
||||
message: "Operation on an invalid type" };
|
||||
}
|
||||
|
||||
return a.operate(env, this.op, b);
|
||||
} else {
|
||||
return new(Operation)(this.op, [a, b], this.isSpaced);
|
||||
}
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
this.operands[0].genCSS(env, output);
|
||||
if (this.isSpaced) {
|
||||
output.add(" ");
|
||||
}
|
||||
output.add(this.op);
|
||||
if (this.isSpaced) {
|
||||
output.add(" ");
|
||||
}
|
||||
this.operands[1].genCSS(env, output);
|
||||
},
|
||||
toCSS: tree.toCSS
|
||||
Operation.prototype = new Node();
|
||||
Operation.prototype.type = "Operation";
|
||||
Operation.prototype.accept = function (visitor) {
|
||||
this.operands = visitor.visit(this.operands);
|
||||
};
|
||||
Operation.prototype.eval = function (env) {
|
||||
var a = this.operands[0].eval(env),
|
||||
b = this.operands[1].eval(env);
|
||||
|
||||
// todo move!
|
||||
tree.operate = function (env, op, a, b) {
|
||||
switch (op) {
|
||||
case '+': return a + b;
|
||||
case '-': return a - b;
|
||||
case '*': return a * b;
|
||||
case '/': return a / b;
|
||||
if (env.isMathOn()) {
|
||||
if (a instanceof Dimension && b instanceof Color) {
|
||||
a = a.toColor();
|
||||
}
|
||||
if (b instanceof Dimension && a instanceof Color) {
|
||||
b = b.toColor();
|
||||
}
|
||||
if (!a.operate) {
|
||||
throw { type: "Operation",
|
||||
message: "Operation on an invalid type" };
|
||||
}
|
||||
|
||||
return a.operate(env, this.op, b);
|
||||
} else {
|
||||
return new(Operation)(this.op, [a, b], this.isSpaced);
|
||||
}
|
||||
};
|
||||
|
||||
return Operation;
|
||||
|
||||
Operation.prototype.genCSS = function (env, output) {
|
||||
this.operands[0].genCSS(env, output);
|
||||
if (this.isSpaced) {
|
||||
output.add(" ");
|
||||
}
|
||||
output.add(this.op);
|
||||
if (this.isSpaced) {
|
||||
output.add(" ");
|
||||
}
|
||||
this.operands[1].genCSS(env, output);
|
||||
};
|
||||
|
||||
module.exports = Operation;
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js");
|
||||
|
||||
var Paren = function (node) {
|
||||
this.value = node;
|
||||
};
|
||||
Paren.prototype = {
|
||||
type: "Paren",
|
||||
accept: function (visitor) {
|
||||
this.value = visitor.visit(this.value);
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
output.add('(');
|
||||
this.value.genCSS(env, output);
|
||||
output.add(')');
|
||||
},
|
||||
toCSS: tree.toCSS,
|
||||
eval: function (env) {
|
||||
return new(Paren)(this.value.eval(env));
|
||||
}
|
||||
Paren.prototype = new Node();
|
||||
Paren.prototype.type = "Paren";
|
||||
Paren.prototype.genCSS = function (env, output) {
|
||||
output.add('(');
|
||||
this.value.genCSS(env, output);
|
||||
output.add(')');
|
||||
};
|
||||
return Paren;
|
||||
Paren.prototype.eval = function (env) {
|
||||
return new(Paren)(this.value.eval(env));
|
||||
};
|
||||
module.exports = Paren;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
module.exports = function (tree) {
|
||||
var JsEvalNode = require("./js-eval-node.js"),
|
||||
Variable = require("./variable.js");
|
||||
|
||||
var Quoted = function (str, content, escaped, index, currentFileInfo) {
|
||||
this.escaped = escaped;
|
||||
@@ -7,50 +8,47 @@ var Quoted = function (str, content, escaped, index, currentFileInfo) {
|
||||
this.index = index;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
};
|
||||
Quoted.prototype = {
|
||||
type: "Quoted",
|
||||
genCSS: function (env, output) {
|
||||
if (!this.escaped) {
|
||||
output.add(this.quote, this.currentFileInfo, this.index);
|
||||
}
|
||||
output.add(this.value);
|
||||
if (!this.escaped) {
|
||||
output.add(this.quote);
|
||||
}
|
||||
},
|
||||
toCSS: tree.toCSS,
|
||||
eval: function (env) {
|
||||
var that = this;
|
||||
var value = this.value.replace(/`([^`]+)`/g, function (_, exp) {
|
||||
return new(tree.JavaScript)(exp, that.index, true).eval(env).value;
|
||||
}).replace(/@\{([\w-]+)\}/g, function (_, name) {
|
||||
var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true);
|
||||
return (v instanceof tree.Quoted) ? v.value : v.toCSS();
|
||||
});
|
||||
return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo);
|
||||
},
|
||||
compare: function (x) {
|
||||
if (!x.toCSS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
var left, right;
|
||||
|
||||
// when comparing quoted strings allow the quote to differ
|
||||
if (x.type === "Quoted" && !this.escaped && !x.escaped) {
|
||||
left = x.value;
|
||||
right = this.value;
|
||||
} else {
|
||||
left = this.toCSS();
|
||||
right = x.toCSS();
|
||||
}
|
||||
|
||||
if (left === right) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return left < right ? -1 : 1;
|
||||
Quoted.prototype = new JsEvalNode();
|
||||
Quoted.prototype.type = "Quoted";
|
||||
Quoted.prototype.genCSS = function (env, output) {
|
||||
if (!this.escaped) {
|
||||
output.add(this.quote, this.currentFileInfo, this.index);
|
||||
}
|
||||
output.add(this.value);
|
||||
if (!this.escaped) {
|
||||
output.add(this.quote);
|
||||
}
|
||||
};
|
||||
return Quoted;
|
||||
Quoted.prototype.eval = function (env) {
|
||||
var that = this;
|
||||
var value = this.value.replace(/`([^`]+)`/g, function (_, exp) {
|
||||
return String(that.evaluateJavaScript(exp, env));
|
||||
}).replace(/@\{([\w-]+)\}/g, function (_, name) {
|
||||
var v = new(Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true);
|
||||
return (v instanceof Quoted) ? v.value : v.toCSS();
|
||||
});
|
||||
return new(Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo);
|
||||
};
|
||||
Quoted.prototype.compare = function (x) {
|
||||
if (!x.toCSS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
var left, right;
|
||||
|
||||
// when comparing quoted strings allow the quote to differ
|
||||
if (x.type === "Quoted" && !this.escaped && !x.escaped) {
|
||||
left = x.value;
|
||||
right = this.value;
|
||||
} else {
|
||||
left = this.toCSS();
|
||||
right = x.toCSS();
|
||||
}
|
||||
|
||||
if (left === right) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return left < right ? -1 : 1;
|
||||
};
|
||||
module.exports = Quoted;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js"),
|
||||
Value = require("./value.js"),
|
||||
Keyword = require("./keyword.js");
|
||||
|
||||
var Rule = function (name, value, important, merge, index, currentFileInfo, inline) {
|
||||
this.name = name;
|
||||
this.value = (value instanceof tree.Value || value instanceof tree.Ruleset) ? value : new(tree.Value)([value]);
|
||||
this.value = (value instanceof Node) ? value : new(Value)([value]); //value instanceof tree.Value || value instanceof tree.Ruleset ??
|
||||
this.important = important ? ' ' + important.trim() : '';
|
||||
this.merge = merge;
|
||||
this.index = index;
|
||||
@@ -11,73 +13,6 @@ var Rule = function (name, value, important, merge, index, currentFileInfo, inli
|
||||
this.variable = name.charAt && (name.charAt(0) === '@');
|
||||
};
|
||||
|
||||
Rule.prototype = {
|
||||
type: "Rule",
|
||||
accept: function (visitor) {
|
||||
this.value = visitor.visit(this.value);
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index);
|
||||
try {
|
||||
this.value.genCSS(env, output);
|
||||
}
|
||||
catch(e) {
|
||||
e.index = this.index;
|
||||
e.filename = this.currentFileInfo.filename;
|
||||
throw e;
|
||||
}
|
||||
output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index);
|
||||
},
|
||||
toCSS: tree.toCSS,
|
||||
eval: function (env) {
|
||||
var strictMathBypass = false, name = this.name, evaldValue;
|
||||
if (typeof name !== "string") {
|
||||
// expand 'primitive' name directly to get
|
||||
// things faster (~10% for benchmark.less):
|
||||
name = (name.length === 1)
|
||||
&& (name[0] instanceof tree.Keyword)
|
||||
? name[0].value : evalName(env, name);
|
||||
}
|
||||
if (name === "font" && !env.strictMath) {
|
||||
strictMathBypass = true;
|
||||
env.strictMath = true;
|
||||
}
|
||||
try {
|
||||
evaldValue = this.value.eval(env);
|
||||
|
||||
if (!this.variable && evaldValue.type === "DetachedRuleset") {
|
||||
throw { message: "Rulesets cannot be evaluated on a property.",
|
||||
index: this.index, filename: this.currentFileInfo.filename };
|
||||
}
|
||||
|
||||
return new(Rule)(name,
|
||||
evaldValue,
|
||||
this.important,
|
||||
this.merge,
|
||||
this.index, this.currentFileInfo, this.inline);
|
||||
}
|
||||
catch(e) {
|
||||
if (typeof e.index !== 'number') {
|
||||
e.index = this.index;
|
||||
e.filename = this.currentFileInfo.filename;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
if (strictMathBypass) {
|
||||
env.strictMath = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
makeImportant: function () {
|
||||
return new(Rule)(this.name,
|
||||
this.value,
|
||||
"!important",
|
||||
this.merge,
|
||||
this.index, this.currentFileInfo, this.inline);
|
||||
}
|
||||
};
|
||||
|
||||
function evalName(env, name) {
|
||||
var value = "", i, n = name.length,
|
||||
output = {add: function (s) {value += s;}};
|
||||
@@ -86,7 +21,67 @@ function evalName(env, name) {
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
return Rule;
|
||||
|
||||
Rule.prototype = new Node();
|
||||
Rule.prototype.type = "Rule";
|
||||
Rule.prototype.genCSS = function (env, output) {
|
||||
output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index);
|
||||
try {
|
||||
this.value.genCSS(env, output);
|
||||
}
|
||||
catch(e) {
|
||||
e.index = this.index;
|
||||
e.filename = this.currentFileInfo.filename;
|
||||
throw e;
|
||||
}
|
||||
output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index);
|
||||
};
|
||||
Rule.prototype.eval = function (env) {
|
||||
var strictMathBypass = false, name = this.name, evaldValue;
|
||||
if (typeof name !== "string") {
|
||||
// expand 'primitive' name directly to get
|
||||
// things faster (~10% for benchmark.less):
|
||||
name = (name.length === 1)
|
||||
&& (name[0] instanceof Keyword)
|
||||
? name[0].value : evalName(env, name);
|
||||
}
|
||||
if (name === "font" && !env.strictMath) {
|
||||
strictMathBypass = true;
|
||||
env.strictMath = true;
|
||||
}
|
||||
try {
|
||||
evaldValue = this.value.eval(env);
|
||||
|
||||
if (!this.variable && evaldValue.type === "DetachedRuleset") {
|
||||
throw { message: "Rulesets cannot be evaluated on a property.",
|
||||
index: this.index, filename: this.currentFileInfo.filename };
|
||||
}
|
||||
|
||||
return new(Rule)(name,
|
||||
evaldValue,
|
||||
this.important,
|
||||
this.merge,
|
||||
this.index, this.currentFileInfo, this.inline);
|
||||
}
|
||||
catch(e) {
|
||||
if (typeof e.index !== 'number') {
|
||||
e.index = this.index;
|
||||
e.filename = this.currentFileInfo.filename;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
if (strictMathBypass) {
|
||||
env.strictMath = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
Rule.prototype.makeImportant = function () {
|
||||
return new(Rule)(this.name,
|
||||
this.value,
|
||||
"!important",
|
||||
this.merge,
|
||||
this.index, this.currentFileInfo, this.inline);
|
||||
};
|
||||
|
||||
module.exports = Rule;
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js"),
|
||||
Variable = require("./variable.js");
|
||||
|
||||
var RulesetCall = function (variable) {
|
||||
this.variable = variable;
|
||||
};
|
||||
RulesetCall.prototype = {
|
||||
type: "RulesetCall",
|
||||
accept: function (visitor) {
|
||||
},
|
||||
eval: function (env) {
|
||||
var detachedRuleset = new(tree.Variable)(this.variable).eval(env);
|
||||
return detachedRuleset.callEval(env);
|
||||
}
|
||||
};
|
||||
return RulesetCall;
|
||||
RulesetCall.prototype = new Node();
|
||||
RulesetCall.prototype.type = "RulesetCall";
|
||||
RulesetCall.prototype.eval = function (env) {
|
||||
var detachedRuleset = new(Variable)(this.variable).eval(env);
|
||||
return detachedRuleset.callEval(env);
|
||||
};
|
||||
module.exports = RulesetCall;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js");
|
||||
|
||||
var Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) {
|
||||
this.elements = elements;
|
||||
@@ -10,120 +10,117 @@ var Selector = function (elements, extendList, condition, index, currentFileInfo
|
||||
this.evaldCondition = true;
|
||||
}
|
||||
};
|
||||
Selector.prototype = {
|
||||
type: "Selector",
|
||||
accept: function (visitor) {
|
||||
if (this.elements) {
|
||||
this.elements = visitor.visitArray(this.elements);
|
||||
}
|
||||
if (this.extendList) {
|
||||
this.extendList = visitor.visitArray(this.extendList);
|
||||
}
|
||||
if (this.condition) {
|
||||
this.condition = visitor.visit(this.condition);
|
||||
}
|
||||
},
|
||||
createDerived: function(elements, extendList, evaldCondition) {
|
||||
evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition;
|
||||
var newSelector = new(Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced);
|
||||
newSelector.evaldCondition = evaldCondition;
|
||||
newSelector.mediaEmpty = this.mediaEmpty;
|
||||
return newSelector;
|
||||
},
|
||||
match: function (other) {
|
||||
var elements = this.elements,
|
||||
len = elements.length,
|
||||
olen, i;
|
||||
|
||||
other.CacheElements();
|
||||
|
||||
olen = other._elements.length;
|
||||
if (olen === 0 || len < olen) {
|
||||
return 0;
|
||||
} else {
|
||||
for (i = 0; i < olen; i++) {
|
||||
if (elements[i].value !== other._elements[i]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return olen; // return number of matched elements
|
||||
},
|
||||
CacheElements: function(){
|
||||
var css = '', len, v, i;
|
||||
|
||||
if( !this._elements ){
|
||||
|
||||
len = this.elements.length;
|
||||
for(i = 0; i < len; i++){
|
||||
|
||||
v = this.elements[i];
|
||||
css += v.combinator.value;
|
||||
|
||||
if( !v.value.value ){
|
||||
css += v.value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( typeof v.value.value !== "string" ){
|
||||
css = '';
|
||||
break;
|
||||
}
|
||||
css += v.value.value;
|
||||
}
|
||||
|
||||
this._elements = css.match(/[,&#\*\.\w-]([\w-]|(\\.))*/g);
|
||||
|
||||
if (this._elements) {
|
||||
if (this._elements[0] === "&") {
|
||||
this._elements.shift();
|
||||
}
|
||||
|
||||
} else {
|
||||
this._elements = [];
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
isJustParentSelector: function() {
|
||||
return !this.mediaEmpty &&
|
||||
this.elements.length === 1 &&
|
||||
this.elements[0].value === '&' &&
|
||||
(this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === '');
|
||||
},
|
||||
eval: function (env) {
|
||||
var evaldCondition = this.condition && this.condition.eval(env),
|
||||
elements = this.elements, extendList = this.extendList;
|
||||
|
||||
elements = elements && elements.map(function (e) { return e.eval(env); });
|
||||
extendList = extendList && extendList.map(function(extend) { return extend.eval(env); });
|
||||
|
||||
return this.createDerived(elements, extendList, evaldCondition);
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
var i, element;
|
||||
if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") {
|
||||
output.add(' ', this.currentFileInfo, this.index);
|
||||
}
|
||||
if (!this._css) {
|
||||
//TODO caching? speed comparison?
|
||||
for(i = 0; i < this.elements.length; i++) {
|
||||
element = this.elements[i];
|
||||
element.genCSS(env, output);
|
||||
}
|
||||
}
|
||||
},
|
||||
toCSS: tree.toCSS,
|
||||
markReferenced: function () {
|
||||
this.isReferenced = true;
|
||||
},
|
||||
getIsReferenced: function() {
|
||||
return !this.currentFileInfo.reference || this.isReferenced;
|
||||
},
|
||||
getIsOutput: function() {
|
||||
return this.evaldCondition;
|
||||
Selector.prototype = new Node();
|
||||
Selector.prototype.type = "Selector";
|
||||
Selector.prototype.accept = function (visitor) {
|
||||
if (this.elements) {
|
||||
this.elements = visitor.visitArray(this.elements);
|
||||
}
|
||||
if (this.extendList) {
|
||||
this.extendList = visitor.visitArray(this.extendList);
|
||||
}
|
||||
if (this.condition) {
|
||||
this.condition = visitor.visit(this.condition);
|
||||
}
|
||||
};
|
||||
return Selector;
|
||||
Selector.prototype.createDerived = function(elements, extendList, evaldCondition) {
|
||||
evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition;
|
||||
var newSelector = new(Selector)(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, this.isReferenced);
|
||||
newSelector.evaldCondition = evaldCondition;
|
||||
newSelector.mediaEmpty = this.mediaEmpty;
|
||||
return newSelector;
|
||||
};
|
||||
Selector.prototype.match = function (other) {
|
||||
var elements = this.elements,
|
||||
len = elements.length,
|
||||
olen, i;
|
||||
|
||||
other.CacheElements();
|
||||
|
||||
olen = other._elements.length;
|
||||
if (olen === 0 || len < olen) {
|
||||
return 0;
|
||||
} else {
|
||||
for (i = 0; i < olen; i++) {
|
||||
if (elements[i].value !== other._elements[i]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return olen; // return number of matched elements
|
||||
};
|
||||
Selector.prototype.CacheElements = function(){
|
||||
var css = '', len, v, i;
|
||||
|
||||
if( !this._elements ){
|
||||
|
||||
len = this.elements.length;
|
||||
for(i = 0; i < len; i++){
|
||||
|
||||
v = this.elements[i];
|
||||
css += v.combinator.value;
|
||||
|
||||
if( !v.value.value ){
|
||||
css += v.value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( typeof v.value.value !== "string" ){
|
||||
css = '';
|
||||
break;
|
||||
}
|
||||
css += v.value.value;
|
||||
}
|
||||
|
||||
this._elements = css.match(/[,&#\*\.\w-]([\w-]|(\\.))*/g);
|
||||
|
||||
if (this._elements) {
|
||||
if (this._elements[0] === "&") {
|
||||
this._elements.shift();
|
||||
}
|
||||
|
||||
} else {
|
||||
this._elements = [];
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
Selector.prototype.isJustParentSelector = function() {
|
||||
return !this.mediaEmpty &&
|
||||
this.elements.length === 1 &&
|
||||
this.elements[0].value === '&' &&
|
||||
(this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === '');
|
||||
};
|
||||
Selector.prototype.eval = function (env) {
|
||||
var evaldCondition = this.condition && this.condition.eval(env),
|
||||
elements = this.elements, extendList = this.extendList;
|
||||
|
||||
elements = elements && elements.map(function (e) { return e.eval(env); });
|
||||
extendList = extendList && extendList.map(function(extend) { return extend.eval(env); });
|
||||
|
||||
return this.createDerived(elements, extendList, evaldCondition);
|
||||
};
|
||||
Selector.prototype.genCSS = function (env, output) {
|
||||
var i, element;
|
||||
if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") {
|
||||
output.add(' ', this.currentFileInfo, this.index);
|
||||
}
|
||||
if (!this._css) {
|
||||
//TODO caching? speed comparison?
|
||||
for(i = 0; i < this.elements.length; i++) {
|
||||
element = this.elements[i];
|
||||
element.genCSS(env, output);
|
||||
}
|
||||
}
|
||||
};
|
||||
Selector.prototype.markReferenced = function () {
|
||||
this.isReferenced = true;
|
||||
};
|
||||
Selector.prototype.getIsReferenced = function() {
|
||||
return !this.currentFileInfo.reference || this.isReferenced;
|
||||
};
|
||||
Selector.prototype.getIsOutput = function() {
|
||||
return this.evaldCondition;
|
||||
};
|
||||
module.exports = Selector;
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js");
|
||||
|
||||
var UnicodeDescriptor = function (value) {
|
||||
this.value = value;
|
||||
};
|
||||
UnicodeDescriptor.prototype = {
|
||||
type: "UnicodeDescriptor",
|
||||
genCSS: function (env, output) {
|
||||
output.add(this.value);
|
||||
},
|
||||
toCSS: tree.toCSS,
|
||||
eval: function () { return this; }
|
||||
};
|
||||
return UnicodeDescriptor;
|
||||
};
|
||||
UnicodeDescriptor.prototype = new Node();
|
||||
UnicodeDescriptor.prototype.type = "UnicodeDescriptor";
|
||||
|
||||
module.exports = UnicodeDescriptor;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
module.exports = function(tree, unitConversions) {
|
||||
var Node = require("./node.js"),
|
||||
unitConversions = require("../data/unit-conversions.js");
|
||||
|
||||
var Unit = function (numerator, denominator, backupUnit) {
|
||||
this.numerator = numerator ? numerator.slice(0).sort() : [];
|
||||
@@ -6,132 +7,120 @@ var Unit = function (numerator, denominator, backupUnit) {
|
||||
this.backupUnit = backupUnit;
|
||||
};
|
||||
|
||||
Unit.prototype = {
|
||||
type: "Unit",
|
||||
clone: function () {
|
||||
return new Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit);
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
if (this.numerator.length >= 1) {
|
||||
output.add(this.numerator[0]);
|
||||
} else
|
||||
if (this.denominator.length >= 1) {
|
||||
output.add(this.denominator[0]);
|
||||
} else
|
||||
if ((!env || !env.strictUnits) && this.backupUnit) {
|
||||
output.add(this.backupUnit);
|
||||
}
|
||||
},
|
||||
toCSS: tree.toCSS,
|
||||
Unit.prototype = new Node();
|
||||
Unit.prototype.type = "Unit";
|
||||
Unit.prototype.clone = function () {
|
||||
return new Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit);
|
||||
};
|
||||
Unit.prototype.genCSS = function (env, output) {
|
||||
if (this.numerator.length >= 1) {
|
||||
output.add(this.numerator[0]);
|
||||
} else
|
||||
if (this.denominator.length >= 1) {
|
||||
output.add(this.denominator[0]);
|
||||
} else
|
||||
if ((!env || !env.strictUnits) && this.backupUnit) {
|
||||
output.add(this.backupUnit);
|
||||
}
|
||||
};
|
||||
Unit.prototype.toString = function () {
|
||||
var i, returnStr = this.numerator.join("*");
|
||||
for (i = 0; i < this.denominator.length; i++) {
|
||||
returnStr += "/" + this.denominator[i];
|
||||
}
|
||||
return returnStr;
|
||||
};
|
||||
Unit.prototype.compare = function (other) {
|
||||
return this.is(other.toString()) ? 0 : -1;
|
||||
};
|
||||
Unit.prototype.is = function (unitString) {
|
||||
return this.toString() === unitString;
|
||||
};
|
||||
Unit.prototype.isLength = function () {
|
||||
return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/));
|
||||
};
|
||||
Unit.prototype.isEmpty = function () {
|
||||
return this.numerator.length === 0 && this.denominator.length === 0;
|
||||
};
|
||||
Unit.prototype.isSingular = function() {
|
||||
return this.numerator.length <= 1 && this.denominator.length === 0;
|
||||
};
|
||||
Unit.prototype.map = function(callback) {
|
||||
var i;
|
||||
|
||||
toString: function () {
|
||||
var i, returnStr = this.numerator.join("*");
|
||||
for (i = 0; i < this.denominator.length; i++) {
|
||||
returnStr += "/" + this.denominator[i];
|
||||
}
|
||||
return returnStr;
|
||||
},
|
||||
for (i = 0; i < this.numerator.length; i++) {
|
||||
this.numerator[i] = callback(this.numerator[i], false);
|
||||
}
|
||||
|
||||
compare: function (other) {
|
||||
return this.is(other.toString()) ? 0 : -1;
|
||||
},
|
||||
for (i = 0; i < this.denominator.length; i++) {
|
||||
this.denominator[i] = callback(this.denominator[i], true);
|
||||
}
|
||||
};
|
||||
Unit.prototype.usedUnits = function() {
|
||||
var group, result = {}, mapUnit;
|
||||
|
||||
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);
|
||||
mapUnit = function (atomicUnit) {
|
||||
/*jshint loopfunc:true */
|
||||
if (group.hasOwnProperty(atomicUnit) && !result[groupName]) {
|
||||
result[groupName] = atomicUnit;
|
||||
}
|
||||
|
||||
for (i = 0; i < this.denominator.length; i++) {
|
||||
this.denominator[i] = callback(this.denominator[i], true);
|
||||
return atomicUnit;
|
||||
};
|
||||
|
||||
for (var groupName in unitConversions) {
|
||||
if (unitConversions.hasOwnProperty(groupName)) {
|
||||
group = unitConversions[groupName];
|
||||
|
||||
this.map(mapUnit);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
usedUnits: function() {
|
||||
var group, result = {}, mapUnit;
|
||||
return result;
|
||||
};
|
||||
Unit.prototype.cancel = function () {
|
||||
var counter = {}, atomicUnit, i, backup;
|
||||
|
||||
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);
|
||||
}
|
||||
for (i = 0; i < this.numerator.length; i++) {
|
||||
atomicUnit = this.numerator[i];
|
||||
if (!backup) {
|
||||
backup = atomicUnit;
|
||||
}
|
||||
counter[atomicUnit] = (counter[atomicUnit] || 0) + 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 = [];
|
||||
|
||||
this.numerator = [];
|
||||
this.denominator = [];
|
||||
for (atomicUnit in counter) {
|
||||
if (counter.hasOwnProperty(atomicUnit)) {
|
||||
var count = counter[atomicUnit];
|
||||
|
||||
for (atomicUnit in counter) {
|
||||
if (counter.hasOwnProperty(atomicUnit)) {
|
||||
var count = counter[atomicUnit];
|
||||
|
||||
if (count > 0) {
|
||||
for (i = 0; i < count; i++) {
|
||||
this.numerator.push(atomicUnit);
|
||||
}
|
||||
} else if (count < 0) {
|
||||
for (i = 0; i < -count; i++) {
|
||||
this.denominator.push(atomicUnit);
|
||||
}
|
||||
if (count > 0) {
|
||||
for (i = 0; i < count; i++) {
|
||||
this.numerator.push(atomicUnit);
|
||||
}
|
||||
} else if (count < 0) {
|
||||
for (i = 0; i < -count; i++) {
|
||||
this.denominator.push(atomicUnit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.numerator.length === 0 && this.denominator.length === 0 && backup) {
|
||||
this.backupUnit = backup;
|
||||
}
|
||||
|
||||
this.numerator.sort();
|
||||
this.denominator.sort();
|
||||
}
|
||||
|
||||
if (this.numerator.length === 0 && this.denominator.length === 0 && backup) {
|
||||
this.backupUnit = backup;
|
||||
}
|
||||
|
||||
this.numerator.sort();
|
||||
this.denominator.sort();
|
||||
};
|
||||
return Unit;
|
||||
};
|
||||
module.exports = Unit;
|
||||
|
||||
@@ -1,53 +1,50 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js");
|
||||
|
||||
var URL = function (val, currentFileInfo, isEvald) {
|
||||
this.value = val;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
this.isEvald = isEvald;
|
||||
};
|
||||
URL.prototype = {
|
||||
type: "Url",
|
||||
accept: function (visitor) {
|
||||
this.value = visitor.visit(this.value);
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
output.add("url(");
|
||||
this.value.genCSS(env, output);
|
||||
output.add(")");
|
||||
},
|
||||
toCSS: tree.toCSS,
|
||||
eval: function (ctx) {
|
||||
var val = this.value.eval(ctx),
|
||||
rootpath;
|
||||
URL.prototype = new Node();
|
||||
URL.prototype.type = "Url";
|
||||
URL.prototype.accept = function (visitor) {
|
||||
this.value = visitor.visit(this.value);
|
||||
};
|
||||
URL.prototype.genCSS = function (env, output) {
|
||||
output.add("url(");
|
||||
this.value.genCSS(env, output);
|
||||
output.add(")");
|
||||
};
|
||||
URL.prototype.eval = function (ctx) {
|
||||
var val = this.value.eval(ctx),
|
||||
rootpath;
|
||||
|
||||
if (!this.isEvald) {
|
||||
// Add the base path if the URL is relative
|
||||
rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
|
||||
if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) {
|
||||
if (!val.quote) {
|
||||
rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; });
|
||||
}
|
||||
val.value = rootpath + val.value;
|
||||
if (!this.isEvald) {
|
||||
// Add the base path if the URL is relative
|
||||
rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
|
||||
if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) {
|
||||
if (!val.quote) {
|
||||
rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; });
|
||||
}
|
||||
val.value = rootpath + val.value;
|
||||
}
|
||||
|
||||
val.value = ctx.normalizePath(val.value);
|
||||
val.value = ctx.normalizePath(val.value);
|
||||
|
||||
// Add url args if enabled
|
||||
if (ctx.urlArgs) {
|
||||
if (!val.value.match(/^\s*data:/)) {
|
||||
var delimiter = val.value.indexOf('?') === -1 ? '?' : '&';
|
||||
var urlArgs = delimiter + ctx.urlArgs;
|
||||
if (val.value.indexOf('#') !== -1) {
|
||||
val.value = val.value.replace('#', urlArgs + '#');
|
||||
} else {
|
||||
val.value += urlArgs;
|
||||
}
|
||||
// Add url args if enabled
|
||||
if (ctx.urlArgs) {
|
||||
if (!val.value.match(/^\s*data:/)) {
|
||||
var delimiter = val.value.indexOf('?') === -1 ? '?' : '&';
|
||||
var urlArgs = delimiter + ctx.urlArgs;
|
||||
if (val.value.indexOf('#') !== -1) {
|
||||
val.value = val.value.replace('#', urlArgs + '#');
|
||||
} else {
|
||||
val.value += urlArgs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new(URL)(val, this.currentFileInfo, true);
|
||||
}
|
||||
|
||||
return new(URL)(val, this.currentFileInfo, true);
|
||||
};
|
||||
return URL;
|
||||
};
|
||||
module.exports = URL;
|
||||
|
||||
@@ -1,34 +1,31 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js");
|
||||
|
||||
var Value = function (value) {
|
||||
this.value = value;
|
||||
};
|
||||
Value.prototype = {
|
||||
type: "Value",
|
||||
accept: function (visitor) {
|
||||
if (this.value) {
|
||||
this.value = visitor.visitArray(this.value);
|
||||
}
|
||||
},
|
||||
eval: function (env) {
|
||||
if (this.value.length === 1) {
|
||||
return this.value[0].eval(env);
|
||||
} else {
|
||||
return new(Value)(this.value.map(function (v) {
|
||||
return v.eval(env);
|
||||
}));
|
||||
}
|
||||
},
|
||||
genCSS: function (env, output) {
|
||||
var i;
|
||||
for(i = 0; i < this.value.length; i++) {
|
||||
this.value[i].genCSS(env, output);
|
||||
if (i+1 < this.value.length) {
|
||||
output.add((env && env.compress) ? ',' : ', ');
|
||||
}
|
||||
}
|
||||
},
|
||||
toCSS: tree.toCSS
|
||||
Value.prototype = new Node();
|
||||
Value.prototype.type = "Value";
|
||||
Value.prototype.accept = function (visitor) {
|
||||
if (this.value) {
|
||||
this.value = visitor.visitArray(this.value);
|
||||
}
|
||||
};
|
||||
return Value;
|
||||
Value.prototype.eval = function (env) {
|
||||
if (this.value.length === 1) {
|
||||
return this.value[0].eval(env);
|
||||
} else {
|
||||
return new(Value)(this.value.map(function (v) {
|
||||
return v.eval(env);
|
||||
}));
|
||||
}
|
||||
};
|
||||
Value.prototype.genCSS = function (env, output) {
|
||||
var i;
|
||||
for(i = 0; i < this.value.length; i++) {
|
||||
this.value[i].genCSS(env, output);
|
||||
if (i+1 < this.value.length) {
|
||||
output.add((env && env.compress) ? ',' : ', ');
|
||||
}
|
||||
}
|
||||
};
|
||||
module.exports = Value;
|
||||
|
||||
@@ -1,44 +1,49 @@
|
||||
module.exports = function (tree) {
|
||||
var Node = require("./node.js");
|
||||
|
||||
var Variable = function (name, index, currentFileInfo) {
|
||||
this.name = name;
|
||||
this.index = index;
|
||||
this.currentFileInfo = currentFileInfo || {};
|
||||
};
|
||||
Variable.prototype = {
|
||||
type: "Variable",
|
||||
eval: function (env) {
|
||||
var variable, name = this.name;
|
||||
Variable.prototype = new Node();
|
||||
Variable.prototype.type = "Variable";
|
||||
Variable.prototype.eval = function (env) {
|
||||
var variable, name = this.name;
|
||||
|
||||
if (name.indexOf('@@') === 0) {
|
||||
name = '@' + new(Variable)(name.slice(1)).eval(env).value;
|
||||
}
|
||||
|
||||
if (this.evaluating) {
|
||||
throw { type: 'Name',
|
||||
message: "Recursive variable definition for " + name,
|
||||
filename: this.currentFileInfo.file,
|
||||
index: this.index };
|
||||
}
|
||||
|
||||
this.evaluating = true;
|
||||
|
||||
variable = tree.find(env.frames, function (frame) {
|
||||
var v = frame.variable(name);
|
||||
if (v) {
|
||||
return v.value.eval(env);
|
||||
}
|
||||
});
|
||||
if (variable) {
|
||||
this.evaluating = false;
|
||||
return variable;
|
||||
} else {
|
||||
throw { type: 'Name',
|
||||
message: "variable " + name + " is undefined",
|
||||
filename: this.currentFileInfo.filename,
|
||||
index: this.index };
|
||||
if (name.indexOf('@@') === 0) {
|
||||
name = '@' + new(Variable)(name.slice(1)).eval(env).value;
|
||||
}
|
||||
|
||||
if (this.evaluating) {
|
||||
throw { type: 'Name',
|
||||
message: "Recursive variable definition for " + name,
|
||||
filename: this.currentFileInfo.file,
|
||||
index: this.index };
|
||||
}
|
||||
|
||||
this.evaluating = true;
|
||||
|
||||
variable = this.find(env.frames, function (frame) {
|
||||
var v = frame.variable(name);
|
||||
if (v) {
|
||||
return v.value.eval(env);
|
||||
}
|
||||
});
|
||||
if (variable) {
|
||||
this.evaluating = false;
|
||||
return variable;
|
||||
} else {
|
||||
throw { type: 'Name',
|
||||
message: "variable " + name + " is undefined",
|
||||
filename: this.currentFileInfo.filename,
|
||||
index: this.index };
|
||||
}
|
||||
};
|
||||
return Variable;
|
||||
Variable.prototype.find = function (obj, fun) {
|
||||
for (var i = 0, r; i < obj.length; i++) {
|
||||
r = fun.call(obj, obj[i]);
|
||||
if (r) { return r; }
|
||||
}
|
||||
return null;
|
||||
};
|
||||
module.exports = Variable;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
var contexts = require("../env.js");
|
||||
module.exports = function (visitor, tree) {
|
||||
var ImportVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) {
|
||||
this._visitor = new visitor(this);
|
||||
this._importer = importer;
|
||||
this._finish = finish;
|
||||
this.env = evalEnv || new tree.evalEnv();
|
||||
this.env = evalEnv || new contexts.evalEnv();
|
||||
this.importCount = 0;
|
||||
this.onceFileDetectionMap = onceFileDetectionMap || {};
|
||||
this.recursionDetector = {};
|
||||
@@ -54,7 +55,7 @@ module.exports = function (visitor, tree) {
|
||||
if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) {
|
||||
importNode = evaldImportNode;
|
||||
this.importCount++;
|
||||
var env = new tree.evalEnv(this.env, this.env.frames.slice(0));
|
||||
var env = new contexts.evalEnv(this.env, this.env.frames.slice(0));
|
||||
|
||||
if (importNode.options.multiple) {
|
||||
env.importMultiple = true;
|
||||
|
||||
Reference in New Issue
Block a user