less 2.5.1 → 2.6.1

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 (150) hide show
  1. package/.travis.yml +3 -2
  2. package/CHANGELOG.md +41 -0
  3. package/Gruntfile.js +5 -10
  4. package/README.md +3 -3
  5. package/appveyor.yml +2 -2
  6. package/bin/lessc +1 -0
  7. package/bower.json +1 -2
  8. package/dist/less.js +969 -605
  9. package/dist/less.min.js +13 -9
  10. package/gradlew +0 -0
  11. package/lib/less/contexts.js +0 -1
  12. package/lib/less/functions/color.js +5 -2
  13. package/lib/less/functions/math-helper.js +16 -0
  14. package/lib/less/functions/math.js +4 -16
  15. package/lib/less/functions/number.js +7 -2
  16. package/lib/less/index.js +1 -1
  17. package/lib/less/parser/parser-input.js +68 -68
  18. package/lib/less/parser/parser.js +159 -75
  19. package/lib/less/transform-tree.js +1 -0
  20. package/lib/less/tree/anonymous.js +4 -2
  21. package/lib/less/tree/color.js +4 -1
  22. package/lib/less/tree/comment.js +2 -6
  23. package/lib/less/tree/directive.js +3 -18
  24. package/lib/less/tree/element.js +9 -2
  25. package/lib/less/tree/extend.js +10 -5
  26. package/lib/less/tree/import.js +23 -5
  27. package/lib/less/tree/media.js +4 -2
  28. package/lib/less/tree/mixin-call.js +31 -18
  29. package/lib/less/tree/mixin-definition.js +19 -7
  30. package/lib/less/tree/node.js +49 -0
  31. package/lib/less/tree/rule.js +1 -1
  32. package/lib/less/tree/ruleset.js +107 -125
  33. package/lib/less/tree/selector.js +4 -9
  34. package/lib/less/tree/unit.js +2 -2
  35. package/lib/less/visitors/extend-visitor.js +17 -8
  36. package/lib/less/visitors/import-visitor.js +5 -0
  37. package/lib/less/visitors/index.js +1 -0
  38. package/lib/less/visitors/set-tree-visibility-visitor.js +38 -0
  39. package/lib/less/visitors/to-css-visitor.js +181 -98
  40. package/lib/less-browser/bootstrap.js +28 -2
  41. package/lib/less-browser/cache.js +11 -4
  42. package/lib/less-browser/error-reporting.js +10 -10
  43. package/lib/less-browser/file-manager.js +1 -1
  44. package/lib/less-browser/image-size.js +28 -0
  45. package/lib/less-browser/index.js +9 -10
  46. package/package.json +14 -14
  47. package/test/browser/less/errors/image-height-error.less +3 -0
  48. package/test/browser/less/errors/image-height-error.txt +4 -0
  49. package/test/browser/less/errors/image-size-error.less +3 -0
  50. package/test/browser/less/errors/image-size-error.txt +4 -0
  51. package/test/browser/less/errors/image-width-error.less +3 -0
  52. package/test/browser/less/errors/image-width-error.txt +4 -0
  53. package/test/browser/less.js +23 -19
  54. package/test/css/comments2.css +7 -1
  55. package/test/css/compression/compression.css +1 -1
  56. package/test/css/css-3.css +6 -0
  57. package/test/css/extend-selector.css +7 -0
  58. package/test/css/extract-and-length.css +1 -1
  59. package/test/css/functions.css +3 -3
  60. package/test/css/import-reference-issues.css +24 -0
  61. package/test/css/import-reference.css +5 -1
  62. package/test/css/include-path/include-path.css +1 -1
  63. package/test/css/include-path-string/include-path-string.css +1 -1
  64. package/test/css/mixins-args.css +51 -1
  65. package/test/css/mixins-guards.css +36 -0
  66. package/test/css/no-strict-math/mixins-guards.css +12 -0
  67. package/test/css/no-strict-math/no-sm-operations.css +12 -0
  68. package/test/css/operations.css +1 -0
  69. package/test/css/scope.css +1 -1
  70. package/test/css/strings.css +1 -1
  71. package/test/css/url-args/urls.css +5 -5
  72. package/test/css/urls.css +8 -8
  73. package/test/css/variables.css +5 -5
  74. package/test/data/image.jpg +0 -0
  75. package/test/data/image.svg +2 -1
  76. package/test/data/page.html +1 -1
  77. package/test/index.js +1 -0
  78. package/test/less/comments2.less +11 -0
  79. package/test/less/css-3.less +10 -2
  80. package/test/less/errors/at-rules-undefined-var.txt +4 -4
  81. package/test/less/errors/css-guard-default-func.txt +4 -4
  82. package/test/less/errors/javascript-undefined-var.txt +4 -4
  83. package/test/less/errors/mixins-guards-default-func-1.txt +4 -4
  84. package/test/less/errors/mixins-guards-default-func-2.txt +4 -4
  85. package/test/less/errors/mixins-guards-default-func-3.txt +4 -4
  86. package/test/less/errors/parse-error-media-no-block-1.txt +1 -1
  87. package/test/less/errors/parse-error-media-no-block-2.txt +1 -1
  88. package/test/less/errors/parse-error-media-no-block-3.txt +1 -1
  89. package/test/less/errors/percentage-non-number-argument.less +3 -0
  90. package/test/less/errors/percentage-non-number-argument.txt +4 -0
  91. package/test/less/errors/property-interp-not-defined.txt +2 -2
  92. package/test/less/extend-selector.less +12 -1
  93. package/test/less/import/import-inline-invalid-css.less +1 -0
  94. package/test/less/import/import-reference.less +10 -1
  95. package/test/less/import-reference-issues/appender-reference-1968.less +6 -0
  96. package/test/less/import-reference-issues/global-scope-import.less +13 -0
  97. package/test/less/import-reference-issues/global-scope-nested.less +3 -0
  98. package/test/less/import-reference-issues/mixin-1968.less +8 -0
  99. package/test/less/import-reference-issues/multiple-import-nested.less +12 -0
  100. package/test/less/import-reference-issues/multiple-import.less +10 -0
  101. package/test/less/import-reference-issues/simple-mixin.css +3 -0
  102. package/test/less/import-reference-issues/simple-ruleset-2162.less +3 -0
  103. package/test/less/import-reference-issues.less +50 -0
  104. package/test/less/import-reference.less +2 -0
  105. package/test/less/mixins-args.less +48 -0
  106. package/test/less/mixins-guards.less +78 -0
  107. package/test/less/no-strict-math/mixins-guards.less +25 -0
  108. package/test/less/no-strict-math/no-sm-operations.less +10 -0
  109. package/test/less/operations.less +1 -0
  110. package/test/less-bom/comments2.less +11 -0
  111. package/test/less-bom/css-3.less +10 -2
  112. package/test/less-bom/errors/at-rules-undefined-var.txt +4 -4
  113. package/test/less-bom/errors/css-guard-default-func.txt +4 -4
  114. package/test/less-bom/errors/javascript-undefined-var.txt +4 -4
  115. package/test/less-bom/errors/mixins-guards-default-func-1.txt +4 -4
  116. package/test/less-bom/errors/mixins-guards-default-func-2.txt +4 -4
  117. package/test/less-bom/errors/mixins-guards-default-func-3.txt +4 -4
  118. package/test/less-bom/errors/parse-error-media-no-block-1.txt +1 -1
  119. package/test/less-bom/errors/parse-error-media-no-block-2.txt +1 -1
  120. package/test/less-bom/errors/parse-error-media-no-block-3.txt +1 -1
  121. package/test/less-bom/errors/percentage-non-number-argument.less +3 -0
  122. package/test/less-bom/errors/percentage-non-number-argument.txt +4 -0
  123. package/test/less-bom/errors/property-interp-not-defined.txt +2 -2
  124. package/test/less-bom/extend-selector.less +12 -1
  125. package/test/less-bom/import/import-inline-invalid-css.less +1 -0
  126. package/test/less-bom/import/import-reference.less +10 -1
  127. package/test/less-bom/import-reference-issues/appender-reference-1968.less +6 -0
  128. package/test/less-bom/import-reference-issues/global-scope-import.less +13 -0
  129. package/test/less-bom/import-reference-issues/global-scope-nested.less +3 -0
  130. package/test/less-bom/import-reference-issues/mixin-1968.less +8 -0
  131. package/test/less-bom/import-reference-issues/multiple-import-nested.less +12 -0
  132. package/test/less-bom/import-reference-issues/multiple-import.less +10 -0
  133. package/test/less-bom/import-reference-issues/simple-mixin.css +3 -0
  134. package/test/less-bom/import-reference-issues/simple-ruleset-2162.less +3 -0
  135. package/test/less-bom/import-reference-issues.less +50 -0
  136. package/test/less-bom/import-reference.less +2 -0
  137. package/test/less-bom/mixins-args.less +48 -0
  138. package/test/less-bom/mixins-guards.less +78 -0
  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 +10 -0
  141. package/test/less-bom/operations.less +1 -0
  142. package/test/less-test.js +6 -1
  143. package/.idea/.name +0 -1
  144. package/.idea/jsLibraryMappings.xml +0 -6
  145. package/.idea/less.js.iml +0 -9
  146. package/.idea/libraries/less_js_node_modules.xml +0 -14
  147. package/.idea/misc.xml +0 -14
  148. package/.idea/modules.xml +0 -8
  149. package/.idea/vcs.xml +0 -6
  150. package/.idea/workspace.xml +0 -281
package/gradlew CHANGED
File without changes
@@ -30,7 +30,6 @@ var parseCopyProperties = [
30
30
  // context
31
31
  'processImports', // option & context - whether to process imports. if false then imports will not be imported.
32
32
  // Used by the import manager to stop multiple import visitors being created.
33
- 'reference', // Used to indicate that the contents are imported by reference
34
33
  'pluginManager' // Used as the plugin manager for the session
35
34
  ];
36
35
 
@@ -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,
@@ -0,0 +1,16 @@
1
+ var Dimension = require("../tree/dimension");
2
+
3
+ var MathHelper = function() {
4
+ };
5
+ MathHelper._math = function (fn, unit, n) {
6
+ if (!(n instanceof Dimension)) {
7
+ throw { type: "Argument", message: "argument must be a number" };
8
+ }
9
+ if (unit == null) {
10
+ unit = n.unit;
11
+ } else {
12
+ n = n.unify();
13
+ }
14
+ return new Dimension(fn(parseFloat(n.value)), unit);
15
+ };
16
+ module.exports = MathHelper;
@@ -1,5 +1,5 @@
1
- var Dimension = require("../tree/dimension"),
2
- functionRegistry = require("./function-registry");
1
+ var functionRegistry = require("./function-registry"),
2
+ mathHelper = require("./math-helper.js");
3
3
 
4
4
  var mathFunctions = {
5
5
  // name, unit
@@ -15,27 +15,15 @@ var mathFunctions = {
15
15
  acos: "rad"
16
16
  };
17
17
 
18
- function _math(fn, unit, n) {
19
- if (!(n instanceof Dimension)) {
20
- throw { type: "Argument", message: "argument must be a number" };
21
- }
22
- if (unit == null) {
23
- unit = n.unit;
24
- } else {
25
- n = n.unify();
26
- }
27
- return new Dimension(fn(parseFloat(n.value)), unit);
28
- }
29
-
30
18
  for (var f in mathFunctions) {
31
19
  if (mathFunctions.hasOwnProperty(f)) {
32
- mathFunctions[f] = _math.bind(null, Math[f], mathFunctions[f]);
20
+ mathFunctions[f] = mathHelper._math.bind(null, Math[f], mathFunctions[f]);
33
21
  }
34
22
  }
35
23
 
36
24
  mathFunctions.round = function (n, f) {
37
25
  var fraction = typeof f === "undefined" ? 0 : f.value;
38
- return _math(function(num) { return num.toFixed(fraction); }, null, n);
26
+ return mathHelper._math(function(num) { return num.toFixed(fraction); }, null, n);
39
27
  };
40
28
 
41
29
  functionRegistry.addMultiple(mathFunctions);
@@ -1,6 +1,7 @@
1
1
  var Dimension = require("../tree/dimension"),
2
2
  Anonymous = require("../tree/anonymous"),
3
- functionRegistry = require("./function-registry");
3
+ functionRegistry = require("./function-registry"),
4
+ mathHelper = require("./math-helper.js");
4
5
 
5
6
  var minMax = function (isMin, args) {
6
7
  args = Array.prototype.slice.call(args);
@@ -71,6 +72,10 @@ functionRegistry.addMultiple({
71
72
  return new Dimension(Math.pow(x.value, y.value), x.unit);
72
73
  },
73
74
  percentage: function (n) {
74
- return new Dimension(n.value * 100, '%');
75
+ var result = mathHelper._math(function(num) {
76
+ return num * 100;
77
+ }, '%', n);
78
+
79
+ return result;
75
80
  }
76
81
  });
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, 5, 1],
5
+ version: [2, 6, 1],
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.i, parserInput.i - comment.i);
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) {
@@ -35,15 +35,27 @@ var LessError = require('../less-error'),
35
35
  // Token matching is done with the `$` function, which either takes
36
36
  // a terminal string or regexp, or a non-terminal function to call.
37
37
  // It also takes care of moving all the indices forwards.
38
- //
38
+ //`
39
39
  //
40
40
  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
 
@@ -489,7 +489,25 @@ var Parser = function Parser(context, imports, fileInfo) {
489
489
  if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters
490
490
  error("Invalid HEX color code");
491
491
  }
492
- return new(tree.Color)(rgb[1]);
492
+ return new(tree.Color)(rgb[1], undefined, '#' + colorCandidateString);
493
+ }
494
+ },
495
+
496
+ colorKeyword: function () {
497
+ parserInput.save();
498
+ var autoCommentAbsorb = parserInput.autoCommentAbsorb;
499
+ parserInput.autoCommentAbsorb = false;
500
+ var k = parserInput.$re(/^[A-Za-z]+/);
501
+ parserInput.autoCommentAbsorb = autoCommentAbsorb;
502
+ if (!k) {
503
+ parserInput.forget();
504
+ return;
505
+ }
506
+ parserInput.restore();
507
+ var color = tree.Color.fromKeyword(k);
508
+ if (color) {
509
+ parserInput.$str(k);
510
+ return color;
493
511
  }
494
512
  },
495
513
 
@@ -503,7 +521,7 @@ var Parser = function Parser(context, imports, fileInfo) {
503
521
  return;
504
522
  }
505
523
 
506
- var value = parserInput.$re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/i);
524
+ var value = parserInput.$re(/^([+-]?\d*\.?\d+)(%|[a-z_]+)?/i);
507
525
  if (value) {
508
526
  return new(tree.Dimension)(value[1], value[2]);
509
527
  }
@@ -569,7 +587,7 @@ var Parser = function Parser(context, imports, fileInfo) {
569
587
  rulesetCall: function () {
570
588
  var name;
571
589
 
572
- if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) {
590
+ if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\(\s*\)\s*;/))) {
573
591
  return new tree.RulesetCall(name[1]);
574
592
  }
575
593
  },
@@ -603,7 +621,7 @@ var Parser = function Parser(context, imports, fileInfo) {
603
621
  if (!elements) {
604
622
  error("Missing target selector for :extend().");
605
623
  }
606
- extend = new(tree.Extend)(new(tree.Selector)(elements), option, index);
624
+ extend = new(tree.Extend)(new(tree.Selector)(elements), option, index, fileInfo);
607
625
  if (extendList) {
608
626
  extendList.push(extend);
609
627
  } else {
@@ -687,7 +705,8 @@ var Parser = function Parser(context, imports, fileInfo) {
687
705
  var entities = parsers.entities,
688
706
  returner = { args:null, variadic: false },
689
707
  expressions = [], argsSemiColon = [], argsComma = [],
690
- isSemiColonSeparated, expressionContainsNamed, name, nameLoop, value, arg;
708
+ isSemiColonSeparated, expressionContainsNamed, name, nameLoop,
709
+ value, arg, expand;
691
710
 
692
711
  parserInput.save();
693
712
 
@@ -749,14 +768,18 @@ var Parser = function Parser(context, imports, fileInfo) {
749
768
  }
750
769
  }
751
770
  nameLoop = (name = val.name);
752
- } else if (!isCall && parserInput.$str("...")) {
753
- returner.variadic = true;
754
- if (parserInput.$char(";") && !isSemiColonSeparated) {
755
- isSemiColonSeparated = true;
771
+ } else if (parserInput.$str("...")) {
772
+ if (!isCall) {
773
+ returner.variadic = true;
774
+ if (parserInput.$char(";") && !isSemiColonSeparated) {
775
+ isSemiColonSeparated = true;
776
+ }
777
+ (isSemiColonSeparated ? argsSemiColon : argsComma)
778
+ .push({ name: arg.name, variadic: true });
779
+ break;
780
+ } else {
781
+ expand = true;
756
782
  }
757
- (isSemiColonSeparated ? argsSemiColon : argsComma)
758
- .push({ name: arg.name, variadic: true });
759
- break;
760
783
  } else if (!isCall) {
761
784
  name = nameLoop = val.name;
762
785
  value = null;
@@ -767,7 +790,7 @@ var Parser = function Parser(context, imports, fileInfo) {
767
790
  expressions.push(value);
768
791
  }
769
792
 
770
- argsComma.push({ name:nameLoop, value:value });
793
+ argsComma.push({ name:nameLoop, value:value, expand:expand });
771
794
 
772
795
  if (parserInput.$char(',')) {
773
796
  continue;
@@ -784,7 +807,7 @@ var Parser = function Parser(context, imports, fileInfo) {
784
807
  if (expressions.length > 1) {
785
808
  value = new(tree.Value)(expressions);
786
809
  }
787
- argsSemiColon.push({ name:name, value:value });
810
+ argsSemiColon.push({ name:name, value:value, expand:expand });
788
811
 
789
812
  name = null;
790
813
  expressions = [];
@@ -1264,12 +1287,10 @@ var Parser = function Parser(context, imports, fileInfo) {
1264
1287
  } else if (e) {
1265
1288
  nodes.push(new(tree.Paren)(e));
1266
1289
  } else {
1267
- parserInput.restore("badly formed media feature definition");
1268
- return null;
1290
+ error("badly formed media feature definition");
1269
1291
  }
1270
1292
  } else {
1271
- parserInput.restore("Missing closing ')'");
1272
- return null;
1293
+ error("Missing closing ')'", "Parse");
1273
1294
  }
1274
1295
  }
1275
1296
  } while (e);
@@ -1314,8 +1335,7 @@ var Parser = function Parser(context, imports, fileInfo) {
1314
1335
  rules = this.block();
1315
1336
 
1316
1337
  if (!rules) {
1317
- parserInput.restore("media definitions require block statements after any features");
1318
- return;
1338
+ error("media definitions require block statements after any features");
1319
1339
  }
1320
1340
 
1321
1341
  parserInput.forget();
@@ -1393,33 +1413,6 @@ var Parser = function Parser(context, imports, fileInfo) {
1393
1413
  }
1394
1414
 
1395
1415
  switch(nonVendorSpecificName) {
1396
- /*
1397
- case "@font-face":
1398
- case "@viewport":
1399
- case "@top-left":
1400
- case "@top-left-corner":
1401
- case "@top-center":
1402
- case "@top-right":
1403
- case "@top-right-corner":
1404
- case "@bottom-left":
1405
- case "@bottom-left-corner":
1406
- case "@bottom-center":
1407
- case "@bottom-right":
1408
- case "@bottom-right-corner":
1409
- case "@left-top":
1410
- case "@left-middle":
1411
- case "@left-bottom":
1412
- case "@right-top":
1413
- case "@right-middle":
1414
- case "@right-bottom":
1415
- hasBlock = true;
1416
- isRooted = true;
1417
- break;
1418
- */
1419
- case "@counter-style":
1420
- hasIdentifier = true;
1421
- hasBlock = true;
1422
- break;
1423
1416
  case "@charset":
1424
1417
  hasIdentifier = true;
1425
1418
  hasBlock = false;
@@ -1429,17 +1422,17 @@ var Parser = function Parser(context, imports, fileInfo) {
1429
1422
  hasBlock = false;
1430
1423
  break;
1431
1424
  case "@keyframes":
1425
+ case "@counter-style":
1432
1426
  hasIdentifier = true;
1433
1427
  break;
1434
- case "@host":
1435
- case "@page":
1436
- hasUnknown = true;
1437
- break;
1438
1428
  case "@document":
1439
1429
  case "@supports":
1440
1430
  hasUnknown = true;
1441
1431
  isRooted = false;
1442
1432
  break;
1433
+ default:
1434
+ hasUnknown = true;
1435
+ break;
1443
1436
  }
1444
1437
 
1445
1438
  parserInput.commentStore.length = 0;
@@ -1456,6 +1449,7 @@ var Parser = function Parser(context, imports, fileInfo) {
1456
1449
  }
1457
1450
  } else if (hasUnknown) {
1458
1451
  value = (parserInput.$re(/^[^{;]+/) || '').trim();
1452
+ hasBlock = (parserInput.currentChar() == '{');
1459
1453
  if (value) {
1460
1454
  value = new(tree.Anonymous)(value);
1461
1455
  }
@@ -1469,7 +1463,6 @@ var Parser = function Parser(context, imports, fileInfo) {
1469
1463
  parserInput.forget();
1470
1464
  return new (tree.Directive)(name, value, rules, index, fileInfo,
1471
1465
  context.dumpLineNumbers ? getDebugInfo(index) : null,
1472
- false,
1473
1466
  isRooted
1474
1467
  );
1475
1468
  }
@@ -1593,11 +1586,103 @@ var Parser = function Parser(context, imports, fileInfo) {
1593
1586
  }
1594
1587
  },
1595
1588
  condition: function () {
1596
- var entities = this.entities, index = parserInput.i, negate = false,
1597
- a, b, c, op;
1589
+ var result, logical, next;
1590
+ function or() {
1591
+ return parserInput.$str("or");
1592
+ }
1593
+
1594
+ result = this.conditionAnd(this);
1595
+ if (!result) {
1596
+ return ;
1597
+ }
1598
+ logical = or();
1599
+ if (logical) {
1600
+ next = this.condition();
1601
+ if (next) {
1602
+ result = new(tree.Condition)(logical, result, next);
1603
+ } else {
1604
+ return ;
1605
+ }
1606
+ }
1607
+ return result;
1608
+ },
1609
+ conditionAnd: function () {
1610
+ var result, logical, next;
1611
+ function insideCondition(me) {
1612
+ return me.negatedCondition() || me.parenthesisCondition();
1613
+ }
1614
+ function and() {
1615
+ return parserInput.$str("and");
1616
+ }
1617
+
1618
+ result = insideCondition(this);
1619
+ if (!result) {
1620
+ return ;
1621
+ }
1622
+ logical = and();
1623
+ if (logical) {
1624
+ next = this.conditionAnd();
1625
+ if (next) {
1626
+ result = new(tree.Condition)(logical, result, next);
1627
+ } else {
1628
+ return ;
1629
+ }
1630
+ }
1631
+ return result;
1632
+ },
1633
+ negatedCondition: function () {
1634
+ if (parserInput.$str("not")) {
1635
+ var result = this.parenthesisCondition();
1636
+ if (result) {
1637
+ result.negate = !result.negate;
1638
+ }
1639
+ return result;
1640
+ }
1641
+ },
1642
+ parenthesisCondition: function () {
1643
+ function tryConditionFollowedByParenthesis(me) {
1644
+ var body;
1645
+ parserInput.save();
1646
+ body = me.condition();
1647
+ if (!body) {
1648
+ parserInput.restore();
1649
+ return ;
1650
+ }
1651
+ if (!parserInput.$char(')')) {
1652
+ parserInput.restore();
1653
+ return ;
1654
+ }
1655
+ parserInput.forget();
1656
+ return body;
1657
+ }
1658
+
1659
+ var body;
1660
+ parserInput.save();
1661
+ if (!parserInput.$str("(")) {
1662
+ parserInput.restore();
1663
+ return ;
1664
+ }
1665
+ body = tryConditionFollowedByParenthesis(this);
1666
+ if (body) {
1667
+ parserInput.forget();
1668
+ return body;
1669
+ }
1670
+
1671
+ body = this.atomicCondition();
1672
+ if (!body) {
1673
+ parserInput.restore();
1674
+ return ;
1675
+ }
1676
+ if (!parserInput.$char(')')) {
1677
+ parserInput.restore("expected ')' got '" + parserInput.currentChar() + "'");
1678
+ return ;
1679
+ }
1680
+ parserInput.forget();
1681
+ return body;
1682
+ },
1683
+ atomicCondition: function () {
1684
+ var entities = this.entities, index = parserInput.i, a, b, c, op;
1598
1685
 
1599
- if (parserInput.$str("not")) { negate = true; }
1600
- expectChar('(');
1601
1686
  a = this.addition() || entities.keyword() || entities.quoted();
1602
1687
  if (a) {
1603
1688
  if (parserInput.$char('>')) {
@@ -1626,15 +1711,14 @@ var Parser = function Parser(context, imports, fileInfo) {
1626
1711
  if (op) {
1627
1712
  b = this.addition() || entities.keyword() || entities.quoted();
1628
1713
  if (b) {
1629
- c = new(tree.Condition)(op, a, b, index, negate);
1714
+ c = new(tree.Condition)(op, a, b, index, false);
1630
1715
  } else {
1631
1716
  error('expected expression');
1632
1717
  }
1633
1718
  } else {
1634
- c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate);
1719
+ c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, false);
1635
1720
  }
1636
- expectChar(')');
1637
- return parserInput.$str("and") ? new(tree.Condition)('and', c, this.condition()) : c;
1721
+ return c;
1638
1722
  }
1639
1723
  },
1640
1724
 
@@ -1651,7 +1735,7 @@ var Parser = function Parser(context, imports, fileInfo) {
1651
1735
 
1652
1736
  var o = this.sub() || entities.dimension() ||
1653
1737
  entities.color() || entities.variable() ||
1654
- entities.call();
1738
+ entities.call() || entities.colorKeyword();
1655
1739
 
1656
1740
  if (negate) {
1657
1741
  o.parensInOp = true;
@@ -39,6 +39,7 @@ module.exports = function(root, options) {
39
39
  var preEvalVisitors = [],
40
40
  visitors = [
41
41
  new visitor.JoinSelectorVisitor(),
42
+ new visitor.MarkVisibleSelectorsVisitor(true),
42
43
  new visitor.ExtendVisitor(),
43
44
  new visitor.ToCSSVisitor({compress: Boolean(options.compress)})
44
45
  ], i;