This commit is contained in:
Miles
2011-08-03 14:45:17 -07:00
8 changed files with 280 additions and 57 deletions

View File

@@ -1,8 +1,8 @@
//
// LESS - Leaner CSS v1.0.44
// LESS - Leaner CSS v1.1.4
// http://lesscss.org
//
// Copyright (c) 2010, Alexis Sellier
// Copyright (c) 2009-2011, Alexis Sellier
// Licensed under the Apache 2.0 License.
//
(function (window, undefined) {
@@ -592,11 +592,15 @@ less.Parser = function Parser(env) {
// "milky way" 'he\'s the one!'
//
quoted: function () {
var str;
if (input.charAt(i) !== '"' && input.charAt(i) !== "'") return;
var str, j = i, e;
if (input.charAt(j) === '~') { j++, e = true } // Escaped strings
if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return;
e && $('~');
if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) {
return new(tree.Quoted)(str[0], str[1] || str[2]);
return new(tree.Quoted)(str[0], str[1] || str[2], e);
}
},
@@ -621,7 +625,7 @@ less.Parser = function Parser(env) {
// The arguments are parsed with the `entities.arguments` parser.
//
call: function () {
var name, args;
var name, args, index = i;
if (! (name = /^([\w-]+|%)\(/.exec(chunks[j]))) return;
@@ -638,7 +642,7 @@ less.Parser = function Parser(env) {
if (! $(')')) return;
if (name) { return new(tree.Call)(name, args) }
if (name) { return new(tree.Call)(name, args, index) }
},
arguments: function () {
var args = [], arg;
@@ -667,7 +671,7 @@ less.Parser = function Parser(env) {
if (input.charAt(i) !== 'u' || !$(/^url\(/)) return;
value = $(this.entities.quoted) || $(this.entities.variable) ||
$(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?]+/) || "";
$(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || "";
if (! $(')')) throw new(Error)("missing closing ) for url()");
return new(tree.URL)((value.value || value.data || value instanceof tree.Variable)
@@ -699,7 +703,7 @@ less.Parser = function Parser(env) {
variable: function () {
var name, index = i;
if (input.charAt(i) === '@' && (name = $(/^@[\w-]+/))) {
if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) {
return new(tree.Variable)(name, index);
}
},
@@ -739,12 +743,15 @@ less.Parser = function Parser(env) {
// `window.location.href`
//
javascript: function () {
var str;
var str, j = i, e;
if (input.charAt(i) !== '`') { return }
if (input.charAt(j) === '~') { j++, e = true } // Escaped strings
if (input.charAt(j) !== '`') { return }
e && $('~');
if (str = $(/^`([^`]*)`/)) {
return new(tree.JavaScript)(str[1], i);
return new(tree.JavaScript)(str[1], i, e);
}
}
},
@@ -892,7 +899,7 @@ less.Parser = function Parser(env) {
alpha: function () {
var value;
if (! $(/^opacity=/i)) return;
if (! $(/^\(opacity=/i)) return;
if (value = $(/^\d+/) || $(this.entities.variable)) {
if (! $(')')) throw new(Error)("missing closing ) for alpha()");
return new(tree.Alpha)(value);
@@ -915,9 +922,13 @@ less.Parser = function Parser(env) {
var e, t, c;
c = $(this.combinator);
e = $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || $('*') || $(this.attribute) || $(/^\([^)@]+\)/);
e = $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || $('*') || $(this.attribute) || $(/^\([^)@]+\)/) || $(/^(?:\d*\.)?\d+%/);
if (e) { return new(tree.Element)(c, e) }
if (c.value && c.value[0] === '&') {
return new(tree.Element)(c, null);
}
},
//
@@ -932,10 +943,18 @@ less.Parser = function Parser(env) {
combinator: function () {
var match, c = input.charAt(i);
if (c === '>' || c === '&' || c === '+' || c === '~') {
if (c === '>' || c === '+' || c === '~') {
i++;
while (input.charAt(i) === ' ') { i++ }
return new(tree.Combinator)(c);
} else if (c === '&') {
match = '&';
i++;
if(input.charAt(i) === ' ') {
match = '& ';
}
while (input.charAt(i) === ' ') { i++ }
return new(tree.Combinator)(match);
} else if (c === ':' && input.charAt(i + 1) === ':') {
i += 2;
while (input.charAt(i) === ' ') { i++ }
@@ -1005,7 +1024,7 @@ less.Parser = function Parser(env) {
var selectors = [], s, rules, match;
save();
if (match = /^([.#: \w-]+)[\s\n]*\{/.exec(chunks[j])) {
if (match = /^([.#:% \w-]+)[\s\n]*\{/.exec(chunks[j])) {
i += match[0].length - 1;
selectors = [new(tree.Selector)([new(tree.Element)(null, match[1])])];
} else {
@@ -1082,7 +1101,7 @@ less.Parser = function Parser(env) {
if (value = $(this['import'])) {
return value;
} else if (name = $(/^@media|@page|@-[-a-z]+/)) {
} else if (name = $(/^@media|@page/) || $(/^@(?:-webkit-)?keyframes/)) {
types = ($(/^[^{]+/) || '').trim();
if (rules = $(this.block)) {
return new(tree.Directive)(name + " " + types, rules);
@@ -1408,7 +1427,10 @@ tree.Alpha.prototype = {
return "alpha(opacity=" +
(this.value.toCSS ? this.value.toCSS() : this.value) + ")";
},
eval: function () { return this }
eval: function (env) {
if (this.value.eval) { this.value = this.value.eval(env) }
return this;
}
};
})(require('less/tree'));
@@ -1430,9 +1452,10 @@ tree.Anonymous.prototype = {
//
// A function call node.
//
tree.Call = function (name, args) {
tree.Call = function (name, args, index) {
this.name = name;
this.args = args;
this.index = index;
};
tree.Call.prototype = {
//
@@ -1451,7 +1474,12 @@ tree.Call.prototype = {
var args = this.args.map(function (a) { return a.eval(env) });
if (this.name in tree.functions) { // 1.
return tree.functions[this.name].apply(tree.functions, args);
try {
return tree.functions[this.name].apply(tree.functions, args);
} catch (e) {
throw { message: "error evaluating function `" + this.name + "`",
index: this.index };
}
} else { // 2.
return new(tree.Anonymous)(this.name +
"(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")");
@@ -1648,7 +1676,7 @@ tree.Directive.prototype = {
tree.Element = function (combinator, value) {
this.combinator = combinator instanceof tree.Combinator ?
combinator : new(tree.Combinator)(combinator);
this.value = value.trim();
this.value = value ? value.trim() : "";
};
tree.Element.prototype.toCSS = function (env) {
return this.combinator.toCSS(env || {}) + this.value;
@@ -1657,6 +1685,8 @@ tree.Element.prototype.toCSS = function (env) {
tree.Combinator = function (value) {
if (value === ' ') {
this.value = ' ';
} else if (value === '& ') {
this.value = '& ';
} else {
this.value = value ? value.trim() : "";
}
@@ -1666,6 +1696,7 @@ tree.Combinator.prototype.toCSS = function (env) {
'' : '',
' ' : ' ',
'&' : '',
'& ' : ' ',
':' : ' :',
'::': '::',
'+' : env.compress ? '+' : ' + ',
@@ -1684,8 +1715,10 @@ tree.Expression.prototype = {
return new(tree.Expression)(this.value.map(function (e) {
return e.eval(env);
}));
} else {
} else if (this.value.length === 1) {
return this.value[0].eval(env);
} else {
return this;
}
},
toCSS: function (env) {
@@ -1775,19 +1808,28 @@ tree.Import.prototype = {
})(require('less/tree'));
(function (tree) {
tree.JavaScript = function (string, index) {
tree.JavaScript = function (string, index, escaped) {
this.escaped = escaped;
this.expression = string;
this.index = index;
};
tree.JavaScript.prototype = {
toCSS: function () {
return JSON.stringify(this.evaluated);
},
eval: function (env) {
var result,
expression = new(Function)('return (' + this.expression + ')'),
that = this,
context = {};
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: `" + expression + "`" ,
index: this.index };
}
for (var k in env.frames[0].variables()) {
context[k.slice(1)] = {
value: env.frames[0].variables()[k].value,
@@ -1798,12 +1840,18 @@ tree.JavaScript.prototype = {
}
try {
this.evaluated = expression.call(context);
result = expression.call(context);
} catch (e) {
throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" ,
index: this.index };
}
return this;
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);
}
}
};
@@ -1828,12 +1876,13 @@ tree.mixin.Call = function (elements, args, index) {
};
tree.mixin.Call.prototype = {
eval: function (env) {
var mixins, rules = [], match = false;
var mixins, args, rules = [], match = false;
for (var i = 0; i < env.frames.length; i++) {
if ((mixins = env.frames[i].find(this.selector)).length > 0) {
args = this.arguments && this.arguments.map(function (a) { return a.eval(env) });
for (var m = 0; m < mixins.length; m++) {
if (mixins[m].match(this.arguments, env)) {
if (mixins[m].match(args, env)) {
try {
Array.prototype.push.apply(
rules, mixins[m].eval(env, this.arguments).rules);
@@ -1882,7 +1931,7 @@ tree.mixin.Definition.prototype = {
rulesets: function () { return this.parent.rulesets.apply(this) },
eval: function (env, args) {
var frame = new(tree.Ruleset)(null, []), context;
var frame = new(tree.Ruleset)(null, []), context, _arguments = [];
for (var i = 0, val; i < this.params.length; i++) {
if (this.params[i].name) {
@@ -1894,7 +1943,10 @@ tree.mixin.Definition.prototype = {
}
}
}
frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(args)));
for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) {
_arguments.push(args[i] || this.params[i].value);
}
frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env)));
return new(tree.Ruleset)(null, this.rules.slice(0)).eval({
frames: [this, frame].concat(this.frames, env.frames)
@@ -1954,16 +2006,29 @@ tree.operate = function (op, a, b) {
})(require('less/tree'));
(function (tree) {
tree.Quoted = function (str, content) {
tree.Quoted = function (str, content, escaped, i) {
this.escaped = escaped;
this.value = content || '';
this.quote = str.charAt(0);
this.index = i;
};
tree.Quoted.prototype = {
toCSS: function () {
return this.quote + this.value + this.quote;
if (this.escaped) {
return this.value;
} else {
return this.quote + this.value + this.quote;
}
},
eval: function () {
return this;
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).eval(env);
return v.value || v.toCSS();
});
return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index);
}
};
@@ -2128,11 +2193,7 @@ tree.Ruleset.prototype = {
if (context.length === 0) {
paths = this.selectors.map(function (s) { return [s] });
} else {
for (var s = 0; s < this.selectors.length; s++) {
for (var c = 0; c < context.length; c++) {
paths.push(context[c].concat([this.selectors[s]]));
}
}
this.joinSelectors( paths, context, this.selectors );
}
}
@@ -2182,6 +2243,43 @@ tree.Ruleset.prototype = {
css.push(rulesets);
return css.join('') + (env.compress ? '\n' : '');
},
joinSelectors: function (paths, context, selectors) {
for (var s = 0; s < selectors.length; s++) {
this.joinSelector(paths, context, selectors[s]);
}
},
joinSelector: function (paths, context, selector) {
var before = [], after = [], beforeElements = [],
afterElements = [], hasParentSelector = false, el;
for (var i = 0; i < selector.elements.length; i++) {
el = selector.elements[i];
if (el.combinator.value[0] === '&') {
hasParentSelector = true;
}
if (hasParentSelector) afterElements.push(el);
else beforeElements.push(el);
}
if (! hasParentSelector) {
afterElements = beforeElements;
beforeElements = [];
}
if (beforeElements.length > 0) {
before.push(new(tree.Selector)(beforeElements));
}
if (afterElements.length > 0) {
after.push(new(tree.Selector)(afterElements));
}
for (var c = 0; c < context.length; c++) {
paths.push(before.concat(context[c]).concat(after));
}
}
};
})(require('less/tree'));
@@ -2220,7 +2318,7 @@ tree.URL = function (val, paths) {
this.attrs = val;
} else {
// Add the base path if the URL is relative and we are in the browser
if (!/^(?:https?:\/|file:\/)?\//.test(val.value) && paths.length > 0 && typeof(window) !== 'undefined') {
if (!/^(?:https?:\/|file:\/|data:\/)?\//.test(val.value) && paths.length > 0 && typeof(window) !== 'undefined') {
val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value);
}
this.value = val;
@@ -2269,13 +2367,17 @@ tree.Variable.prototype = {
eval: function (env) {
var variable, v, name = this.name;
if (name.indexOf('@@') == 0) {
name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value;
}
if (variable = tree.find(env.frames, function (frame) {
if (v = frame.variable(name)) {
return v.value.eval(env);
}
})) { return variable }
else {
throw { message: "variable " + this.name + " is undefined",
throw { message: "variable " + name + " is undefined",
index: this.index };
}
}
@@ -2288,12 +2390,20 @@ require('less/tree').find = function (obj, fun) {
}
return null;
};
require('less/tree').jsify = function (obj) {
if (Array.isArray(obj.value) && (obj.value.length > 1)) {
return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']';
} else {
return obj.toCSS(false);
}
};
//
// browser.js - client-side engine
//
var isFileProtocol = (location.protocol === 'file:' ||
location.protocol === 'chrome:' ||
location.protocol === 'chrome-extension:' ||
location.protocol === 'resource:');
less.env = less.env || (location.hostname == '127.0.0.1' ||
@@ -2412,7 +2522,11 @@ function loadStyleSheet(sheet, callback, reload, remaining) {
// Stylesheets in IE don't always return the full path
if (! /^(https?|file):/.test(href)) {
href = url.slice(0, url.lastIndexOf('/') + 1) + href;
if (href.charAt(0) == "/") {
href = window.location.protocol + "//" + window.location.host + href;
} else {
href = url.slice(0, url.lastIndexOf('/') + 1) + href;
}
}
xhr(sheet.href, sheet.type, function (data, lastModified) {

16
dist/less-1.1.4.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -787,9 +787,13 @@ less.Parser = function Parser(env) {
var e, t, c;
c = $(this.combinator);
e = $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || $('*') || $(this.attribute) || $(/^\([^)@]+\)/);
e = $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || $('*') || $(this.attribute) || $(/^\([^)@]+\)/) || $(/^(?:\d*\.)?\d+%/);
if (e) { return new(tree.Element)(c, e) }
if (c.value && c.value[0] === '&') {
return new(tree.Element)(c, null);
}
},
//
@@ -804,10 +808,18 @@ less.Parser = function Parser(env) {
combinator: function () {
var match, c = input.charAt(i);
if (c === '>' || c === '&' || c === '+' || c === '~') {
if (c === '>' || c === '+' || c === '~') {
i++;
while (input.charAt(i) === ' ') { i++ }
return new(tree.Combinator)(c);
} else if (c === '&') {
match = '&';
i++;
if(input.charAt(i) === ' ') {
match = '& ';
}
while (input.charAt(i) === ' ') { i++ }
return new(tree.Combinator)(match);
} else if (c === ':' && input.charAt(i + 1) === ':') {
i += 2;
while (input.charAt(i) === ' ') { i++ }
@@ -877,7 +889,7 @@ less.Parser = function Parser(env) {
var selectors = [], s, rules, match;
save();
if (match = /^([.#: \w-]+)[\s\n]*\{/.exec(chunks[j])) {
if (match = /^([.#:% \w-]+)[\s\n]*\{/.exec(chunks[j])) {
i += match[0].length - 1;
selectors = [new(tree.Selector)([new(tree.Element)(null, match[1])])];
} else {
@@ -954,7 +966,7 @@ less.Parser = function Parser(env) {
if (value = $(this['import'])) {
return value;
} else if (name = $(/^@media|@page|@-[-a-z]+/)) {
} else if (name = $(/^@media|@page/) || $(/^@(?:-webkit-)?keyframes/)) {
types = ($(/^[^{]+/) || '').trim();
if (rules = $(this.block)) {
return new(tree.Directive)(name + " " + types, rules);

View File

@@ -3,7 +3,7 @@
tree.Element = function (combinator, value) {
this.combinator = combinator instanceof tree.Combinator ?
combinator : new(tree.Combinator)(combinator);
this.value = value.trim();
this.value = value ? value.trim() : "";
};
tree.Element.prototype.toCSS = function (env) {
return this.combinator.toCSS(env || {}) + this.value;
@@ -12,6 +12,8 @@ tree.Element.prototype.toCSS = function (env) {
tree.Combinator = function (value) {
if (value === ' ') {
this.value = ' ';
} else if (value === '& ') {
this.value = '& ';
} else {
this.value = value ? value.trim() : "";
}
@@ -21,6 +23,7 @@ tree.Combinator.prototype.toCSS = function (env) {
'' : '',
' ' : ' ',
'&' : '',
'& ' : ' ',
':' : ' :',
'::': '::',
'+' : env.compress ? '+' : ' + ',

View File

@@ -120,11 +120,7 @@ tree.Ruleset.prototype = {
if (context.length === 0) {
paths = this.selectors.map(function (s) { return [s] });
} else {
for (var s = 0; s < this.selectors.length; s++) {
for (var c = 0; c < context.length; c++) {
paths.push(context[c].concat([this.selectors[s]]));
}
}
this.joinSelectors( paths, context, this.selectors );
}
}
@@ -174,6 +170,43 @@ tree.Ruleset.prototype = {
css.push(rulesets);
return css.join('') + (env.compress ? '\n' : '');
},
joinSelectors: function (paths, context, selectors) {
for (var s = 0; s < selectors.length; s++) {
this.joinSelector(paths, context, selectors[s]);
}
},
joinSelector: function (paths, context, selector) {
var before = [], after = [], beforeElements = [],
afterElements = [], hasParentSelector = false, el;
for (var i = 0; i < selector.elements.length; i++) {
el = selector.elements[i];
if (el.combinator.value[0] === '&') {
hasParentSelector = true;
}
if (hasParentSelector) afterElements.push(el);
else beforeElements.push(el);
}
if (! hasParentSelector) {
afterElements = beforeElements;
beforeElements = [];
}
if (beforeElements.length > 0) {
before.push(new(tree.Selector)(beforeElements));
}
if (afterElements.length > 0) {
after.push(new(tree.Selector)(afterElements));
}
for (var c = 0; c < context.length; c++) {
paths.push(before.concat(context[c]).concat(after));
}
}
};
})(require('less/tree'));

View File

@@ -5,9 +5,9 @@
"keywords" : ["css", "parser", "lesscss", "browser"],
"author" : "Alexis Sellier <self@cloudhead.net>",
"contributors" : [],
"version" : "1.1.3",
"version" : "1.1.4",
"bin" : { "lessc": "./bin/lessc" },
"main" : "./lib/less/index",
"directories" : { "test": "./test" },
"engines" : { "node": ">=0.2.0" }
"engines" : { "node": ">=0.4.0" }
}

View File

@@ -30,3 +30,24 @@ td {
td, input {
line-height: 1em;
}
a {
color: red;
}
a:hover {
color: blue;
}
div a {
color: green;
}
p a span {
color: yellow;
}
.foo .bar .qux, .foo .baz .qux {
display: block;
}
.qux .foo .bar, .qux .foo .baz {
display: inline;
}
.qux .foo .bar .biz, .qux .foo .baz .biz {
display: none;
}

View File

@@ -22,3 +22,27 @@ td {
td, input {
line-height: 1em;
}
a {
color: red;
&:hover { color: blue; }
div & { color: green; }
p & span { color: yellow; }
}
.foo {
.bar, .baz {
& .qux {
display: block;
}
.qux & {
display: inline;
}
.qux & .biz {
display: none;
}
}
}