less 2.5.1 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Changelog.md +5 -1
- data/README.md +12 -0
- data/lib/less/js/.travis.yml +8 -0
- data/lib/less/js/CHANGELOG.md +22 -0
- data/lib/less/js/CONTRIBUTING.md +1 -1
- data/lib/less/js/Gruntfile.js +8 -0
- data/lib/less/js/README.md +5 -304
- data/lib/less/js/bin/lessc +16 -13
- data/lib/less/js/bower.json +2 -2
- data/lib/less/js/build/README.md +4 -303
- data/lib/less/js/build/build.yml +2 -0
- data/lib/less/js/dist/less-1.6.3.js +7627 -0
- data/lib/less/js/dist/less-1.6.3.min.js +16 -0
- data/lib/less/js/dist/less-1.7.0.js +7921 -0
- data/lib/less/js/dist/less-1.7.0.min.js +16 -0
- data/lib/less/js/dist/less-rhino-1.6.3.js +9020 -0
- data/lib/less/js/dist/less-rhino-1.7.0.js +9301 -0
- data/lib/less/js/dist/lessc-rhino-1.6.3.js +449 -0
- data/lib/less/js/dist/lessc-rhino-1.7.0.js +449 -0
- data/lib/less/js/lib/less/browser.js +22 -9
- data/lib/less/js/lib/less/functions.js +47 -16
- data/lib/less/js/lib/less/import-visitor.js +29 -5
- data/lib/less/js/lib/less/index.js +10 -3
- data/lib/less/js/lib/less/parser.js +139 -52
- data/lib/less/js/lib/less/to-css-visitor.js +26 -4
- data/lib/less/js/lib/less/tree/color.js +11 -1
- data/lib/less/js/lib/less/tree/detached-ruleset.js +20 -0
- data/lib/less/js/lib/less/tree/dimension.js +17 -6
- data/lib/less/js/lib/less/tree/directive.js +33 -29
- data/lib/less/js/lib/less/tree/import.js +8 -1
- data/lib/less/js/lib/less/tree/keyword.js +1 -0
- data/lib/less/js/lib/less/tree/media.js +3 -0
- data/lib/less/js/lib/less/tree/mixin.js +15 -11
- data/lib/less/js/lib/less/tree/rule.js +14 -4
- data/lib/less/js/lib/less/tree/ruleset-call.js +16 -0
- data/lib/less/js/lib/less/tree/ruleset.js +48 -19
- data/lib/less/js/package.json +13 -13
- data/lib/less/js/test/browser/css/postProcessor/postProcessor.css +4 -0
- data/lib/less/js/test/browser/less/postProcessor/postProcessor.less +4 -0
- data/lib/less/js/test/browser/runner-postProcessor-options.js +4 -0
- data/lib/less/js/test/browser/runner-postProcessor.js +3 -0
- data/lib/less/js/test/css/debug/linenumbers-all.css +2 -2
- data/lib/less/js/test/css/debug/linenumbers-comments.css +1 -1
- data/lib/less/js/test/css/debug/linenumbers-mediaquery.css +1 -1
- data/lib/less/js/test/css/detached-rulesets.css +71 -0
- data/lib/less/js/test/css/functions.css +21 -4
- data/lib/less/js/test/css/import-reference.css +23 -4
- data/lib/less/js/test/css/merge.css +8 -0
- data/lib/less/js/test/css/mixins-pattern.css +4 -0
- data/lib/less/js/test/css/scope.css +3 -0
- data/lib/less/js/test/css/variables-in-at-rules.css +18 -0
- data/lib/less/js/test/less/css-guards.less +3 -0
- data/lib/less/js/test/less/detached-rulesets.less +103 -0
- data/lib/less/js/test/less/errors/at-rules-undefined-var.less +4 -0
- data/lib/less/js/test/less/errors/at-rules-undefined-var.txt +4 -0
- data/lib/less/js/test/less/errors/detached-ruleset-1.less +6 -0
- data/lib/less/js/test/less/errors/detached-ruleset-1.txt +4 -0
- data/lib/less/js/test/less/errors/detached-ruleset-2.less +6 -0
- data/lib/less/js/test/less/errors/detached-ruleset-2.txt +4 -0
- data/lib/less/js/test/less/errors/detached-ruleset-3.less +4 -0
- data/lib/less/js/test/less/errors/detached-ruleset-3.txt +4 -0
- data/lib/less/js/test/less/errors/detached-ruleset-4.less +5 -0
- data/lib/less/js/test/less/errors/detached-ruleset-4.txt +3 -0
- data/lib/less/js/test/less/errors/detached-ruleset-5.less +4 -0
- data/lib/less/js/test/less/errors/detached-ruleset-5.txt +3 -0
- data/lib/less/js/test/less/errors/detached-ruleset-6.less +5 -0
- data/lib/less/js/test/less/errors/detached-ruleset-6.txt +4 -0
- data/lib/less/js/test/less/errors/mixin-not-visible-in-scope-1.less +9 -0
- data/lib/less/js/test/less/errors/mixin-not-visible-in-scope-1.txt +4 -0
- data/lib/less/js/test/less/errors/percentage-missing-space.less +3 -0
- data/lib/less/js/test/less/errors/percentage-missing-space.txt +4 -0
- data/lib/less/js/test/less/functions.less +25 -7
- data/lib/less/js/test/less/import-reference.less +7 -4
- data/lib/less/js/test/less/import/import-reference.less +8 -0
- data/lib/less/js/test/less/merge.less +20 -1
- data/lib/less/js/test/less/mixins-guards.less +7 -1
- data/lib/less/js/test/less/mixins-pattern.less +3 -0
- data/lib/less/js/test/less/scope.less +25 -0
- data/lib/less/js/test/less/variables-in-at-rules.less +20 -0
- data/lib/less/version.rb +1 -1
- metadata +39 -2
@@ -14,17 +14,19 @@ less.env = less.env || (location.hostname == '127.0.0.1' ||
|
|
14
14
|
: 'production');
|
15
15
|
|
16
16
|
var logLevel = {
|
17
|
+
debug: 3,
|
17
18
|
info: 2,
|
18
19
|
errors: 1,
|
19
20
|
none: 0
|
20
21
|
};
|
21
22
|
|
22
23
|
// The amount of logging in the javascript console.
|
24
|
+
// 3 - Debug, information and errors
|
23
25
|
// 2 - Information and errors
|
24
26
|
// 1 - Errors
|
25
27
|
// 0 - None
|
26
28
|
// Defaults to 2
|
27
|
-
less.logLevel = typeof(less.logLevel) != 'undefined' ? less.logLevel : logLevel.
|
29
|
+
less.logLevel = typeof(less.logLevel) != 'undefined' ? less.logLevel : (less.env === 'development' ? logLevel.debug : logLevel.errors);
|
28
30
|
|
29
31
|
// Load styles asynchronously (default: false)
|
30
32
|
//
|
@@ -57,7 +59,7 @@ var cache = null;
|
|
57
59
|
var fileCache = {};
|
58
60
|
|
59
61
|
function log(str, level) {
|
60
|
-
if (
|
62
|
+
if (typeof(console) !== 'undefined' && less.logLevel >= level) {
|
61
63
|
console.log('less: ' + str);
|
62
64
|
}
|
63
65
|
}
|
@@ -159,6 +161,13 @@ function createCSS(styles, sheet, lastModified) {
|
|
159
161
|
}
|
160
162
|
}
|
161
163
|
|
164
|
+
function postProcessCSS(styles) {
|
165
|
+
if (less.postProcessor && typeof less.postProcessor === 'function') {
|
166
|
+
styles = less.postProcessor.call(styles, styles) || styles;
|
167
|
+
}
|
168
|
+
return styles;
|
169
|
+
}
|
170
|
+
|
162
171
|
function errorHTML(e, rootHref) {
|
163
172
|
var id = 'less-error-message:' + extractId(rootHref || "");
|
164
173
|
var template = '<li><label>{line}</label><pre class="{class}">{content}</pre></li>';
|
@@ -402,12 +411,12 @@ function pathDiff(url, baseUrl) {
|
|
402
411
|
}
|
403
412
|
|
404
413
|
function getXMLHttpRequest() {
|
405
|
-
if (window.XMLHttpRequest) {
|
414
|
+
if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !window.ActiveXObject)) {
|
406
415
|
return new XMLHttpRequest();
|
407
416
|
} else {
|
408
417
|
try {
|
409
418
|
/*global ActiveXObject */
|
410
|
-
return new ActiveXObject("
|
419
|
+
return new ActiveXObject("Microsoft.XMLHTTP");
|
411
420
|
} catch (e) {
|
412
421
|
log("browser doesn't support AJAX.", logLevel.errors);
|
413
422
|
return null;
|
@@ -422,7 +431,7 @@ function doXHR(url, type, callback, errback) {
|
|
422
431
|
if (typeof(xhr.overrideMimeType) === 'function') {
|
423
432
|
xhr.overrideMimeType('text/css');
|
424
433
|
}
|
425
|
-
log("XHR: Getting '" + url + "'", logLevel.
|
434
|
+
log("XHR: Getting '" + url + "'", logLevel.debug);
|
426
435
|
xhr.open('GET', url, async);
|
427
436
|
xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5');
|
428
437
|
xhr.send(null);
|
@@ -575,7 +584,9 @@ function initRunningMode(){
|
|
575
584
|
if (e) {
|
576
585
|
error(e, sheet.href);
|
577
586
|
} else if (root) {
|
578
|
-
|
587
|
+
var styles = root.toCSS(less);
|
588
|
+
styles = postProcessCSS(styles);
|
589
|
+
createCSS(styles, sheet, env.lastModified);
|
579
590
|
}
|
580
591
|
});
|
581
592
|
}
|
@@ -644,12 +655,14 @@ less.refresh = function (reload, modifyVars) {
|
|
644
655
|
if (env.local) {
|
645
656
|
log("loading " + sheet.href + " from cache.", logLevel.info);
|
646
657
|
} else {
|
647
|
-
log("parsed " + sheet.href + " successfully.", logLevel.
|
648
|
-
|
658
|
+
log("parsed " + sheet.href + " successfully.", logLevel.debug);
|
659
|
+
var styles = root.toCSS(less);
|
660
|
+
styles = postProcessCSS(styles);
|
661
|
+
createCSS(styles, sheet, env.lastModified);
|
649
662
|
}
|
650
663
|
log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms', logLevel.info);
|
651
664
|
if (env.remaining === 0) {
|
652
|
-
log("css generated in " + (new Date() - startTime) + 'ms', logLevel.info);
|
665
|
+
log("less has finished. css generated in " + (new Date() - startTime) + 'ms', logLevel.info);
|
653
666
|
}
|
654
667
|
endTime = new Date();
|
655
668
|
}, reload, modifyVars);
|
@@ -95,6 +95,14 @@ tree.functions = {
|
|
95
95
|
luma: function (color) {
|
96
96
|
return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%');
|
97
97
|
},
|
98
|
+
luminance: function (color) {
|
99
|
+
var luminance =
|
100
|
+
(0.2126 * color.rgb[0] / 255)
|
101
|
+
+ (0.7152 * color.rgb[1] / 255)
|
102
|
+
+ (0.0722 * color.rgb[2] / 255);
|
103
|
+
|
104
|
+
return new(tree.Dimension)(Math.round(luminance * color.alpha * 100), '%');
|
105
|
+
},
|
98
106
|
saturate: function (color, amount) {
|
99
107
|
// filter: saturate(3.2);
|
100
108
|
// should be kept as is, so check for color
|
@@ -218,25 +226,40 @@ tree.functions = {
|
|
218
226
|
escape: function (str) {
|
219
227
|
return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29"));
|
220
228
|
},
|
221
|
-
|
229
|
+
replace: function (string, pattern, replacement, flags) {
|
230
|
+
var result = string.value;
|
231
|
+
|
232
|
+
result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement.value);
|
233
|
+
return new(tree.Quoted)(string.quote || '', result, string.escaped);
|
234
|
+
},
|
235
|
+
'%': function (string /* arg, arg, ...*/) {
|
222
236
|
var args = Array.prototype.slice.call(arguments, 1),
|
223
|
-
|
237
|
+
result = string.value;
|
224
238
|
|
225
239
|
for (var i = 0; i < args.length; i++) {
|
226
240
|
/*jshint loopfunc:true */
|
227
|
-
|
241
|
+
result = result.replace(/%[sda]/i, function(token) {
|
228
242
|
var value = token.match(/s/i) ? args[i].value : args[i].toCSS();
|
229
243
|
return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value;
|
230
244
|
});
|
231
245
|
}
|
232
|
-
|
233
|
-
return new(tree.Quoted)(
|
246
|
+
result = result.replace(/%%/g, '%');
|
247
|
+
return new(tree.Quoted)(string.quote || '', result, string.escaped);
|
234
248
|
},
|
235
249
|
unit: function (val, unit) {
|
236
250
|
if(!(val instanceof tree.Dimension)) {
|
237
251
|
throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") };
|
238
252
|
}
|
239
|
-
|
253
|
+
if (unit) {
|
254
|
+
if (unit instanceof tree.Keyword) {
|
255
|
+
unit = unit.value;
|
256
|
+
} else {
|
257
|
+
unit = unit.toCSS();
|
258
|
+
}
|
259
|
+
} else {
|
260
|
+
unit = "";
|
261
|
+
}
|
262
|
+
return new(tree.Dimension)(val.value, unit);
|
240
263
|
},
|
241
264
|
convert: function (val, unit) {
|
242
265
|
return val.convertTo(unit.value);
|
@@ -264,28 +287,34 @@ tree.functions = {
|
|
264
287
|
_minmax: function (isMin, args) {
|
265
288
|
args = Array.prototype.slice.call(args);
|
266
289
|
switch(args.length) {
|
267
|
-
|
268
|
-
case 1: return args[0];
|
290
|
+
case 0: throw { type: "Argument", message: "one or more arguments required" };
|
269
291
|
}
|
270
|
-
var i, j, current, currentUnified, referenceUnified, unit,
|
292
|
+
var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone,
|
271
293
|
order = [], // elems only contains original argument values.
|
272
294
|
values = {}; // key is the unit.toString() for unified tree.Dimension values,
|
273
295
|
// value is the index into the order array.
|
274
296
|
for (i = 0; i < args.length; i++) {
|
275
297
|
current = args[i];
|
276
298
|
if (!(current instanceof tree.Dimension)) {
|
277
|
-
|
299
|
+
if(Array.isArray(args[i].value)) {
|
300
|
+
Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value));
|
301
|
+
}
|
278
302
|
continue;
|
279
303
|
}
|
280
|
-
currentUnified = current.unify();
|
281
|
-
unit = currentUnified.unit.toString();
|
282
|
-
|
304
|
+
currentUnified = current.unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(current.value, unitClone).unify() : current.unify();
|
305
|
+
unit = currentUnified.unit.toString() === "" && unitStatic !== undefined ? unitStatic : currentUnified.unit.toString();
|
306
|
+
unitStatic = unit !== "" && unitStatic === undefined || unit !== "" && order[0].unify().unit.toString() === "" ? unit : unitStatic;
|
307
|
+
unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone;
|
308
|
+
j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit];
|
283
309
|
if (j === undefined) {
|
310
|
+
if(unitStatic !== undefined && unit !== unitStatic) {
|
311
|
+
throw{ type: "Argument", message: "incompatible types" };
|
312
|
+
}
|
284
313
|
values[unit] = order.length;
|
285
314
|
order.push(current);
|
286
315
|
continue;
|
287
316
|
}
|
288
|
-
referenceUnified = order[j].unify();
|
317
|
+
referenceUnified = order[j].unit.toString() === "" && unitClone !== undefined ? new(tree.Dimension)(order[j].value, unitClone).unify() : order[j].unify();
|
289
318
|
if ( isMin && currentUnified.value < referenceUnified.value ||
|
290
319
|
!isMin && currentUnified.value > referenceUnified.value) {
|
291
320
|
order[j] = current;
|
@@ -294,8 +323,7 @@ tree.functions = {
|
|
294
323
|
if (order.length == 1) {
|
295
324
|
return order[0];
|
296
325
|
}
|
297
|
-
args = order.map(function (a) { return a.toCSS(this.env); })
|
298
|
-
.join(this.env.compress ? "," : ", ");
|
326
|
+
args = order.map(function (a) { return a.toCSS(this.env); }).join(this.env.compress ? "," : ", ");
|
299
327
|
return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")");
|
300
328
|
},
|
301
329
|
min: function () {
|
@@ -304,6 +332,9 @@ tree.functions = {
|
|
304
332
|
max: function () {
|
305
333
|
return this._minmax(false, arguments);
|
306
334
|
},
|
335
|
+
"get-unit": function (n) {
|
336
|
+
return new(tree.Anonymous)(n.unit);
|
337
|
+
},
|
307
338
|
argb: function (color) {
|
308
339
|
return new(tree.Anonymous)(color.toARGB());
|
309
340
|
},
|
@@ -1,10 +1,19 @@
|
|
1
1
|
(function (tree) {
|
2
|
-
tree.importVisitor = function(importer, finish, evalEnv) {
|
2
|
+
tree.importVisitor = function(importer, finish, evalEnv, onceFileDetectionMap, recursionDetector) {
|
3
3
|
this._visitor = new tree.visitor(this);
|
4
4
|
this._importer = importer;
|
5
5
|
this._finish = finish;
|
6
6
|
this.env = evalEnv || new tree.evalEnv();
|
7
7
|
this.importCount = 0;
|
8
|
+
this.onceFileDetectionMap = onceFileDetectionMap || {};
|
9
|
+
this.recursionDetector = {};
|
10
|
+
if (recursionDetector) {
|
11
|
+
for(var fullFilename in recursionDetector) {
|
12
|
+
if (recursionDetector.hasOwnProperty(fullFilename)) {
|
13
|
+
this.recursionDetector[fullFilename] = true;
|
14
|
+
}
|
15
|
+
}
|
16
|
+
}
|
8
17
|
};
|
9
18
|
|
10
19
|
tree.importVisitor.prototype = {
|
@@ -51,10 +60,22 @@
|
|
51
60
|
env.importMultiple = true;
|
52
61
|
}
|
53
62
|
|
54
|
-
this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root,
|
63
|
+
this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, importedAtRoot, fullPath) {
|
55
64
|
if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; }
|
56
65
|
|
57
|
-
if (
|
66
|
+
if (!env.importMultiple) {
|
67
|
+
if (importedAtRoot) {
|
68
|
+
importNode.skip = true;
|
69
|
+
} else {
|
70
|
+
importNode.skip = function() {
|
71
|
+
if (fullPath in importVisitor.onceFileDetectionMap) {
|
72
|
+
return true;
|
73
|
+
}
|
74
|
+
importVisitor.onceFileDetectionMap[fullPath] = true;
|
75
|
+
return false;
|
76
|
+
};
|
77
|
+
}
|
78
|
+
}
|
58
79
|
|
59
80
|
var subFinish = function(e) {
|
60
81
|
importVisitor.importCount--;
|
@@ -67,8 +88,11 @@
|
|
67
88
|
if (root) {
|
68
89
|
importNode.root = root;
|
69
90
|
importNode.importedFilename = fullPath;
|
70
|
-
|
71
|
-
|
91
|
+
var duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector;
|
92
|
+
|
93
|
+
if (!inlineCSS && (env.importMultiple || !duplicateImport)) {
|
94
|
+
importVisitor.recursionDetector[fullPath] = true;
|
95
|
+
new(tree.importVisitor)(importVisitor._importer, subFinish, env, importVisitor.onceFileDetectionMap, importVisitor.recursionDetector)
|
72
96
|
.run(root);
|
73
97
|
return;
|
74
98
|
}
|
@@ -4,7 +4,7 @@ var path = require('path'),
|
|
4
4
|
fs = require('fs');
|
5
5
|
|
6
6
|
var less = {
|
7
|
-
version: [1,
|
7
|
+
version: [1, 7, 0],
|
8
8
|
Parser: require('./parser').Parser,
|
9
9
|
tree: require('./tree'),
|
10
10
|
render: function (input, options, callback) {
|
@@ -20,8 +20,13 @@ var less = {
|
|
20
20
|
|
21
21
|
if (callback) {
|
22
22
|
parser.parse(input, function (e, root) {
|
23
|
-
|
24
|
-
|
23
|
+
if (e) { callback(e); return; }
|
24
|
+
var css;
|
25
|
+
try {
|
26
|
+
css = root && root.toCSS && root.toCSS(options);
|
27
|
+
}
|
28
|
+
catch (err) { callback(err); return; }
|
29
|
+
callback(null, css);
|
25
30
|
});
|
26
31
|
} else {
|
27
32
|
ee = new (require('events').EventEmitter)();
|
@@ -94,6 +99,7 @@ var less = {
|
|
94
99
|
|
95
100
|
require('./tree/color');
|
96
101
|
require('./tree/directive');
|
102
|
+
require('./tree/detached-ruleset');
|
97
103
|
require('./tree/operation');
|
98
104
|
require('./tree/dimension');
|
99
105
|
require('./tree/keyword');
|
@@ -120,6 +126,7 @@ require('./tree/media');
|
|
120
126
|
require('./tree/unicode-descriptor');
|
121
127
|
require('./tree/negative');
|
122
128
|
require('./tree/extend');
|
129
|
+
require('./tree/ruleset-call');
|
123
130
|
|
124
131
|
|
125
132
|
var isUrlRe = /^(?:https?:)?\/\//i;
|
@@ -43,8 +43,7 @@ less.Parser = function Parser(env) {
|
|
43
43
|
var input, // LeSS input string
|
44
44
|
i, // current index in `input`
|
45
45
|
j, // current chunk
|
46
|
-
|
47
|
-
memo, // temporarily holds `i`, when backtracking
|
46
|
+
saveStack = [], // holds state for backtracking
|
48
47
|
furthest, // furthest index the parser has gone to
|
49
48
|
chunks, // chunkified input
|
50
49
|
current, // current chunk
|
@@ -74,7 +73,7 @@ less.Parser = function Parser(env) {
|
|
74
73
|
var fileParsedFunc = function (e, root, fullPath) {
|
75
74
|
parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue
|
76
75
|
|
77
|
-
var importedPreviously = fullPath
|
76
|
+
var importedPreviously = fullPath === rootFilename;
|
78
77
|
|
79
78
|
parserImports.files[fullPath] = root; // Store the root
|
80
79
|
|
@@ -111,8 +110,9 @@ less.Parser = function Parser(env) {
|
|
111
110
|
}
|
112
111
|
};
|
113
112
|
|
114
|
-
function save() {
|
115
|
-
function restore() { current =
|
113
|
+
function save() { currentPos = i; saveStack.push( { current: current, i: i, j: j }); }
|
114
|
+
function restore() { var state = saveStack.pop(); current = state.current; currentPos = i = state.i; j = state.j; }
|
115
|
+
function forget() { saveStack.pop(); }
|
116
116
|
|
117
117
|
function sync() {
|
118
118
|
if (i > currentPos) {
|
@@ -424,7 +424,7 @@ less.Parser = function Parser(env) {
|
|
424
424
|
if (--level < 0) {
|
425
425
|
return fail("missing opening `{`");
|
426
426
|
}
|
427
|
-
if (!level) { emitChunk(); }
|
427
|
+
if (!level && !parenLevel) { emitChunk(); }
|
428
428
|
continue;
|
429
429
|
case 92: // \
|
430
430
|
if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; }
|
@@ -729,7 +729,7 @@ less.Parser = function Parser(env) {
|
|
729
729
|
while (current)
|
730
730
|
{
|
731
731
|
node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() ||
|
732
|
-
mixin.call() || this.comment() || this.directive();
|
732
|
+
mixin.call() || this.comment() || this.rulesetCall() || this.directive();
|
733
733
|
if (node) {
|
734
734
|
root.push(node);
|
735
735
|
} else {
|
@@ -737,6 +737,9 @@ less.Parser = function Parser(env) {
|
|
737
737
|
break;
|
738
738
|
}
|
739
739
|
}
|
740
|
+
if (peekChar('}')) {
|
741
|
+
break;
|
742
|
+
}
|
740
743
|
}
|
741
744
|
|
742
745
|
return root;
|
@@ -804,7 +807,7 @@ less.Parser = function Parser(env) {
|
|
804
807
|
keyword: function () {
|
805
808
|
var k;
|
806
809
|
|
807
|
-
k = $re(
|
810
|
+
k = $re(/^%|^[_A-Za-z-][_A-Za-z0-9-]*/);
|
808
811
|
if (k) {
|
809
812
|
var color = tree.Color.fromKeyword(k);
|
810
813
|
if (color) {
|
@@ -1027,6 +1030,19 @@ less.Parser = function Parser(env) {
|
|
1027
1030
|
if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; }
|
1028
1031
|
},
|
1029
1032
|
|
1033
|
+
//
|
1034
|
+
// The variable part of a variable definition. Used in the `rule` parser
|
1035
|
+
//
|
1036
|
+
// @fink();
|
1037
|
+
//
|
1038
|
+
rulesetCall: function () {
|
1039
|
+
var name;
|
1040
|
+
|
1041
|
+
if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) {
|
1042
|
+
return new tree.RulesetCall(name[1]);
|
1043
|
+
}
|
1044
|
+
},
|
1045
|
+
|
1030
1046
|
//
|
1031
1047
|
// extend syntax - used to extend selectors
|
1032
1048
|
//
|
@@ -1112,6 +1128,7 @@ less.Parser = function Parser(env) {
|
|
1112
1128
|
}
|
1113
1129
|
|
1114
1130
|
if (parsers.end()) {
|
1131
|
+
forget();
|
1115
1132
|
return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important);
|
1116
1133
|
}
|
1117
1134
|
}
|
@@ -1124,9 +1141,11 @@ less.Parser = function Parser(env) {
|
|
1124
1141
|
expressions = [], argsSemiColon = [], argsComma = [],
|
1125
1142
|
isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg;
|
1126
1143
|
|
1144
|
+
save();
|
1145
|
+
|
1127
1146
|
while (true) {
|
1128
1147
|
if (isCall) {
|
1129
|
-
arg = parsers.expression();
|
1148
|
+
arg = parsers.detachedRuleset() || parsers.expression();
|
1130
1149
|
} else {
|
1131
1150
|
parsers.comments();
|
1132
1151
|
if (input.charAt(i) === '.' && $re(/^\.{3}/)) {
|
@@ -1154,7 +1173,7 @@ less.Parser = function Parser(env) {
|
|
1154
1173
|
|
1155
1174
|
if (isCall) {
|
1156
1175
|
// Variable
|
1157
|
-
if (arg.value.length == 1) {
|
1176
|
+
if (arg.value && arg.value.length == 1) {
|
1158
1177
|
val = arg.value[0];
|
1159
1178
|
}
|
1160
1179
|
} else {
|
@@ -1169,7 +1188,21 @@ less.Parser = function Parser(env) {
|
|
1169
1188
|
}
|
1170
1189
|
expressionContainsNamed = true;
|
1171
1190
|
}
|
1172
|
-
|
1191
|
+
|
1192
|
+
// we do not support setting a ruleset as a default variable - it doesn't make sense
|
1193
|
+
// However if we do want to add it, there is nothing blocking it, just don't error
|
1194
|
+
// and remove isCall dependency below
|
1195
|
+
value = (isCall && parsers.detachedRuleset()) || parsers.expression();
|
1196
|
+
|
1197
|
+
if (!value) {
|
1198
|
+
if (isCall) {
|
1199
|
+
error("could not understand value for named argument");
|
1200
|
+
} else {
|
1201
|
+
restore();
|
1202
|
+
returner.args = [];
|
1203
|
+
return returner;
|
1204
|
+
}
|
1205
|
+
}
|
1173
1206
|
nameLoop = (name = val.name);
|
1174
1207
|
} else if (!isCall && $re(/^\.{3}/)) {
|
1175
1208
|
returner.variadic = true;
|
@@ -1214,6 +1247,7 @@ less.Parser = function Parser(env) {
|
|
1214
1247
|
}
|
1215
1248
|
}
|
1216
1249
|
|
1250
|
+
forget();
|
1217
1251
|
returner.args = isSemiColonSeperated ? argsSemiColon : argsComma;
|
1218
1252
|
return returner;
|
1219
1253
|
},
|
@@ -1254,10 +1288,14 @@ less.Parser = function Parser(env) {
|
|
1254
1288
|
variadic = argInfo.variadic;
|
1255
1289
|
|
1256
1290
|
// .mixincall("@{a}");
|
1257
|
-
// looks a bit like a mixin definition..
|
1291
|
+
// looks a bit like a mixin definition..
|
1292
|
+
// also
|
1293
|
+
// .mixincall(@a: {rule: set;});
|
1294
|
+
// so we have to be nice and restore
|
1258
1295
|
if (!$char(')')) {
|
1259
1296
|
furthest = i;
|
1260
1297
|
restore();
|
1298
|
+
return;
|
1261
1299
|
}
|
1262
1300
|
|
1263
1301
|
parsers.comments();
|
@@ -1269,10 +1307,13 @@ less.Parser = function Parser(env) {
|
|
1269
1307
|
ruleset = parsers.block();
|
1270
1308
|
|
1271
1309
|
if (ruleset) {
|
1310
|
+
forget();
|
1272
1311
|
return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic);
|
1273
1312
|
} else {
|
1274
1313
|
restore();
|
1275
1314
|
}
|
1315
|
+
} else {
|
1316
|
+
forget();
|
1276
1317
|
}
|
1277
1318
|
}
|
1278
1319
|
},
|
@@ -1336,10 +1377,16 @@ less.Parser = function Parser(env) {
|
|
1336
1377
|
this.entities.variableCurly();
|
1337
1378
|
|
1338
1379
|
if (! e) {
|
1380
|
+
save();
|
1339
1381
|
if ($char('(')) {
|
1340
1382
|
if ((v = this.selector()) && $char(')')) {
|
1341
1383
|
e = new(tree.Paren)(v);
|
1384
|
+
forget();
|
1385
|
+
} else {
|
1386
|
+
restore();
|
1342
1387
|
}
|
1388
|
+
} else {
|
1389
|
+
forget();
|
1343
1390
|
}
|
1344
1391
|
}
|
1345
1392
|
|
@@ -1442,6 +1489,22 @@ less.Parser = function Parser(env) {
|
|
1442
1489
|
}
|
1443
1490
|
},
|
1444
1491
|
|
1492
|
+
blockRuleset: function() {
|
1493
|
+
var block = this.block();
|
1494
|
+
|
1495
|
+
if (block) {
|
1496
|
+
block = new tree.Ruleset(null, block);
|
1497
|
+
}
|
1498
|
+
return block;
|
1499
|
+
},
|
1500
|
+
|
1501
|
+
detachedRuleset: function() {
|
1502
|
+
var blockRuleset = this.blockRuleset();
|
1503
|
+
if (blockRuleset) {
|
1504
|
+
return new tree.DetachedRuleset(blockRuleset);
|
1505
|
+
}
|
1506
|
+
},
|
1507
|
+
|
1445
1508
|
//
|
1446
1509
|
// div, .class, body > p {...}
|
1447
1510
|
//
|
@@ -1472,6 +1535,7 @@ less.Parser = function Parser(env) {
|
|
1472
1535
|
}
|
1473
1536
|
|
1474
1537
|
if (selectors && (rules = this.block())) {
|
1538
|
+
forget();
|
1475
1539
|
var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports);
|
1476
1540
|
if (env.dumpLineNumbers) {
|
1477
1541
|
ruleset.debugInfo = debugInfo;
|
@@ -1484,28 +1548,38 @@ less.Parser = function Parser(env) {
|
|
1484
1548
|
}
|
1485
1549
|
},
|
1486
1550
|
rule: function (tryAnonymous) {
|
1487
|
-
var name, value, c = input.charAt(
|
1488
|
-
save();
|
1551
|
+
var name, value, startOfRule = i, c = input.charAt(startOfRule), important, merge, isVariable;
|
1489
1552
|
|
1490
1553
|
if (c === '.' || c === '#' || c === '&') { return; }
|
1491
1554
|
|
1555
|
+
save();
|
1556
|
+
|
1492
1557
|
name = this.variable() || this.ruleProperty();
|
1493
1558
|
if (name) {
|
1494
|
-
|
1495
|
-
// but always fallback on the other one
|
1496
|
-
value = !tryAnonymous && (env.compress || (name.charAt && (name.charAt(0) === '@'))) ?
|
1497
|
-
(this.value() || this.anonymousValue()) :
|
1498
|
-
(this.anonymousValue() || this.value());
|
1499
|
-
|
1500
|
-
important = this.important();
|
1559
|
+
isVariable = typeof name === "string";
|
1501
1560
|
|
1502
|
-
|
1503
|
-
|
1504
|
-
|
1505
|
-
|
1561
|
+
if (isVariable) {
|
1562
|
+
value = this.detachedRuleset();
|
1563
|
+
}
|
1564
|
+
|
1565
|
+
if (!value) {
|
1566
|
+
// prefer to try to parse first if its a variable or we are compressing
|
1567
|
+
// but always fallback on the other one
|
1568
|
+
value = !tryAnonymous && (env.compress || isVariable) ?
|
1569
|
+
(this.value() || this.anonymousValue()) :
|
1570
|
+
(this.anonymousValue() || this.value());
|
1571
|
+
|
1572
|
+
important = this.important();
|
1573
|
+
|
1574
|
+
// a name returned by this.ruleProperty() is always an array of the form:
|
1575
|
+
// [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"]
|
1576
|
+
// where each item is a tree.Keyword or tree.Variable
|
1577
|
+
merge = !isVariable && name.pop().value;
|
1578
|
+
}
|
1506
1579
|
|
1507
1580
|
if (value && this.end()) {
|
1508
|
-
|
1581
|
+
forget();
|
1582
|
+
return new (tree.Rule)(name, value, important, merge, startOfRule, env.currentFileInfo);
|
1509
1583
|
} else {
|
1510
1584
|
furthest = i;
|
1511
1585
|
restore();
|
@@ -1513,6 +1587,8 @@ less.Parser = function Parser(env) {
|
|
1513
1587
|
return this.rule(true);
|
1514
1588
|
}
|
1515
1589
|
}
|
1590
|
+
} else {
|
1591
|
+
forget();
|
1516
1592
|
}
|
1517
1593
|
},
|
1518
1594
|
anonymousValue: function () {
|
@@ -1546,6 +1622,7 @@ less.Parser = function Parser(env) {
|
|
1546
1622
|
if (dir && (path = this.entities.quoted() || this.entities.url())) {
|
1547
1623
|
features = this.mediaFeatures();
|
1548
1624
|
if ($char(';')) {
|
1625
|
+
forget();
|
1549
1626
|
features = features && new(tree.Value)(features);
|
1550
1627
|
return new(tree.Import)(path, features, options, index, env.currentFileInfo);
|
1551
1628
|
}
|
@@ -1662,7 +1739,7 @@ less.Parser = function Parser(env) {
|
|
1662
1739
|
//
|
1663
1740
|
directive: function () {
|
1664
1741
|
var index = i, name, value, rules, nonVendorSpecificName,
|
1665
|
-
|
1742
|
+
hasIdentifier, hasExpression, hasUnknown, hasBlock = true;
|
1666
1743
|
|
1667
1744
|
if (input.charAt(i) !== '@') { return; }
|
1668
1745
|
|
@@ -1683,9 +1760,8 @@ less.Parser = function Parser(env) {
|
|
1683
1760
|
}
|
1684
1761
|
|
1685
1762
|
switch(nonVendorSpecificName) {
|
1763
|
+
/*
|
1686
1764
|
case "@font-face":
|
1687
|
-
hasBlock = true;
|
1688
|
-
break;
|
1689
1765
|
case "@viewport":
|
1690
1766
|
case "@top-left":
|
1691
1767
|
case "@top-left-corner":
|
@@ -1705,40 +1781,51 @@ less.Parser = function Parser(env) {
|
|
1705
1781
|
case "@right-bottom":
|
1706
1782
|
hasBlock = true;
|
1707
1783
|
break;
|
1708
|
-
|
1709
|
-
case "@
|
1710
|
-
case "@document":
|
1711
|
-
case "@supports":
|
1712
|
-
case "@keyframes":
|
1713
|
-
hasBlock = true;
|
1784
|
+
*/
|
1785
|
+
case "@charset":
|
1714
1786
|
hasIdentifier = true;
|
1787
|
+
hasBlock = false;
|
1715
1788
|
break;
|
1716
1789
|
case "@namespace":
|
1717
1790
|
hasExpression = true;
|
1791
|
+
hasBlock = false;
|
1792
|
+
break;
|
1793
|
+
case "@keyframes":
|
1794
|
+
hasIdentifier = true;
|
1795
|
+
break;
|
1796
|
+
case "@host":
|
1797
|
+
case "@page":
|
1798
|
+
case "@document":
|
1799
|
+
case "@supports":
|
1800
|
+
hasUnknown = true;
|
1718
1801
|
break;
|
1719
1802
|
}
|
1720
1803
|
|
1721
1804
|
if (hasIdentifier) {
|
1722
|
-
|
1723
|
-
if (
|
1724
|
-
|
1805
|
+
value = this.entity();
|
1806
|
+
if (!value) {
|
1807
|
+
error("expected " + name + " identifier");
|
1808
|
+
}
|
1809
|
+
} else if (hasExpression) {
|
1810
|
+
value = this.expression();
|
1811
|
+
if (!value) {
|
1812
|
+
error("expected " + name + " expression");
|
1813
|
+
}
|
1814
|
+
} else if (hasUnknown) {
|
1815
|
+
value = ($re(/^[^{;]+/) || '').trim();
|
1816
|
+
if (value) {
|
1817
|
+
value = new(tree.Anonymous)(value);
|
1725
1818
|
}
|
1726
1819
|
}
|
1727
1820
|
|
1728
1821
|
if (hasBlock) {
|
1729
|
-
rules = this.
|
1730
|
-
|
1731
|
-
|
1732
|
-
|
1733
|
-
|
1734
|
-
value
|
1735
|
-
|
1736
|
-
var directive = new(tree.Directive)(name, value, index, env.currentFileInfo);
|
1737
|
-
if (env.dumpLineNumbers) {
|
1738
|
-
directive.debugInfo = getDebugInfo(i, input, env);
|
1739
|
-
}
|
1740
|
-
return directive;
|
1741
|
-
}
|
1822
|
+
rules = this.blockRuleset();
|
1823
|
+
}
|
1824
|
+
|
1825
|
+
if (rules || (!hasBlock && value && $char(';'))) {
|
1826
|
+
forget();
|
1827
|
+
return new(tree.Directive)(name, value, rules, index, env.currentFileInfo,
|
1828
|
+
env.dumpLineNumbers ? getDebugInfo(index, input, env) : null);
|
1742
1829
|
}
|
1743
1830
|
|
1744
1831
|
restore();
|
@@ -1944,7 +2031,7 @@ less.Parser = function Parser(env) {
|
|
1944
2031
|
|
1945
2032
|
match(/^(\*?)/);
|
1946
2033
|
while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // !
|
1947
|
-
if ((name.length > 1) && match(/^\s*(
|
2034
|
+
if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) {
|
1948
2035
|
// at last, we have the complete match now. move forward,
|
1949
2036
|
// convert name particles to tree objects and return:
|
1950
2037
|
skipWhitespace(length);
|