less 2.3.3 → 2.4.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.
Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +5 -0
  3. data/less.gemspec +1 -1
  4. data/lib/less/js/.gitattributes +9 -0
  5. data/lib/less/js/.gitignore +1 -0
  6. data/lib/less/js/.npmignore +1 -1
  7. data/lib/less/js/CHANGELOG.md +68 -0
  8. data/lib/less/js/CONTRIBUTING.md +33 -34
  9. data/lib/less/js/Makefile +24 -9
  10. data/lib/less/js/README.md +2 -2
  11. data/lib/less/js/bin/lessc +102 -25
  12. data/lib/less/js/build/amd.js +1 -1
  13. data/lib/less/js/build/header.js +9 -7
  14. data/lib/less/js/dist/less-1.3.3.js +2 -2
  15. data/lib/less/js/dist/less-1.3.3.min.js +2 -2
  16. data/lib/less/js/dist/less-1.4.0-beta.js +5830 -0
  17. data/lib/less/js/dist/less-1.4.0-beta.min.js +11 -0
  18. data/lib/less/js/dist/less-1.4.0.js +5830 -0
  19. data/lib/less/js/dist/less-1.4.0.min.js +11 -0
  20. data/lib/less/js/dist/less-1.4.1.js +5837 -0
  21. data/lib/less/js/dist/less-1.4.1.min.js +11 -0
  22. data/lib/less/js/dist/less-1.4.2.js +5837 -0
  23. data/lib/less/js/dist/less-1.4.2.min.js +11 -0
  24. data/lib/less/js/dist/less-rhino-1.4.0.js +4273 -0
  25. data/lib/less/js/lib/less/browser.js +131 -101
  26. data/lib/less/js/lib/less/env.js +105 -0
  27. data/lib/less/js/lib/less/extend-visitor.js +391 -0
  28. data/lib/less/js/lib/less/functions.js +174 -19
  29. data/lib/less/js/lib/less/import-visitor.js +107 -0
  30. data/lib/less/js/lib/less/index.js +70 -63
  31. data/lib/less/js/lib/less/join-selector-visitor.js +37 -0
  32. data/lib/less/js/lib/less/lessc_helper.js +13 -4
  33. data/lib/less/js/lib/less/parser.js +353 -264
  34. data/lib/less/js/lib/less/rhino.js +5 -2
  35. data/lib/less/js/lib/less/tree.js +1 -1
  36. data/lib/less/js/lib/less/tree/alpha.js +7 -3
  37. data/lib/less/js/lib/less/tree/anonymous.js +1 -0
  38. data/lib/less/js/lib/less/tree/assignment.js +4 -0
  39. data/lib/less/js/lib/less/tree/call.js +14 -8
  40. data/lib/less/js/lib/less/tree/color.js +50 -5
  41. data/lib/less/js/lib/less/tree/comment.js +1 -0
  42. data/lib/less/js/lib/less/tree/condition.js +35 -28
  43. data/lib/less/js/lib/less/tree/dimension.js +270 -16
  44. data/lib/less/js/lib/less/tree/directive.js +7 -2
  45. data/lib/less/js/lib/less/tree/element.js +57 -21
  46. data/lib/less/js/lib/less/tree/expression.js +29 -4
  47. data/lib/less/js/lib/less/tree/extend.js +43 -0
  48. data/lib/less/js/lib/less/tree/import.js +49 -28
  49. data/lib/less/js/lib/less/tree/javascript.js +1 -0
  50. data/lib/less/js/lib/less/tree/keyword.js +3 -2
  51. data/lib/less/js/lib/less/tree/media.js +20 -4
  52. data/lib/less/js/lib/less/tree/mixin.js +38 -18
  53. data/lib/less/js/lib/less/tree/negative.js +22 -0
  54. data/lib/less/js/lib/less/tree/operation.js +32 -17
  55. data/lib/less/js/lib/less/tree/paren.js +5 -1
  56. data/lib/less/js/lib/less/tree/quoted.js +5 -3
  57. data/lib/less/js/lib/less/tree/rule.js +44 -31
  58. data/lib/less/js/lib/less/tree/ruleset.js +50 -23
  59. data/lib/less/js/lib/less/tree/selector.js +49 -39
  60. data/lib/less/js/lib/less/tree/unicode-descriptor.js +1 -0
  61. data/lib/less/js/lib/less/tree/url.js +9 -5
  62. data/lib/less/js/lib/less/tree/value.js +4 -1
  63. data/lib/less/js/lib/less/tree/variable.js +4 -3
  64. data/lib/less/js/lib/less/visitor.js +54 -0
  65. data/lib/less/js/package.json +69 -19
  66. data/lib/less/js/test/browser-test-prepare.js +23 -6
  67. data/lib/less/js/test/browser/common.js +55 -3
  68. data/lib/less/js/test/browser/css/urls.css +13 -0
  69. data/lib/less/js/test/browser/less/relative-urls/urls.less +1 -1
  70. data/lib/less/js/test/browser/less/urls.less +16 -0
  71. data/lib/less/js/test/browser/phantom-runner.js +7 -5
  72. data/lib/less/js/test/browser/runner-browser.js +5 -1
  73. data/lib/less/js/test/browser/runner-errors.js +5 -0
  74. data/lib/less/js/test/browser/runner-legacy.js +6 -0
  75. data/lib/less/js/test/browser/runner-production.js +7 -0
  76. data/lib/less/js/test/browser/template.htm +6 -6
  77. data/lib/less/js/test/css/comments.css +1 -0
  78. data/lib/less/js/test/css/compression/compression.css +2 -0
  79. data/lib/less/js/test/css/css-3.css +4 -0
  80. data/lib/less/js/test/css/css.css +9 -3
  81. data/lib/less/js/test/css/extend-chaining.css +72 -0
  82. data/lib/less/js/test/css/extend-clearfix.css +19 -0
  83. data/lib/less/js/test/css/extend-exact.css +37 -0
  84. data/lib/less/js/test/css/extend-media.css +24 -0
  85. data/lib/less/js/test/css/extend-nest.css +57 -0
  86. data/lib/less/js/test/css/extend-selector.css +72 -0
  87. data/lib/less/js/test/css/extend.css +76 -0
  88. data/lib/less/js/test/css/functions.css +28 -0
  89. data/lib/less/js/test/css/import-interpolation.css +6 -0
  90. data/lib/less/js/test/css/import.css +18 -1
  91. data/lib/less/js/test/css/legacy/legacy.css +7 -0
  92. data/lib/less/js/test/css/media.css +9 -1
  93. data/lib/less/js/test/css/mixins-args.css +18 -0
  94. data/lib/less/js/test/css/mixins-guards.css +5 -0
  95. data/lib/less/js/test/css/parens.css +18 -5
  96. data/lib/less/js/test/css/selectors.css +14 -6
  97. data/lib/less/js/test/css/urls.css +17 -0
  98. data/lib/less/js/test/css/variables.css +22 -3
  99. data/lib/less/js/test/data/data-uri-fail.png +0 -0
  100. data/lib/less/js/test/data/image.jpg +0 -0
  101. data/lib/less/js/test/data/page.html +1 -0
  102. data/lib/less/js/test/less-test.js +41 -9
  103. data/lib/less/js/test/less/colors.less +4 -4
  104. data/lib/less/js/test/less/comments.less +2 -2
  105. data/lib/less/js/test/less/compression/compression.less +16 -0
  106. data/lib/less/js/test/less/css-3.less +5 -1
  107. data/lib/less/js/test/less/css.less +9 -3
  108. data/lib/less/js/test/less/errors/add-mixed-units.less +3 -0
  109. data/lib/less/js/test/less/errors/add-mixed-units.txt +2 -0
  110. data/lib/less/js/test/less/errors/add-mixed-units2.less +3 -0
  111. data/lib/less/js/test/less/errors/add-mixed-units2.txt +2 -0
  112. data/lib/less/js/test/less/errors/bad-variable-declaration1.txt +1 -1
  113. data/lib/less/js/test/less/errors/color-operation-error.less +3 -0
  114. data/lib/less/js/test/less/errors/color-operation-error.txt +2 -0
  115. data/lib/less/js/test/less/errors/comment-in-selector.txt +1 -1
  116. data/lib/less/js/test/less/errors/divide-mixed-units.less +3 -0
  117. data/lib/less/js/test/less/errors/divide-mixed-units.txt +4 -0
  118. data/lib/less/js/test/less/errors/extend-no-selector.less +3 -0
  119. data/lib/less/js/test/less/errors/extend-no-selector.txt +3 -0
  120. data/lib/less/js/test/less/errors/extend-not-at-end.less +3 -0
  121. data/lib/less/js/test/less/errors/extend-not-at-end.txt +3 -0
  122. data/lib/less/js/test/less/errors/import-missing.less +5 -0
  123. data/lib/less/js/test/less/errors/import-missing.txt +3 -3
  124. data/lib/less/js/test/less/errors/import-no-semi.txt +1 -1
  125. data/lib/less/js/test/less/errors/import-subfolder1.txt +1 -1
  126. data/lib/less/js/test/less/errors/import-subfolder2.txt +1 -1
  127. data/lib/less/js/test/less/errors/javascript-error.txt +1 -1
  128. data/lib/less/js/test/less/errors/mixed-mixin-definition-args-1.txt +1 -1
  129. data/lib/less/js/test/less/errors/mixed-mixin-definition-args-2.txt +1 -1
  130. data/lib/less/js/test/less/errors/mixin-not-defined.txt +1 -1
  131. data/lib/less/js/test/less/errors/mixin-not-matched.txt +1 -1
  132. data/lib/less/js/test/less/errors/mixin-not-matched2.txt +1 -1
  133. data/lib/less/js/test/less/errors/multiply-mixed-units.less +7 -0
  134. data/lib/less/js/test/less/errors/multiply-mixed-units.txt +4 -0
  135. data/lib/less/js/test/less/errors/parens-error-1.less +3 -0
  136. data/lib/less/js/test/less/errors/parens-error-1.txt +4 -0
  137. data/lib/less/js/test/less/errors/parens-error-2.less +3 -0
  138. data/lib/less/js/test/less/errors/parens-error-2.txt +4 -0
  139. data/lib/less/js/test/less/errors/parens-error-3.less +3 -0
  140. data/lib/less/js/test/less/errors/parens-error-3.txt +4 -0
  141. data/lib/less/js/test/less/errors/parse-error-curly-bracket.txt +1 -1
  142. data/lib/less/js/test/less/errors/parse-error-missing-bracket.txt +2 -1
  143. data/lib/less/js/test/less/errors/parse-error-with-import.txt +1 -1
  144. data/lib/less/js/test/less/errors/property-ie5-hack.txt +1 -1
  145. data/lib/less/js/test/less/errors/property-in-root.less +4 -0
  146. data/lib/less/js/test/less/errors/property-in-root.txt +4 -0
  147. data/lib/less/js/test/less/errors/property-in-root2.less +1 -0
  148. data/lib/less/js/test/less/errors/property-in-root2.txt +4 -0
  149. data/lib/less/js/test/less/errors/property-in-root3.less +4 -0
  150. data/lib/less/js/test/less/errors/property-in-root3.txt +3 -0
  151. data/lib/less/js/test/less/errors/recursive-variable.txt +1 -1
  152. data/lib/less/js/test/less/extend-chaining.less +79 -0
  153. data/lib/less/js/test/less/extend-clearfix.less +19 -0
  154. data/lib/less/js/test/less/extend-exact.less +46 -0
  155. data/lib/less/js/test/less/extend-media.less +24 -0
  156. data/lib/less/js/test/less/extend-nest.less +65 -0
  157. data/lib/less/js/test/less/extend-selector.less +84 -0
  158. data/lib/less/js/test/less/extend.less +81 -0
  159. data/lib/less/js/test/less/functions.less +37 -6
  160. data/lib/less/js/test/less/import-interpolation.less +8 -0
  161. data/lib/less/js/test/less/import-once.less +4 -4
  162. data/lib/less/js/test/less/import.less +11 -2
  163. data/lib/less/js/test/less/import/deeper/import-once-test-a.less +1 -1
  164. data/lib/less/js/test/less/import/import-interpolation.less +1 -0
  165. data/lib/less/js/test/less/import/import-interpolation2.less +5 -0
  166. data/lib/less/js/test/less/javascript.less +1 -1
  167. data/lib/less/js/test/less/legacy/legacy.less +7 -0
  168. data/lib/less/js/test/less/media.less +14 -3
  169. data/lib/less/js/test/less/mixins-args.less +43 -5
  170. data/lib/less/js/test/less/mixins-guards.less +13 -0
  171. data/lib/less/js/test/less/mixins-named-args.less +5 -5
  172. data/lib/less/js/test/less/mixins-nested.less +2 -2
  173. data/lib/less/js/test/less/mixins-pattern.less +1 -1
  174. data/lib/less/js/test/less/mixins.less +1 -1
  175. data/lib/less/js/test/less/operations.less +27 -27
  176. data/lib/less/js/test/less/parens.less +20 -5
  177. data/lib/less/js/test/less/selectors.less +14 -7
  178. data/lib/less/js/test/less/urls.less +24 -0
  179. data/lib/less/js/test/less/variables.less +42 -12
  180. data/lib/less/loader.rb +33 -0
  181. data/lib/less/version.rb +1 -1
  182. data/spec/less/parser_spec.rb +5 -5
  183. metadata +76 -6
  184. data/lib/less/js/build/ecma-5.js +0 -120
  185. data/lib/less/js/lib/less/tree/ratio.js +0 -13
@@ -0,0 +1,22 @@
1
+ (function (tree) {
2
+
3
+ tree.Negative = function (node) {
4
+ this.value = node;
5
+ };
6
+ tree.Negative.prototype = {
7
+ type: "Negative",
8
+ accept: function (visitor) {
9
+ this.value = visitor.visit(this.value);
10
+ },
11
+ toCSS: function (env) {
12
+ return '-' + this.value.toCSS(env);
13
+ },
14
+ eval: function (env) {
15
+ if (env.isMathOn()) {
16
+ return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env);
17
+ }
18
+ return new(tree.Negative)(this.value.eval(env));
19
+ }
20
+ };
21
+
22
+ })(require('../tree'));
@@ -1,31 +1,46 @@
1
1
  (function (tree) {
2
2
 
3
- tree.Operation = function (op, operands) {
3
+ tree.Operation = function (op, operands, isSpaced) {
4
4
  this.op = op.trim();
5
5
  this.operands = operands;
6
+ this.isSpaced = isSpaced;
6
7
  };
7
- tree.Operation.prototype.eval = function (env) {
8
- var a = this.operands[0].eval(env),
9
- b = this.operands[1].eval(env),
10
- temp;
8
+ tree.Operation.prototype = {
9
+ type: "Operation",
10
+ accept: function (visitor) {
11
+ this.operands = visitor.visit(this.operands);
12
+ },
13
+ eval: function (env) {
14
+ var a = this.operands[0].eval(env),
15
+ b = this.operands[1].eval(env),
16
+ temp;
11
17
 
12
- if (a instanceof tree.Dimension && b instanceof tree.Color) {
13
- if (this.op === '*' || this.op === '+') {
14
- temp = b, b = a, a = temp;
18
+ if (env.isMathOn()) {
19
+ if (a instanceof tree.Dimension && b instanceof tree.Color) {
20
+ if (this.op === '*' || this.op === '+') {
21
+ temp = b, b = a, a = temp;
22
+ } else {
23
+ throw { type: "Operation",
24
+ message: "Can't substract or divide a color from a number" };
25
+ }
26
+ }
27
+ if (!a.operate) {
28
+ throw { type: "Operation",
29
+ message: "Operation on an invalid type" };
30
+ }
31
+
32
+ return a.operate(env, this.op, b);
15
33
  } else {
16
- throw { name: "OperationError",
17
- message: "Can't substract or divide a color from a number" };
34
+ return new(tree.Operation)(this.op, [a, b], this.isSpaced);
18
35
  }
36
+ },
37
+ toCSS: function (env) {
38
+ var separator = this.isSpaced ? " " : "";
39
+ return this.operands[0].toCSS() + separator + this.op + separator + this.operands[1].toCSS();
19
40
  }
20
- if (!a.operate) {
21
- throw { name: "OperationError",
22
- message: "Operation on an invalid type" };
23
- }
24
-
25
- return a.operate(this.op, b);
26
41
  };
27
42
 
28
- tree.operate = function (op, a, b) {
43
+ tree.operate = function (env, op, a, b) {
29
44
  switch (op) {
30
45
  case '+': return a + b;
31
46
  case '-': return a - b;
@@ -5,8 +5,12 @@ tree.Paren = function (node) {
5
5
  this.value = node;
6
6
  };
7
7
  tree.Paren.prototype = {
8
+ type: "Paren",
9
+ accept: function (visitor) {
10
+ this.value = visitor.visit(this.value);
11
+ },
8
12
  toCSS: function (env) {
9
- return '(' + this.value.toCSS(env) + ')';
13
+ return '(' + this.value.toCSS(env).trim() + ')';
10
14
  },
11
15
  eval: function (env) {
12
16
  return new(tree.Paren)(this.value.eval(env));
@@ -1,12 +1,14 @@
1
1
  (function (tree) {
2
2
 
3
- tree.Quoted = function (str, content, escaped, i) {
3
+ tree.Quoted = function (str, content, escaped, index, currentFileInfo) {
4
4
  this.escaped = escaped;
5
5
  this.value = content || '';
6
6
  this.quote = str.charAt(0);
7
- this.index = i;
7
+ this.index = index;
8
+ this.currentFileInfo = currentFileInfo;
8
9
  };
9
10
  tree.Quoted.prototype = {
11
+ type: "Quoted",
10
12
  toCSS: function () {
11
13
  if (this.escaped) {
12
14
  return this.value;
@@ -19,7 +21,7 @@ tree.Quoted.prototype = {
19
21
  var value = this.value.replace(/`([^`]+)`/g, function (_, exp) {
20
22
  return new(tree.JavaScript)(exp, that.index, true).eval(env).value;
21
23
  }).replace(/@\{([\w-]+)\}/g, function (_, name) {
22
- var v = new(tree.Variable)('@' + name, that.index).eval(env);
24
+ var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true);
23
25
  return (v instanceof tree.Quoted) ? v.value : v.toCSS();
24
26
  });
25
27
  return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index);
@@ -1,49 +1,62 @@
1
1
  (function (tree) {
2
2
 
3
- tree.Rule = function (name, value, important, index, inline) {
3
+ tree.Rule = function (name, value, important, index, currentFileInfo, inline) {
4
4
  this.name = name;
5
5
  this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]);
6
6
  this.important = important ? ' ' + important.trim() : '';
7
7
  this.index = index;
8
+ this.currentFileInfo = currentFileInfo;
8
9
  this.inline = inline || false;
9
10
 
10
11
  if (name.charAt(0) === '@') {
11
12
  this.variable = true;
12
13
  } else { this.variable = false }
13
14
  };
14
- tree.Rule.prototype.toCSS = function (env) {
15
- if (this.variable) { return "" }
16
- else {
17
- return this.name + (env.compress ? ':' : ': ') +
18
- this.value.toCSS(env) +
19
- this.important + (this.inline ? "" : ";");
20
- }
21
- };
22
-
23
- tree.Rule.prototype.eval = function (context) {
24
- return new(tree.Rule)(this.name,
25
- this.value.eval(context),
26
- this.important,
27
- this.index, this.inline);
28
- };
29
-
30
- tree.Rule.prototype.makeImportant = function () {
31
- return new(tree.Rule)(this.name,
32
- this.value,
33
- "!important",
34
- this.index, this.inline);
35
- };
36
15
 
37
- tree.Shorthand = function (a, b) {
38
- this.a = a;
39
- this.b = b;
40
- };
41
-
42
- tree.Shorthand.prototype = {
16
+ tree.Rule.prototype = {
17
+ type: "Rule",
18
+ accept: function (visitor) {
19
+ this.value = visitor.visit(this.value);
20
+ },
43
21
  toCSS: function (env) {
44
- return this.a.toCSS(env) + "/" + this.b.toCSS(env);
22
+ if (this.variable) { return "" }
23
+ else {
24
+ try {
25
+ return this.name + (env.compress ? ':' : ': ') +
26
+ this.value.toCSS(env) +
27
+ this.important + (this.inline ? "" : ";");
28
+ }
29
+ catch(e) {
30
+ e.index = this.index;
31
+ e.filename = this.currentFileInfo.filename;
32
+ throw e;
33
+ }
34
+ }
45
35
  },
46
- eval: function () { return this }
36
+ eval: function (env) {
37
+ var strictMathBypass = false;
38
+ if (this.name === "font" && !env.strictMath) {
39
+ strictMathBypass = true;
40
+ env.strictMath = true;
41
+ }
42
+ try {
43
+ return new(tree.Rule)(this.name,
44
+ this.value.eval(env),
45
+ this.important,
46
+ this.index, this.currentFileInfo, this.inline);
47
+ }
48
+ finally {
49
+ if (strictMathBypass) {
50
+ env.strictMath = false;
51
+ }
52
+ }
53
+ },
54
+ makeImportant: function () {
55
+ return new(tree.Rule)(this.name,
56
+ this.value,
57
+ "!important",
58
+ this.index, this.currentFileInfo, this.inline);
59
+ }
47
60
  };
48
61
 
49
62
  })(require('../tree'));
@@ -7,6 +7,11 @@ tree.Ruleset = function (selectors, rules, strictImports) {
7
7
  this.strictImports = strictImports;
8
8
  };
9
9
  tree.Ruleset.prototype = {
10
+ type: "Ruleset",
11
+ accept: function (visitor) {
12
+ this.selectors = visitor.visit(this.selectors);
13
+ this.rules = visitor.visit(this.rules);
14
+ },
10
15
  eval: function (env) {
11
16
  var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) });
12
17
  var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports);
@@ -14,6 +19,7 @@ tree.Ruleset.prototype = {
14
19
 
15
20
  ruleset.originalRuleset = this;
16
21
  ruleset.root = this.root;
22
+ ruleset.firstRoot = this.firstRoot;
17
23
  ruleset.allowImports = this.allowImports;
18
24
 
19
25
  if(this.debugInfo) {
@@ -23,6 +29,12 @@ tree.Ruleset.prototype = {
23
29
  // push the current ruleset to the frames stack
24
30
  env.frames.unshift(ruleset);
25
31
 
32
+ // currrent selectors
33
+ if (!env.selectors) {
34
+ env.selectors = [];
35
+ }
36
+ env.selectors.unshift(this.selectors);
37
+
26
38
  // Evaluate imports
27
39
  if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) {
28
40
  ruleset.evalImports(env);
@@ -41,13 +53,21 @@ tree.Ruleset.prototype = {
41
53
  // Evaluate mixin calls.
42
54
  for (var i = 0; i < ruleset.rules.length; i++) {
43
55
  if (ruleset.rules[i] instanceof tree.mixin.Call) {
44
- rules = ruleset.rules[i].eval(env);
56
+ rules = ruleset.rules[i].eval(env).filter(function(r) {
57
+ if ((r instanceof tree.Rule) && r.variable) {
58
+ // do not pollute the scope if the variable is
59
+ // already there. consider returning false here
60
+ // but we need a way to "return" variable from mixins
61
+ return !(ruleset.variable(r.name));
62
+ }
63
+ return true;
64
+ });
45
65
  ruleset.rules.splice.apply(ruleset.rules, [i, 1].concat(rules));
46
66
  i += rules.length-1;
47
67
  ruleset.resetCache();
48
68
  }
49
69
  }
50
-
70
+
51
71
  // Evaluate everything else
52
72
  for (var i = 0, rule; i < ruleset.rules.length; i++) {
53
73
  rule = ruleset.rules[i];
@@ -59,6 +79,7 @@ tree.Ruleset.prototype = {
59
79
 
60
80
  // Pop the stack
61
81
  env.frames.shift();
82
+ env.selectors.shift();
62
83
 
63
84
  if (env.mediaBlocks) {
64
85
  for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) {
@@ -115,12 +136,9 @@ tree.Ruleset.prototype = {
115
136
  return this.variables()[name];
116
137
  },
117
138
  rulesets: function () {
118
- if (this._rulesets) { return this._rulesets }
119
- else {
120
- return this._rulesets = this.rules.filter(function (r) {
121
- return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition);
122
- });
123
- }
139
+ return this.rules.filter(function (r) {
140
+ return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition);
141
+ });
124
142
  },
125
143
  find: function (selector, self) {
126
144
  self = self || this;
@@ -151,28 +169,23 @@ tree.Ruleset.prototype = {
151
169
  //
152
170
  // `context` holds an array of arrays.
153
171
  //
154
- toCSS: function (context, env) {
172
+ toCSS: function (env) {
155
173
  var css = [], // The CSS output
156
174
  rules = [], // node.Rule instances
157
175
  _rules = [], //
158
176
  rulesets = [], // node.Ruleset instances
159
- paths = [], // Current selectors
160
177
  selector, // The fully rendered selector
161
178
  debugInfo, // Line number debugging
162
179
  rule;
163
180
 
164
- if (! this.root) {
165
- this.joinSelectors(paths, context, this.selectors);
166
- }
167
-
168
181
  // Compile rules and rulesets
169
182
  for (var i = 0; i < this.rules.length; i++) {
170
183
  rule = this.rules[i];
171
184
 
172
185
  if (rule.rules || (rule instanceof tree.Media)) {
173
- rulesets.push(rule.toCSS(paths, env));
186
+ rulesets.push(rule.toCSS(env));
174
187
  } else if (rule instanceof tree.Directive) {
175
- var cssValue = rule.toCSS(paths, env);
188
+ var cssValue = rule.toCSS(env);
176
189
  // Output only the first @charset definition as such - convert the others
177
190
  // to comments in case debug is enabled
178
191
  if (rule.name === "@charset") {
@@ -199,6 +212,10 @@ tree.Ruleset.prototype = {
199
212
  }
200
213
  } else {
201
214
  if (rule.toCSS && !rule.variable) {
215
+ if (this.firstRoot && rule instanceof tree.Rule) {
216
+ throw { message: "properties must be inside selector blocks, they cannot be in the root.",
217
+ index: rule.index, filename: rule.currentFileInfo ? rule.currentFileInfo.filename : null};
218
+ }
202
219
  rules.push(rule.toCSS(env));
203
220
  } else if (rule.value && !rule.variable) {
204
221
  rules.push(rule.value.toString());
@@ -206,6 +223,14 @@ tree.Ruleset.prototype = {
206
223
  }
207
224
  }
208
225
 
226
+ // Remove last semicolon
227
+ if (env.compress && rules.length) {
228
+ rule = rules[rules.length - 1];
229
+ if (rule.charAt(rule.length - 1) === ';') {
230
+ rules[rules.length - 1] = rule.substring(0, rule.length - 1);
231
+ }
232
+ }
233
+
209
234
  rulesets = rulesets.join('');
210
235
 
211
236
  // If this is the root node, we don't render
@@ -216,7 +241,7 @@ tree.Ruleset.prototype = {
216
241
  } else {
217
242
  if (rules.length > 0) {
218
243
  debugInfo = tree.debugInfo(env, this);
219
- selector = paths.map(function (p) {
244
+ selector = this.paths.map(function (p) {
220
245
  return p.map(function (s) {
221
246
  return s.toCSS(env);
222
247
  }).join('').trim();
@@ -224,7 +249,7 @@ tree.Ruleset.prototype = {
224
249
 
225
250
  // Remove duplicates
226
251
  for (var i = rules.length - 1; i >= 0; i--) {
227
- if (_rules.indexOf(rules[i]) === -1) {
252
+ if (rules[i].slice(0, 2) === "/*" || _rules.indexOf(rules[i]) === -1) {
228
253
  _rules.unshift(rules[i]);
229
254
  }
230
255
  }
@@ -339,11 +364,11 @@ tree.Ruleset.prototype = {
339
364
  if (sel.length > 0) {
340
365
  newSelectorPath = sel.slice(0);
341
366
  lastSelector = newSelectorPath.pop();
342
- newJoinedSelector = new(tree.Selector)(lastSelector.elements.slice(0));
367
+ newJoinedSelector = new(tree.Selector)(lastSelector.elements.slice(0), selector.extendList);
343
368
  newJoinedSelectorEmpty = false;
344
369
  }
345
370
  else {
346
- newJoinedSelector = new(tree.Selector)([]);
371
+ newJoinedSelector = new(tree.Selector)([], selector.extendList);
347
372
  }
348
373
 
349
374
  //put together the parent selectors after the join
@@ -386,12 +411,14 @@ tree.Ruleset.prototype = {
386
411
  }
387
412
 
388
413
  for(i = 0; i < newSelectors.length; i++) {
389
- paths.push(newSelectors[i]);
414
+ if (newSelectors[i].length > 0) {
415
+ paths.push(newSelectors[i]);
416
+ }
390
417
  }
391
418
  },
392
419
 
393
420
  mergeElementsOnToSelectors: function(elements, selectors) {
394
- var i, sel;
421
+ var i, sel, extendList;
395
422
 
396
423
  if (selectors.length == 0) {
397
424
  selectors.push([ new(tree.Selector)(elements) ]);
@@ -403,7 +430,7 @@ tree.Ruleset.prototype = {
403
430
 
404
431
  // if the previous thing in sel is a parent this needs to join on to it
405
432
  if (sel.length > 0) {
406
- sel[sel.length - 1] = new(tree.Selector)(sel[sel.length - 1].elements.concat(elements));
433
+ sel[sel.length - 1] = new(tree.Selector)(sel[sel.length - 1].elements.concat(elements), sel[sel.length - 1].extendList);
407
434
  }
408
435
  else {
409
436
  sel.push(new(tree.Selector)(elements));
@@ -1,52 +1,62 @@
1
1
  (function (tree) {
2
2
 
3
- tree.Selector = function (elements) {
3
+ tree.Selector = function (elements, extendList) {
4
4
  this.elements = elements;
5
+ this.extendList = extendList || [];
5
6
  };
6
- tree.Selector.prototype.match = function (other) {
7
- var elements = this.elements,
8
- len = elements.length,
9
- oelements, olen, max, i;
7
+ tree.Selector.prototype = {
8
+ type: "Selector",
9
+ accept: function (visitor) {
10
+ this.elements = visitor.visit(this.elements);
11
+ this.extendList = visitor.visit(this.extendList)
12
+ },
13
+ match: function (other) {
14
+ var elements = this.elements,
15
+ len = elements.length,
16
+ oelements, olen, max, i;
10
17
 
11
- oelements = other.elements.slice(
12
- (other.elements.length && other.elements[0].value === "&") ? 1 : 0);
13
- olen = oelements.length;
14
- max = Math.min(len, olen)
18
+ oelements = other.elements.slice(
19
+ (other.elements.length && other.elements[0].value === "&") ? 1 : 0);
20
+ olen = oelements.length;
21
+ max = Math.min(len, olen);
15
22
 
16
- if (olen === 0 || len < olen) {
17
- return false;
18
- } else {
19
- for (i = 0; i < max; i++) {
20
- if (elements[i].value !== oelements[i].value) {
21
- return false;
23
+ if (olen === 0 || len < olen) {
24
+ return false;
25
+ } else {
26
+ for (i = 0; i < max; i++) {
27
+ if (elements[i].value !== oelements[i].value) {
28
+ return false;
29
+ }
22
30
  }
23
31
  }
24
- }
25
- return true;
26
- };
27
- tree.Selector.prototype.eval = function (env) {
28
- return new(tree.Selector)(this.elements.map(function (e) {
29
- return e.eval(env);
30
- }));
31
- };
32
- tree.Selector.prototype.toCSS = function (env) {
33
- if (this._css) { return this._css }
34
-
35
- if (this.elements[0].combinator.value === "") {
36
- this._css = ' ';
37
- } else {
38
- this._css = '';
39
- }
40
-
41
- this._css += this.elements.map(function (e) {
42
- if (typeof(e) === 'string') {
43
- return ' ' + e.trim();
32
+ return true;
33
+ },
34
+ eval: function (env) {
35
+ return new(tree.Selector)(this.elements.map(function (e) {
36
+ return e.eval(env);
37
+ }), this.extendList.map(function(extend) {
38
+ return extend.eval(env);
39
+ }));
40
+ },
41
+ toCSS: function (env) {
42
+ if (this._css) { return this._css }
43
+
44
+ if (this.elements[0].combinator.value === "") {
45
+ this._css = ' ';
44
46
  } else {
45
- return e.toCSS(env);
47
+ this._css = '';
46
48
  }
47
- }).join('');
48
-
49
- return this._css;
49
+
50
+ this._css += this.elements.map(function (e) {
51
+ if (typeof(e) === 'string') {
52
+ return ' ' + e.trim();
53
+ } else {
54
+ return e.toCSS(env);
55
+ }
56
+ }).join('');
57
+
58
+ return this._css;
59
+ }
50
60
  };
51
61
 
52
62
  })(require('../tree'));