less 2.6.0 → 2.7.2

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.
Files changed (144) hide show
  1. package/.travis.yml +3 -2
  2. package/CHANGELOG.md +30 -2
  3. package/Gruntfile.js +2 -3
  4. package/README.md +2 -2
  5. package/appveyor.yml +1 -0
  6. package/bin/lessc +47 -23
  7. package/dist/less.js +914 -582
  8. package/dist/less.min.js +9 -12
  9. package/lib/less/environment/abstract-file-manager.js +2 -2
  10. package/lib/less/functions/color.js +6 -3
  11. package/lib/less/index.js +1 -1
  12. package/lib/less/parser/parser-input.js +69 -69
  13. package/lib/less/parser/parser.js +111 -51
  14. package/lib/less/tree/anonymous.js +1 -1
  15. package/lib/less/tree/call.js +7 -4
  16. package/lib/less/tree/comment.js +2 -0
  17. package/lib/less/tree/directive.js +1 -0
  18. package/lib/less/tree/extend.js +1 -0
  19. package/lib/less/tree/import.js +1 -0
  20. package/lib/less/tree/media.js +1 -0
  21. package/lib/less/tree/mixin-call.js +1 -0
  22. package/lib/less/tree/mixin-definition.js +2 -1
  23. package/lib/less/tree/rule.js +1 -0
  24. package/lib/less/tree/ruleset-call.js +1 -0
  25. package/lib/less/tree/ruleset.js +86 -85
  26. package/lib/less/tree/unit.js +2 -2
  27. package/lib/less/visitors/extend-visitor.js +5 -5
  28. package/lib/less/visitors/to-css-visitor.js +20 -10
  29. package/lib/less-browser/error-reporting.js +10 -10
  30. package/lib/less-browser/index.js +48 -25
  31. package/lib/less-node/file-manager.js +1 -1
  32. package/lib/less-node/lessc-helper.js +2 -2
  33. package/lib/source-map/source-map-0.1.31.js +1 -1
  34. package/package.json +29 -22
  35. package/test/browser/common.js +29 -8
  36. package/test/browser/less/console-errors/test-error.txt +1 -1
  37. package/test/browser/less.js +13 -16
  38. package/test/browser/runner-production-options.js +1 -1
  39. package/test/css/comments2.css +6 -0
  40. package/test/css/no-strict-math/mixins-guards.css +12 -0
  41. package/test/css/no-strict-math/no-sm-operations.css +3 -0
  42. package/test/css/operations.css +1 -1
  43. package/test/css/plugin.css +5 -0
  44. package/test/less/comments.less +1 -1
  45. package/test/less/comments2.less +11 -0
  46. package/test/less/errors/detached-ruleset-3.txt +1 -1
  47. package/test/less/errors/functions-1.less +2 -0
  48. package/test/less/errors/functions-1.txt +3 -0
  49. package/test/less/errors/functions-10-keyword.less +2 -0
  50. package/test/less/errors/functions-10-keyword.txt +3 -0
  51. package/test/less/errors/functions-11-operation.less +2 -0
  52. package/test/less/errors/functions-11-operation.txt +3 -0
  53. package/test/less/errors/functions-12-quoted.less +2 -0
  54. package/test/less/errors/functions-12-quoted.txt +3 -0
  55. package/test/less/errors/functions-13-selector.less +2 -0
  56. package/test/less/errors/functions-13-selector.txt +3 -0
  57. package/test/less/errors/functions-14-url.less +2 -0
  58. package/test/less/errors/functions-14-url.txt +3 -0
  59. package/test/less/errors/functions-15-value.less +2 -0
  60. package/test/less/errors/functions-15-value.txt +3 -0
  61. package/test/less/errors/functions-2-alpha.less +2 -0
  62. package/test/less/errors/functions-2-alpha.txt +3 -0
  63. package/test/less/errors/functions-3-assignment.less +2 -0
  64. package/test/less/errors/functions-3-assignment.txt +3 -0
  65. package/test/less/errors/functions-4-call.less +2 -0
  66. package/test/less/errors/functions-4-call.txt +3 -0
  67. package/test/less/errors/functions-5-color-2.less +1 -0
  68. package/test/less/errors/functions-5-color-2.txt +2 -0
  69. package/test/less/errors/functions-5-color.less +2 -0
  70. package/test/less/errors/functions-5-color.txt +3 -0
  71. package/test/less/errors/functions-6-condition.less +2 -0
  72. package/test/less/errors/functions-6-condition.txt +3 -0
  73. package/test/less/errors/functions-7-dimension.less +2 -0
  74. package/test/less/errors/functions-7-dimension.txt +3 -0
  75. package/test/less/errors/functions-8-element.less +2 -0
  76. package/test/less/errors/functions-8-element.txt +3 -0
  77. package/test/less/errors/functions-9-expression.less +2 -0
  78. package/test/less/errors/functions-9-expression.txt +3 -0
  79. package/test/less/errors/property-in-root.txt +1 -1
  80. package/test/less/errors/property-in-root2.txt +1 -1
  81. package/test/less/errors/property-in-root3.txt +1 -1
  82. package/test/less/errors/root-func-undefined-1.less +1 -0
  83. package/test/less/errors/root-func-undefined-1.txt +2 -0
  84. package/test/less/errors/root-func-undefined-2.less +2 -0
  85. package/test/less/errors/root-func-undefined-2.txt +3 -0
  86. package/test/less/extend-chaining.less +1 -1
  87. package/test/less/merge.less +4 -4
  88. package/test/less/mixins-guards.less +1 -1
  89. package/test/less/no-strict-math/mixins-guards.less +25 -0
  90. package/test/less/no-strict-math/no-sm-operations.less +4 -1
  91. package/test/less/operations.less +1 -1
  92. package/test/less/plugin/plugin-tree-nodes.js +80 -0
  93. package/test/less/plugin.less +8 -0
  94. package/test/less-bom/comments.less +1 -1
  95. package/test/less-bom/comments2.less +11 -0
  96. package/test/less-bom/errors/detached-ruleset-3.txt +1 -1
  97. package/test/less-bom/errors/functions-1.less +2 -0
  98. package/test/less-bom/errors/functions-1.txt +3 -0
  99. package/test/less-bom/errors/functions-10-keyword.less +2 -0
  100. package/test/less-bom/errors/functions-10-keyword.txt +3 -0
  101. package/test/less-bom/errors/functions-11-operation.less +2 -0
  102. package/test/less-bom/errors/functions-11-operation.txt +3 -0
  103. package/test/less-bom/errors/functions-12-quoted.less +2 -0
  104. package/test/less-bom/errors/functions-12-quoted.txt +3 -0
  105. package/test/less-bom/errors/functions-13-selector.less +2 -0
  106. package/test/less-bom/errors/functions-13-selector.txt +3 -0
  107. package/test/less-bom/errors/functions-14-url.less +2 -0
  108. package/test/less-bom/errors/functions-14-url.txt +3 -0
  109. package/test/less-bom/errors/functions-15-value.less +2 -0
  110. package/test/less-bom/errors/functions-15-value.txt +3 -0
  111. package/test/less-bom/errors/functions-2-alpha.less +2 -0
  112. package/test/less-bom/errors/functions-2-alpha.txt +3 -0
  113. package/test/less-bom/errors/functions-3-assignment.less +2 -0
  114. package/test/less-bom/errors/functions-3-assignment.txt +3 -0
  115. package/test/less-bom/errors/functions-4-call.less +2 -0
  116. package/test/less-bom/errors/functions-4-call.txt +3 -0
  117. package/test/less-bom/errors/functions-5-color-2.less +1 -0
  118. package/test/less-bom/errors/functions-5-color-2.txt +2 -0
  119. package/test/less-bom/errors/functions-5-color.less +2 -0
  120. package/test/less-bom/errors/functions-5-color.txt +3 -0
  121. package/test/less-bom/errors/functions-6-condition.less +2 -0
  122. package/test/less-bom/errors/functions-6-condition.txt +3 -0
  123. package/test/less-bom/errors/functions-7-dimension.less +2 -0
  124. package/test/less-bom/errors/functions-7-dimension.txt +3 -0
  125. package/test/less-bom/errors/functions-8-element.less +2 -0
  126. package/test/less-bom/errors/functions-8-element.txt +3 -0
  127. package/test/less-bom/errors/functions-9-expression.less +2 -0
  128. package/test/less-bom/errors/functions-9-expression.txt +3 -0
  129. package/test/less-bom/errors/property-in-root.txt +1 -1
  130. package/test/less-bom/errors/property-in-root2.txt +1 -1
  131. package/test/less-bom/errors/property-in-root3.txt +1 -1
  132. package/test/less-bom/errors/root-func-undefined-1.less +1 -0
  133. package/test/less-bom/errors/root-func-undefined-1.txt +2 -0
  134. package/test/less-bom/errors/root-func-undefined-2.less +2 -0
  135. package/test/less-bom/errors/root-func-undefined-2.txt +3 -0
  136. package/test/less-bom/extend-chaining.less +1 -1
  137. package/test/less-bom/merge.less +4 -4
  138. package/test/less-bom/mixins-guards.less +1 -1
  139. package/test/less-bom/no-strict-math/mixins-guards.less +25 -0
  140. package/test/less-bom/no-strict-math/no-sm-operations.less +4 -1
  141. package/test/less-bom/operations.less +1 -1
  142. package/test/less-bom/plugin/plugin-tree-nodes.js +80 -0
  143. package/test/less-bom/plugin.less +8 -0
  144. package/test/sourcemaps/maps/import-map.map +1 -0
@@ -67,13 +67,13 @@ abstractFileManager.prototype.pathDiff = function pathDiff(url, baseUrl) {
67
67
  };
68
68
  // helper function, not part of API
69
69
  abstractFileManager.prototype.extractUrlParts = function extractUrlParts(url, baseUrl) {
70
- // urlParts[1] = protocol&hostname || /
70
+ // urlParts[1] = protocol://hostname/ OR /
71
71
  // urlParts[2] = / if path relative to host base
72
72
  // urlParts[3] = directories
73
73
  // urlParts[4] = filename
74
74
  // urlParts[5] = parameters
75
75
 
76
- var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i,
76
+ var urlPartsRegex = /^((?:[a-z-]+:)?\/{2}(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i,
77
77
  urlParts = url.match(urlPartsRegex),
78
78
  returner = {}, directories = [], i, baseUrlParts;
79
79
 
@@ -43,6 +43,9 @@ colorFunctions = {
43
43
  return colorFunctions.hsla(h, s, l, 1.0);
44
44
  },
45
45
  hsla: function (h, s, l, a) {
46
+
47
+ var m1, m2;
48
+
46
49
  function hue(h) {
47
50
  h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
48
51
  if (h * 6 < 1) {
@@ -62,8 +65,8 @@ colorFunctions = {
62
65
  h = (number(h) % 360) / 360;
63
66
  s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a));
64
67
 
65
- var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
66
- var m1 = l * 2 - m2;
68
+ m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
69
+ m1 = l * 2 - m2;
67
70
 
68
71
  return colorFunctions.rgba(hue(h + 1 / 3) * 255,
69
72
  hue(h) * 255,
@@ -234,7 +237,7 @@ colorFunctions = {
234
237
  return hsla(hsl);
235
238
  },
236
239
  //
237
- // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
240
+ // Copyright (c) 2006-2009 Hampton Catlin, Natalie Weizenbaum, and Chris Eppstein
238
241
  // http://sass-lang.com
239
242
  //
240
243
  mix: function (color1, color2, weight) {
package/lib/less/index.js CHANGED
@@ -2,7 +2,7 @@ module.exports = function(environment, fileManagers) {
2
2
  var SourceMapOutput, SourceMapBuilder, ParseTree, ImportManager, Environment;
3
3
 
4
4
  var less = {
5
- version: [2, 6, 0],
5
+ version: [2, 7, 2],
6
6
  data: require('./data'),
7
7
  tree: require('./tree'),
8
8
  Environment: (Environment = require("./environment/environment")),
@@ -11,6 +11,74 @@ module.exports = function() {
11
11
  currentPos, // index of current chunk, in `input`
12
12
  parserInput = {};
13
13
 
14
+ var CHARCODE_SPACE = 32,
15
+ CHARCODE_TAB = 9,
16
+ CHARCODE_LF = 10,
17
+ CHARCODE_CR = 13,
18
+ CHARCODE_PLUS = 43,
19
+ CHARCODE_COMMA = 44,
20
+ CHARCODE_FORWARD_SLASH = 47,
21
+ CHARCODE_9 = 57;
22
+
23
+ function skipWhitespace(length) {
24
+ var oldi = parserInput.i, oldj = j,
25
+ curr = parserInput.i - currentPos,
26
+ endIndex = parserInput.i + current.length - curr,
27
+ mem = (parserInput.i += length),
28
+ inp = input,
29
+ c, nextChar, comment;
30
+
31
+ for (; parserInput.i < endIndex; parserInput.i++) {
32
+ c = inp.charCodeAt(parserInput.i);
33
+
34
+ if (parserInput.autoCommentAbsorb && c === CHARCODE_FORWARD_SLASH) {
35
+ nextChar = inp.charAt(parserInput.i + 1);
36
+ if (nextChar === '/') {
37
+ comment = {index: parserInput.i, isLineComment: true};
38
+ var nextNewLine = inp.indexOf("\n", parserInput.i + 2);
39
+ if (nextNewLine < 0) {
40
+ nextNewLine = endIndex;
41
+ }
42
+ parserInput.i = nextNewLine;
43
+ comment.text = inp.substr(comment.index, parserInput.i - comment.index);
44
+ parserInput.commentStore.push(comment);
45
+ continue;
46
+ } else if (nextChar === '*') {
47
+ var nextStarSlash = inp.indexOf("*/", parserInput.i + 2);
48
+ if (nextStarSlash >= 0) {
49
+ comment = {
50
+ index: parserInput.i,
51
+ text: inp.substr(parserInput.i, nextStarSlash + 2 - parserInput.i),
52
+ isLineComment: false
53
+ };
54
+ parserInput.i += comment.text.length - 1;
55
+ parserInput.commentStore.push(comment);
56
+ continue;
57
+ }
58
+ }
59
+ break;
60
+ }
61
+
62
+ if ((c !== CHARCODE_SPACE) && (c !== CHARCODE_LF) && (c !== CHARCODE_TAB) && (c !== CHARCODE_CR)) {
63
+ break;
64
+ }
65
+ }
66
+
67
+ current = current.slice(length + parserInput.i - mem + curr);
68
+ currentPos = parserInput.i;
69
+
70
+ if (!current.length) {
71
+ if (j < chunks.length - 1) {
72
+ current = chunks[++j];
73
+ skipWhitespace(0); // skip space at the beginning of a chunk
74
+ return true; // things changed
75
+ }
76
+ parserInput.finished = true;
77
+ }
78
+
79
+ return oldi !== parserInput.i || oldj !== j;
80
+ }
81
+
14
82
  parserInput.save = function() {
15
83
  currentPos = parserInput.i;
16
84
  saveStack.push( { current: current, i: parserInput.i, j: j });
@@ -105,78 +173,10 @@ module.exports = function() {
105
173
  return null;
106
174
  };
107
175
 
108
- var CHARCODE_SPACE = 32,
109
- CHARCODE_TAB = 9,
110
- CHARCODE_LF = 10,
111
- CHARCODE_CR = 13,
112
- CHARCODE_PLUS = 43,
113
- CHARCODE_COMMA = 44,
114
- CHARCODE_FORWARD_SLASH = 47,
115
- CHARCODE_9 = 57;
116
-
117
176
  parserInput.autoCommentAbsorb = true;
118
177
  parserInput.commentStore = [];
119
178
  parserInput.finished = false;
120
179
 
121
- var skipWhitespace = function(length) {
122
- var oldi = parserInput.i, oldj = j,
123
- curr = parserInput.i - currentPos,
124
- endIndex = parserInput.i + current.length - curr,
125
- mem = (parserInput.i += length),
126
- inp = input,
127
- c, nextChar, comment;
128
-
129
- for (; parserInput.i < endIndex; parserInput.i++) {
130
- c = inp.charCodeAt(parserInput.i);
131
-
132
- if (parserInput.autoCommentAbsorb && c === CHARCODE_FORWARD_SLASH) {
133
- nextChar = inp.charAt(parserInput.i + 1);
134
- if (nextChar === '/') {
135
- comment = {index: parserInput.i, isLineComment: true};
136
- var nextNewLine = inp.indexOf("\n", parserInput.i + 2);
137
- if (nextNewLine < 0) {
138
- nextNewLine = endIndex;
139
- }
140
- parserInput.i = nextNewLine;
141
- comment.text = inp.substr(comment.i, parserInput.i - comment.i);
142
- parserInput.commentStore.push(comment);
143
- continue;
144
- } else if (nextChar === '*') {
145
- var nextStarSlash = inp.indexOf("*/", parserInput.i + 2);
146
- if (nextStarSlash >= 0) {
147
- comment = {
148
- index: parserInput.i,
149
- text: inp.substr(parserInput.i, nextStarSlash + 2 - parserInput.i),
150
- isLineComment: false
151
- };
152
- parserInput.i += comment.text.length - 1;
153
- parserInput.commentStore.push(comment);
154
- continue;
155
- }
156
- }
157
- break;
158
- }
159
-
160
- if ((c !== CHARCODE_SPACE) && (c !== CHARCODE_LF) && (c !== CHARCODE_TAB) && (c !== CHARCODE_CR)) {
161
- break;
162
- }
163
- }
164
-
165
- current = current.slice(length + parserInput.i - mem + curr);
166
- currentPos = parserInput.i;
167
-
168
- if (!current.length) {
169
- if (j < chunks.length - 1) {
170
- current = chunks[++j];
171
- skipWhitespace(0); // skip space at the beginning of a chunk
172
- return true; // things changed
173
- }
174
- parserInput.finished = true;
175
- }
176
-
177
- return oldi !== parserInput.i || oldj !== j;
178
- };
179
-
180
180
  // Same as $(), but don't change the state of the parser,
181
181
  // just return the match.
182
182
  parserInput.peek = function(tok) {
@@ -217,7 +217,7 @@ module.exports = function() {
217
217
  input = str;
218
218
  parserInput.i = j = currentPos = furthest = 0;
219
219
 
220
- // chunking apparantly makes things quicker (but my tests indicate
220
+ // chunking apparently makes things quicker (but my tests indicate
221
221
  // it might actually make things slower in node at least)
222
222
  // and it is a non-perfect parse - it can't recognise
223
223
  // unquoted urls, meaning it can't distinguish comments
@@ -41,9 +41,21 @@ var Parser = function Parser(context, imports, fileInfo) {
41
41
  var parsers,
42
42
  parserInput = getParserInput();
43
43
 
44
+ function error(msg, type) {
45
+ throw new LessError(
46
+ {
47
+ index: parserInput.i,
48
+ filename: fileInfo.filename,
49
+ type: type || 'Syntax',
50
+ message: msg
51
+ },
52
+ imports
53
+ );
54
+ }
55
+
44
56
  function expect(arg, msg, index) {
45
57
  // some older browsers return typeof 'function' for RegExp
46
- var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : parserInput.$re(arg);
58
+ var result = (arg instanceof Function) ? arg.call(parsers) : parserInput.$re(arg);
47
59
  if (result) {
48
60
  return result;
49
61
  }
@@ -59,18 +71,6 @@ var Parser = function Parser(context, imports, fileInfo) {
59
71
  error(msg || "expected '" + arg + "' got '" + parserInput.currentChar() + "'");
60
72
  }
61
73
 
62
- function error(msg, type) {
63
- throw new LessError(
64
- {
65
- index: parserInput.i,
66
- filename: fileInfo.filename,
67
- type: type || 'Syntax',
68
- message: msg
69
- },
70
- imports
71
- );
72
- }
73
-
74
74
  function getDebugInfo(index) {
75
75
  var filename = fileInfo.filename;
76
76
 
@@ -261,7 +261,7 @@ var Parser = function Parser(context, imports, fileInfo) {
261
261
  }
262
262
 
263
263
  node = mixin.definition() || this.rule() || this.ruleset() ||
264
- mixin.call() || this.rulesetCall() || this.directive();
264
+ mixin.call() || this.rulesetCall() || this.entities.call() || this.directive();
265
265
  if (node) {
266
266
  root.push(node);
267
267
  } else {
@@ -325,26 +325,6 @@ var Parser = function Parser(context, imports, fileInfo) {
325
325
  }
326
326
  },
327
327
 
328
- namedColor: function () {
329
- parserInput.save();
330
- var autoCommentAbsorb = parserInput.autoCommentAbsorb;
331
- parserInput.autoCommentAbsorb = false;
332
- var k = parserInput.$re(/^[_A-Za-z-][_A-Za-z0-9-]*/);
333
- parserInput.autoCommentAbsorb = autoCommentAbsorb;
334
- if (!k) {
335
- parserInput.forget();
336
- return ;
337
- }
338
- var result = tree.Color.fromKeyword(k);
339
- if (!result) {
340
- parserInput.restore();
341
- } else {
342
- parserInput.forget();
343
- return result;
344
- }
345
-
346
- },
347
-
348
328
  //
349
329
  // A function call
350
330
  //
@@ -390,19 +370,51 @@ var Parser = function Parser(context, imports, fileInfo) {
390
370
  return new(tree.Call)(name, args, index, fileInfo);
391
371
  },
392
372
  arguments: function () {
393
- var args = [], arg;
373
+ var argsSemiColon = [], argsComma = [],
374
+ expressions = [],
375
+ isSemiColonSeparated, value, arg;
376
+
377
+ parserInput.save();
394
378
 
395
379
  while (true) {
396
- arg = this.assignment() || parsers.expression();
380
+
381
+ arg = parsers.detachedRuleset() || this.assignment() || parsers.expression();
382
+
397
383
  if (!arg) {
398
384
  break;
399
385
  }
400
- args.push(arg);
401
- if (! parserInput.$char(',')) {
402
- break;
386
+
387
+ value = arg;
388
+
389
+ if (arg.value && arg.value.length == 1) {
390
+ value = arg.value[0];
391
+ }
392
+
393
+ if (value) {
394
+ expressions.push(value);
395
+ }
396
+
397
+ argsComma.push(value);
398
+
399
+ if (parserInput.$char(',')) {
400
+ continue;
401
+ }
402
+
403
+ if (parserInput.$char(';') || isSemiColonSeparated) {
404
+
405
+ isSemiColonSeparated = true;
406
+
407
+ if (expressions.length > 1) {
408
+ value = new(tree.Value)(expressions);
409
+ }
410
+ argsSemiColon.push(value);
411
+
412
+ expressions = [];
403
413
  }
404
414
  }
405
- return args;
415
+
416
+ parserInput.forget();
417
+ return isSemiColonSeparated ? argsSemiColon : argsComma;
406
418
  },
407
419
  literal: function () {
408
420
  return this.dimension() ||
@@ -482,7 +494,7 @@ var Parser = function Parser(context, imports, fileInfo) {
482
494
  }
483
495
  },
484
496
 
485
- // A variable entity useing the protective {} e.g. @{var}
497
+ // A variable entity using the protective {} e.g. @{var}
486
498
  variableCurly: function () {
487
499
  var curly, index = parserInput.i;
488
500
 
@@ -511,8 +523,24 @@ var Parser = function Parser(context, imports, fileInfo) {
511
523
  }
512
524
  return new(tree.Color)(rgb[1], undefined, '#' + colorCandidateString);
513
525
  }
526
+ },
514
527
 
515
- return this.namedColor();
528
+ colorKeyword: function () {
529
+ parserInput.save();
530
+ var autoCommentAbsorb = parserInput.autoCommentAbsorb;
531
+ parserInput.autoCommentAbsorb = false;
532
+ var k = parserInput.$re(/^[_A-Za-z-][_A-Za-z0-9-]+/);
533
+ parserInput.autoCommentAbsorb = autoCommentAbsorb;
534
+ if (!k) {
535
+ parserInput.forget();
536
+ return;
537
+ }
538
+ parserInput.restore();
539
+ var color = tree.Color.fromKeyword(k);
540
+ if (color) {
541
+ parserInput.$str(k);
542
+ return color;
543
+ }
516
544
  },
517
545
 
518
546
  //
@@ -591,7 +619,7 @@ var Parser = function Parser(context, imports, fileInfo) {
591
619
  rulesetCall: function () {
592
620
  var name;
593
621
 
594
- if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) {
622
+ if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\(\s*\)\s*;/))) {
595
623
  return new tree.RulesetCall(name[1]);
596
624
  }
597
625
  },
@@ -903,7 +931,7 @@ var Parser = function Parser(context, imports, fileInfo) {
903
931
  //
904
932
  // A Rule terminator. Note that we use `peek()` to check for '}',
905
933
  // because the `block` rule will be expecting it, but we still need to make sure
906
- // it's there, if ';' was ommitted.
934
+ // it's there, if ';' was omitted.
907
935
  //
908
936
  end: function () {
909
937
  return parserInput.$char(';') || parserInput.peek('}');
@@ -1325,10 +1353,10 @@ var Parser = function Parser(context, imports, fileInfo) {
1325
1353
  },
1326
1354
 
1327
1355
  media: function () {
1328
- var features, rules, media, debugInfo;
1356
+ var features, rules, media, debugInfo, index = parserInput.i;
1329
1357
 
1330
1358
  if (context.dumpLineNumbers) {
1331
- debugInfo = getDebugInfo(parserInput.i);
1359
+ debugInfo = getDebugInfo(index);
1332
1360
  }
1333
1361
 
1334
1362
  parserInput.save();
@@ -1344,7 +1372,7 @@ var Parser = function Parser(context, imports, fileInfo) {
1344
1372
 
1345
1373
  parserInput.forget();
1346
1374
 
1347
- media = new(tree.Media)(rules, features, parserInput.i, fileInfo);
1375
+ media = new(tree.Media)(rules, features, index, fileInfo);
1348
1376
  if (context.dumpLineNumbers) {
1349
1377
  media.debugInfo = debugInfo;
1350
1378
  }
@@ -1644,12 +1672,44 @@ var Parser = function Parser(context, imports, fileInfo) {
1644
1672
  }
1645
1673
  },
1646
1674
  parenthesisCondition: function () {
1675
+ function tryConditionFollowedByParenthesis(me) {
1676
+ var body;
1677
+ parserInput.save();
1678
+ body = me.condition();
1679
+ if (!body) {
1680
+ parserInput.restore();
1681
+ return ;
1682
+ }
1683
+ if (!parserInput.$char(')')) {
1684
+ parserInput.restore();
1685
+ return ;
1686
+ }
1687
+ parserInput.forget();
1688
+ return body;
1689
+ }
1690
+
1647
1691
  var body;
1692
+ parserInput.save();
1648
1693
  if (!parserInput.$str("(")) {
1694
+ parserInput.restore();
1649
1695
  return ;
1650
1696
  }
1651
- body = this.condition() || this.atomicCondition();
1652
- expectChar(')');
1697
+ body = tryConditionFollowedByParenthesis(this);
1698
+ if (body) {
1699
+ parserInput.forget();
1700
+ return body;
1701
+ }
1702
+
1703
+ body = this.atomicCondition();
1704
+ if (!body) {
1705
+ parserInput.restore();
1706
+ return ;
1707
+ }
1708
+ if (!parserInput.$char(')')) {
1709
+ parserInput.restore("expected ')' got '" + parserInput.currentChar() + "'");
1710
+ return ;
1711
+ }
1712
+ parserInput.forget();
1653
1713
  return body;
1654
1714
  },
1655
1715
  atomicCondition: function () {
@@ -1706,8 +1766,8 @@ var Parser = function Parser(context, imports, fileInfo) {
1706
1766
  }
1707
1767
 
1708
1768
  var o = this.sub() || entities.dimension() ||
1709
- entities.variable() ||
1710
- entities.call() || entities.color();
1769
+ entities.color() || entities.variable() ||
1770
+ entities.call() || entities.colorKeyword();
1711
1771
 
1712
1772
  if (negate) {
1713
1773
  o.parensInOp = true;
@@ -6,7 +6,7 @@ var Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike,
6
6
  this.mapLines = mapLines;
7
7
  this.currentFileInfo = currentFileInfo;
8
8
  this.rulesetLike = (typeof rulesetLike === 'undefined') ? false : rulesetLike;
9
-
9
+ this.allowRoot = true;
10
10
  this.copyVisibilityInfo(visibilityInfo);
11
11
  };
12
12
  Anonymous.prototype = new Node();
@@ -31,18 +31,21 @@ Call.prototype.eval = function (context) {
31
31
  var args = this.args.map(function (a) { return a.eval(context); }),
32
32
  result, funcCaller = new FunctionCaller(this.name, context, this.index, this.currentFileInfo);
33
33
 
34
- if (funcCaller.isValid()) { // 1.
34
+ if (funcCaller.isValid()) {
35
35
  try {
36
36
  result = funcCaller.call(args);
37
- if (result != null) {
38
- return result;
39
- }
40
37
  } catch (e) {
41
38
  throw { type: e.type || "Runtime",
42
39
  message: "error evaluating function `" + this.name + "`" +
43
40
  (e.message ? ': ' + e.message : ''),
44
41
  index: this.index, filename: this.currentFileInfo.filename };
45
42
  }
43
+
44
+ if (result != null) {
45
+ result.index = this.index;
46
+ result.currentFileInfo = this.currentFileInfo;
47
+ return result;
48
+ }
46
49
  }
47
50
 
48
51
  return new Call(this.name, args, this.index, this.currentFileInfo);
@@ -4,7 +4,9 @@ var Node = require("./node"),
4
4
  var Comment = function (value, isLineComment, index, currentFileInfo) {
5
5
  this.value = value;
6
6
  this.isLineComment = isLineComment;
7
+ this.index = index;
7
8
  this.currentFileInfo = currentFileInfo;
9
+ this.allowRoot = true;
8
10
  };
9
11
  Comment.prototype = new Node();
10
12
  Comment.prototype.type = "Comment";
@@ -23,6 +23,7 @@ var Directive = function (name, value, rules, index, currentFileInfo, debugInfo,
23
23
  this.debugInfo = debugInfo;
24
24
  this.isRooted = isRooted || false;
25
25
  this.copyVisibilityInfo(visibilityInfo);
26
+ this.allowRoot = true;
26
27
  };
27
28
 
28
29
  Directive.prototype = new Node();
@@ -9,6 +9,7 @@ var Extend = function Extend(selector, option, index, currentFileInfo, visibilit
9
9
  this.parent_ids = [this.object_id];
10
10
  this.currentFileInfo = currentFileInfo || {};
11
11
  this.copyVisibilityInfo(visibilityInfo);
12
+ this.allowRoot = true;
12
13
 
13
14
  switch(option) {
14
15
  case "all":
@@ -23,6 +23,7 @@ var Import = function (path, features, options, index, currentFileInfo, visibili
23
23
  this.path = path;
24
24
  this.features = features;
25
25
  this.currentFileInfo = currentFileInfo;
26
+ this.allowRoot = true;
26
27
 
27
28
  if (this.options.less !== undefined || this.options.inline) {
28
29
  this.css = !this.options.less || this.options.inline;
@@ -15,6 +15,7 @@ var Media = function (value, features, index, currentFileInfo, visibilityInfo) {
15
15
  this.rules = [new Ruleset(selectors, value)];
16
16
  this.rules[0].allowImports = true;
17
17
  this.copyVisibilityInfo(visibilityInfo);
18
+ this.allowRoot = true;
18
19
  };
19
20
  Media.prototype = new Directive();
20
21
  Media.prototype.type = "Media";
@@ -9,6 +9,7 @@ var MixinCall = function (elements, args, index, currentFileInfo, important) {
9
9
  this.index = index;
10
10
  this.currentFileInfo = currentFileInfo;
11
11
  this.important = important;
12
+ this.allowRoot = true;
12
13
  };
13
14
  MixinCall.prototype = new Node();
14
15
  MixinCall.prototype.type = "MixinCall";
@@ -27,6 +27,7 @@ var Definition = function (name, params, rules, condition, variadic, frames, vis
27
27
  this.optionalParameters = optionalParameters;
28
28
  this.frames = frames;
29
29
  this.copyVisibilityInfo(visibilityInfo);
30
+ this.allowRoot = true;
30
31
  };
31
32
  Definition.prototype = new Ruleset();
32
33
  Definition.prototype.type = "MixinDefinition";
@@ -127,7 +128,7 @@ Definition.prototype.makeImportant = function() {
127
128
  return r;
128
129
  }
129
130
  });
130
- var result = new Definition (this.name, this.params, rules, this.condition, this.variadic, this.frames);
131
+ var result = new Definition(this.name, this.params, rules, this.condition, this.variadic, this.frames);
131
132
  return result;
132
133
  };
133
134
  Definition.prototype.eval = function (context) {
@@ -12,6 +12,7 @@ var Rule = function (name, value, important, merge, index, currentFileInfo, inli
12
12
  this.inline = inline || false;
13
13
  this.variable = (variable !== undefined) ? variable
14
14
  : (name.charAt && (name.charAt(0) === '@'));
15
+ this.allowRoot = true;
15
16
  };
16
17
 
17
18
  function evalName(context, name) {
@@ -3,6 +3,7 @@ var Node = require("./node"),
3
3
 
4
4
  var RulesetCall = function (variable) {
5
5
  this.variable = variable;
6
+ this.allowRoot = true;
6
7
  };
7
8
  RulesetCall.prototype = new Node();
8
9
  RulesetCall.prototype.type = "RulesetCall";