less 2.3.3 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
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'));