less 2.5.1 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +5 -1
  3. data/README.md +12 -0
  4. data/lib/less/js/.travis.yml +8 -0
  5. data/lib/less/js/CHANGELOG.md +22 -0
  6. data/lib/less/js/CONTRIBUTING.md +1 -1
  7. data/lib/less/js/Gruntfile.js +8 -0
  8. data/lib/less/js/README.md +5 -304
  9. data/lib/less/js/bin/lessc +16 -13
  10. data/lib/less/js/bower.json +2 -2
  11. data/lib/less/js/build/README.md +4 -303
  12. data/lib/less/js/build/build.yml +2 -0
  13. data/lib/less/js/dist/less-1.6.3.js +7627 -0
  14. data/lib/less/js/dist/less-1.6.3.min.js +16 -0
  15. data/lib/less/js/dist/less-1.7.0.js +7921 -0
  16. data/lib/less/js/dist/less-1.7.0.min.js +16 -0
  17. data/lib/less/js/dist/less-rhino-1.6.3.js +9020 -0
  18. data/lib/less/js/dist/less-rhino-1.7.0.js +9301 -0
  19. data/lib/less/js/dist/lessc-rhino-1.6.3.js +449 -0
  20. data/lib/less/js/dist/lessc-rhino-1.7.0.js +449 -0
  21. data/lib/less/js/lib/less/browser.js +22 -9
  22. data/lib/less/js/lib/less/functions.js +47 -16
  23. data/lib/less/js/lib/less/import-visitor.js +29 -5
  24. data/lib/less/js/lib/less/index.js +10 -3
  25. data/lib/less/js/lib/less/parser.js +139 -52
  26. data/lib/less/js/lib/less/to-css-visitor.js +26 -4
  27. data/lib/less/js/lib/less/tree/color.js +11 -1
  28. data/lib/less/js/lib/less/tree/detached-ruleset.js +20 -0
  29. data/lib/less/js/lib/less/tree/dimension.js +17 -6
  30. data/lib/less/js/lib/less/tree/directive.js +33 -29
  31. data/lib/less/js/lib/less/tree/import.js +8 -1
  32. data/lib/less/js/lib/less/tree/keyword.js +1 -0
  33. data/lib/less/js/lib/less/tree/media.js +3 -0
  34. data/lib/less/js/lib/less/tree/mixin.js +15 -11
  35. data/lib/less/js/lib/less/tree/rule.js +14 -4
  36. data/lib/less/js/lib/less/tree/ruleset-call.js +16 -0
  37. data/lib/less/js/lib/less/tree/ruleset.js +48 -19
  38. data/lib/less/js/package.json +13 -13
  39. data/lib/less/js/test/browser/css/postProcessor/postProcessor.css +4 -0
  40. data/lib/less/js/test/browser/less/postProcessor/postProcessor.less +4 -0
  41. data/lib/less/js/test/browser/runner-postProcessor-options.js +4 -0
  42. data/lib/less/js/test/browser/runner-postProcessor.js +3 -0
  43. data/lib/less/js/test/css/debug/linenumbers-all.css +2 -2
  44. data/lib/less/js/test/css/debug/linenumbers-comments.css +1 -1
  45. data/lib/less/js/test/css/debug/linenumbers-mediaquery.css +1 -1
  46. data/lib/less/js/test/css/detached-rulesets.css +71 -0
  47. data/lib/less/js/test/css/functions.css +21 -4
  48. data/lib/less/js/test/css/import-reference.css +23 -4
  49. data/lib/less/js/test/css/merge.css +8 -0
  50. data/lib/less/js/test/css/mixins-pattern.css +4 -0
  51. data/lib/less/js/test/css/scope.css +3 -0
  52. data/lib/less/js/test/css/variables-in-at-rules.css +18 -0
  53. data/lib/less/js/test/less/css-guards.less +3 -0
  54. data/lib/less/js/test/less/detached-rulesets.less +103 -0
  55. data/lib/less/js/test/less/errors/at-rules-undefined-var.less +4 -0
  56. data/lib/less/js/test/less/errors/at-rules-undefined-var.txt +4 -0
  57. data/lib/less/js/test/less/errors/detached-ruleset-1.less +6 -0
  58. data/lib/less/js/test/less/errors/detached-ruleset-1.txt +4 -0
  59. data/lib/less/js/test/less/errors/detached-ruleset-2.less +6 -0
  60. data/lib/less/js/test/less/errors/detached-ruleset-2.txt +4 -0
  61. data/lib/less/js/test/less/errors/detached-ruleset-3.less +4 -0
  62. data/lib/less/js/test/less/errors/detached-ruleset-3.txt +4 -0
  63. data/lib/less/js/test/less/errors/detached-ruleset-4.less +5 -0
  64. data/lib/less/js/test/less/errors/detached-ruleset-4.txt +3 -0
  65. data/lib/less/js/test/less/errors/detached-ruleset-5.less +4 -0
  66. data/lib/less/js/test/less/errors/detached-ruleset-5.txt +3 -0
  67. data/lib/less/js/test/less/errors/detached-ruleset-6.less +5 -0
  68. data/lib/less/js/test/less/errors/detached-ruleset-6.txt +4 -0
  69. data/lib/less/js/test/less/errors/mixin-not-visible-in-scope-1.less +9 -0
  70. data/lib/less/js/test/less/errors/mixin-not-visible-in-scope-1.txt +4 -0
  71. data/lib/less/js/test/less/errors/percentage-missing-space.less +3 -0
  72. data/lib/less/js/test/less/errors/percentage-missing-space.txt +4 -0
  73. data/lib/less/js/test/less/functions.less +25 -7
  74. data/lib/less/js/test/less/import-reference.less +7 -4
  75. data/lib/less/js/test/less/import/import-reference.less +8 -0
  76. data/lib/less/js/test/less/merge.less +20 -1
  77. data/lib/less/js/test/less/mixins-guards.less +7 -1
  78. data/lib/less/js/test/less/mixins-pattern.less +3 -0
  79. data/lib/less/js/test/less/scope.less +25 -0
  80. data/lib/less/js/test/less/variables-in-at-rules.less +20 -0
  81. data/lib/less/version.rb +1 -1
  82. metadata +39 -2
@@ -202,14 +202,36 @@
202
202
  }
203
203
 
204
204
  Object.keys(groups).map(function (k) {
205
+
206
+ function toExpression(values) {
207
+ return new (tree.Expression)(values.map(function (p) {
208
+ return p.value;
209
+ }));
210
+ }
211
+
212
+ function toValue(values) {
213
+ return new (tree.Value)(values.map(function (p) {
214
+ return p;
215
+ }));
216
+ }
217
+
205
218
  parts = groups[k];
206
219
 
207
220
  if (parts.length > 1) {
208
221
  rule = parts[0];
209
-
210
- rule.value = new (tree.Value)(parts.map(function (p) {
211
- return p.value;
212
- }));
222
+ var spacedGroups = [];
223
+ var lastSpacedGroup = [];
224
+ parts.map(function (p) {
225
+ if (p.merge==="+") {
226
+ if (lastSpacedGroup.length > 0) {
227
+ spacedGroups.push(toExpression(lastSpacedGroup));
228
+ }
229
+ lastSpacedGroup = [];
230
+ }
231
+ lastSpacedGroup.push(p);
232
+ });
233
+ spacedGroups.push(toExpression(lastSpacedGroup));
234
+ rule.value = toValue(spacedGroups);
213
235
  }
214
236
  });
215
237
  }
@@ -28,7 +28,17 @@ var transparentKeyword = "transparent";
28
28
  tree.Color.prototype = {
29
29
  type: "Color",
30
30
  eval: function () { return this; },
31
- luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); },
31
+ luma: function () {
32
+ var r = this.rgb[0] / 255,
33
+ g = this.rgb[1] / 255,
34
+ b = this.rgb[2] / 255;
35
+
36
+ r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4);
37
+ g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4);
38
+ b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4);
39
+
40
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
41
+ },
32
42
 
33
43
  genCSS: function (env, output) {
34
44
  output.add(this.toCSS(env));
@@ -0,0 +1,20 @@
1
+ (function (tree) {
2
+
3
+ tree.DetachedRuleset = function (ruleset, frames) {
4
+ this.ruleset = ruleset;
5
+ this.frames = frames;
6
+ };
7
+ tree.DetachedRuleset.prototype = {
8
+ type: "DetachedRuleset",
9
+ accept: function (visitor) {
10
+ this.ruleset = visitor.visit(this.ruleset);
11
+ },
12
+ eval: function (env) {
13
+ var frames = this.frames || env.frames.slice(0);
14
+ return new tree.DetachedRuleset(this.ruleset, frames);
15
+ },
16
+ callEval: function (env) {
17
+ return this.ruleset.eval(this.frames ? new(tree.evalEnv)(env, this.frames.concat(env.frames)) : env);
18
+ }
19
+ };
20
+ })(require('../tree'));
@@ -89,17 +89,27 @@ tree.Dimension.prototype = {
89
89
 
90
90
  compare: function (other) {
91
91
  if (other instanceof tree.Dimension) {
92
- var a = this.unify(), b = other.unify(),
93
- aValue = a.value, bValue = b.value;
92
+ var a, b,
93
+ aValue, bValue;
94
+
95
+ if (this.unit.isEmpty() || other.unit.isEmpty()) {
96
+ a = this;
97
+ b = other;
98
+ } else {
99
+ a = this.unify();
100
+ b = other.unify();
101
+ if (a.unit.compare(b.unit) !== 0) {
102
+ return -1;
103
+ }
104
+ }
105
+ aValue = a.value;
106
+ bValue = b.value;
94
107
 
95
108
  if (bValue > aValue) {
96
109
  return -1;
97
110
  } else if (bValue < aValue) {
98
111
  return 1;
99
112
  } else {
100
- if (!b.unit.isEmpty() && a.unit.compare(b.unit) !== 0) {
101
- return -1;
102
- }
103
113
  return 0;
104
114
  }
105
115
  } else {
@@ -108,7 +118,7 @@ tree.Dimension.prototype = {
108
118
  },
109
119
 
110
120
  unify: function () {
111
- return this.convertTo({ length: 'm', duration: 's', angle: 'rad' });
121
+ return this.convertTo({ length: 'px', duration: 's', angle: 'rad' });
112
122
  },
113
123
 
114
124
  convertTo: function (conversions) {
@@ -161,6 +171,7 @@ tree.UnitConversions = {
161
171
  'cm': 0.01,
162
172
  'mm': 0.001,
163
173
  'in': 0.0254,
174
+ 'px': 0.0254 / 96,
164
175
  'pt': 0.0254 / 72,
165
176
  'pc': 0.0254 / 72 * 12
166
177
  },
@@ -1,58 +1,62 @@
1
1
  (function (tree) {
2
2
 
3
- tree.Directive = function (name, value, index, currentFileInfo) {
4
- this.name = name;
5
-
6
- if (Array.isArray(value)) {
7
- this.rules = [new(tree.Ruleset)(null, value)];
8
- this.rules[0].allowImports = true;
9
- } else {
10
- this.value = value;
3
+ tree.Directive = function (name, value, rules, index, currentFileInfo, debugInfo) {
4
+ this.name = name;
5
+ this.value = value;
6
+ if (rules) {
7
+ this.rules = rules;
8
+ this.rules.allowImports = true;
11
9
  }
12
10
  this.index = index;
13
11
  this.currentFileInfo = currentFileInfo;
14
-
12
+ this.debugInfo = debugInfo;
15
13
  };
14
+
16
15
  tree.Directive.prototype = {
17
16
  type: "Directive",
18
17
  accept: function (visitor) {
19
- if (this.rules) {
20
- this.rules = visitor.visitArray(this.rules);
18
+ var value = this.value, rules = this.rules;
19
+ if (rules) {
20
+ rules = visitor.visit(rules);
21
21
  }
22
- if (this.value) {
23
- this.value = visitor.visit(this.value);
22
+ if (value) {
23
+ value = visitor.visit(value);
24
24
  }
25
25
  },
26
26
  genCSS: function (env, output) {
27
+ var value = this.value, rules = this.rules;
27
28
  output.add(this.name, this.currentFileInfo, this.index);
28
- if (this.rules) {
29
- tree.outputRuleset(env, output, this.rules);
30
- } else {
29
+ if (value) {
31
30
  output.add(' ');
32
- this.value.genCSS(env, output);
31
+ value.genCSS(env, output);
32
+ }
33
+ if (rules) {
34
+ tree.outputRuleset(env, output, [rules]);
35
+ } else {
33
36
  output.add(';');
34
37
  }
35
38
  },
36
39
  toCSS: tree.toCSS,
37
40
  eval: function (env) {
38
- var evaldDirective = this;
39
- if (this.rules) {
40
- env.frames.unshift(this);
41
- evaldDirective = new(tree.Directive)(this.name, null, this.index, this.currentFileInfo);
42
- evaldDirective.rules = [this.rules[0].eval(env)];
43
- evaldDirective.rules[0].root = true;
44
- env.frames.shift();
41
+ var value = this.value, rules = this.rules;
42
+ if (value) {
43
+ value = value.eval(env);
44
+ }
45
+ if (rules) {
46
+ rules = rules.eval(env);
47
+ rules.root = true;
45
48
  }
46
- return evaldDirective;
49
+ return new(tree.Directive)(this.name, value, rules,
50
+ this.index, this.currentFileInfo, this.debugInfo);
47
51
  },
48
- variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); },
49
- find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); },
50
- rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); },
52
+ variable: function (name) { if (this.rules) return tree.Ruleset.prototype.variable.call(this.rules, name); },
53
+ find: function () { if (this.rules) return tree.Ruleset.prototype.find.apply(this.rules, arguments); },
54
+ rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); },
51
55
  markReferenced: function () {
52
56
  var i, rules;
53
57
  this.isReferenced = true;
54
58
  if (this.rules) {
55
- rules = this.rules[0].rules;
59
+ rules = this.rules.rules;
56
60
  for (i = 0; i < rules.length; i++) {
57
61
  if (rules[i].markReferenced) {
58
62
  rules[i].markReferenced();
@@ -92,7 +92,14 @@ tree.Import.prototype = {
92
92
  eval: function (env) {
93
93
  var ruleset, features = this.features && this.features.eval(env);
94
94
 
95
- if (this.skip) { return []; }
95
+ if (this.skip) {
96
+ if (typeof this.skip === "function") {
97
+ this.skip = this.skip();
98
+ }
99
+ if (this.skip) {
100
+ return [];
101
+ }
102
+ }
96
103
 
97
104
  if (this.options.inline) {
98
105
  //todo needs to reference css file not import
@@ -5,6 +5,7 @@ tree.Keyword.prototype = {
5
5
  type: "Keyword",
6
6
  eval: function () { return this; },
7
7
  genCSS: function (env, output) {
8
+ if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; }
8
9
  output.add(this.value);
9
10
  },
10
11
  toCSS: tree.toCSS,
@@ -74,6 +74,7 @@ tree.Media.prototype = {
74
74
  },
75
75
  markReferenced: function () {
76
76
  var i, rules = this.rules[0].rules;
77
+ this.rules[0].markReferenced();
77
78
  this.isReferenced = true;
78
79
  for (i = 0; i < rules.length; i++) {
79
80
  if (rules[i].markReferenced) {
@@ -147,6 +148,8 @@ tree.Media.prototype = {
147
148
  }
148
149
  },
149
150
  bubbleSelectors: function (selectors) {
151
+ if (!selectors)
152
+ return;
150
153
  this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])];
151
154
  }
152
155
  };
@@ -103,7 +103,7 @@ tree.mixin.Call.prototype = {
103
103
  mixin.originalRuleset = mixins[m].originalRuleset || mixins[m];
104
104
  }
105
105
  Array.prototype.push.apply(
106
- rules, mixin.eval(env, args, this.important).rules);
106
+ rules, mixin.evalCall(env, args, this.important).rules);
107
107
  } catch (e) {
108
108
  throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack };
109
109
  }
@@ -150,7 +150,7 @@ tree.mixin.Call.prototype = {
150
150
  }
151
151
  };
152
152
 
153
- tree.mixin.Definition = function (name, params, rules, condition, variadic) {
153
+ tree.mixin.Definition = function (name, params, rules, condition, variadic, frames) {
154
154
  this.name = name;
155
155
  this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])];
156
156
  this.params = params;
@@ -164,7 +164,7 @@ tree.mixin.Definition = function (name, params, rules, condition, variadic) {
164
164
  else { return count; }
165
165
  }, 0);
166
166
  this.parent = tree.Ruleset.prototype;
167
- this.frames = [];
167
+ this.frames = frames;
168
168
  };
169
169
  tree.mixin.Definition.prototype = {
170
170
  type: "MixinDefinition",
@@ -187,14 +187,15 @@ tree.mixin.Definition.prototype = {
187
187
  var frame = new(tree.Ruleset)(null, null),
188
188
  varargs, arg,
189
189
  params = this.params.slice(0),
190
- i, j, val, name, isNamedFound, argIndex;
190
+ i, j, val, name, isNamedFound, argIndex, argsLength = 0;
191
191
 
192
192
  mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames));
193
193
 
194
194
  if (args) {
195
195
  args = args.slice(0);
196
+ argsLength = args.length;
196
197
 
197
- for(i = 0; i < args.length; i++) {
198
+ for(i = 0; i < argsLength; i++) {
198
199
  arg = args[i];
199
200
  if (name = (arg && arg.name)) {
200
201
  isNamedFound = false;
@@ -224,9 +225,9 @@ tree.mixin.Definition.prototype = {
224
225
  arg = args && args[argIndex];
225
226
 
226
227
  if (name = params[i].name) {
227
- if (params[i].variadic && args) {
228
+ if (params[i].variadic) {
228
229
  varargs = [];
229
- for (j = argIndex; j < args.length; j++) {
230
+ for (j = argIndex; j < argsLength; j++) {
230
231
  varargs.push(args[j].value.eval(env));
231
232
  }
232
233
  frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env)));
@@ -239,7 +240,7 @@ tree.mixin.Definition.prototype = {
239
240
  frame.resetCache();
240
241
  } else {
241
242
  throw { type: 'Runtime', message: "wrong number of arguments for " + this.name +
242
- ' (' + args.length + ' for ' + this.arity + ')' };
243
+ ' (' + argsLength + ' for ' + this.arity + ')' };
243
244
  }
244
245
 
245
246
  frame.prependRule(new(tree.Rule)(name, val));
@@ -248,7 +249,7 @@ tree.mixin.Definition.prototype = {
248
249
  }
249
250
 
250
251
  if (params[i].variadic && args) {
251
- for (j = argIndex; j < args.length; j++) {
252
+ for (j = argIndex; j < argsLength; j++) {
252
253
  evaldArguments[j] = args[j].value.eval(env);
253
254
  }
254
255
  }
@@ -257,9 +258,12 @@ tree.mixin.Definition.prototype = {
257
258
 
258
259
  return frame;
259
260
  },
260
- eval: function (env, args, important) {
261
+ eval: function (env) {
262
+ return new tree.mixin.Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || env.frames.slice(0));
263
+ },
264
+ evalCall: function (env, args, important) {
261
265
  var _arguments = [],
262
- mixinFrames = this.frames.concat(env.frames),
266
+ mixinFrames = this.frames ? this.frames.concat(env.frames) : env.frames,
263
267
  frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments),
264
268
  rules, ruleset;
265
269
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) {
4
4
  this.name = name;
5
- this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]);
5
+ this.value = (value instanceof tree.Value || value instanceof tree.Ruleset) ? value : new(tree.Value)([value]);
6
6
  this.important = important ? ' ' + important.trim() : '';
7
7
  this.merge = merge;
8
8
  this.index = index;
@@ -30,7 +30,7 @@ tree.Rule.prototype = {
30
30
  },
31
31
  toCSS: tree.toCSS,
32
32
  eval: function (env) {
33
- var strictMathBypass = false, name = this.name;
33
+ var strictMathBypass = false, name = this.name, evaldValue;
34
34
  if (typeof name !== "string") {
35
35
  // expand 'primitive' name directly to get
36
36
  // things faster (~10% for benchmark.less):
@@ -43,14 +43,24 @@ tree.Rule.prototype = {
43
43
  env.strictMath = true;
44
44
  }
45
45
  try {
46
+ evaldValue = this.value.eval(env);
47
+
48
+ if (!this.variable && evaldValue.type === "DetachedRuleset") {
49
+ throw { message: "Rulesets cannot be evaluated on a property.",
50
+ index: this.index, filename: this.currentFileInfo.filename };
51
+ }
52
+
46
53
  return new(tree.Rule)(name,
47
- this.value.eval(env),
54
+ evaldValue,
48
55
  this.important,
49
56
  this.merge,
50
57
  this.index, this.currentFileInfo, this.inline);
51
58
  }
52
59
  catch(e) {
53
- e.index = e.index || this.index;
60
+ if (typeof e.index !== 'number') {
61
+ e.index = this.index;
62
+ e.filename = this.currentFileInfo.filename;
63
+ }
54
64
  throw e;
55
65
  }
56
66
  finally {
@@ -0,0 +1,16 @@
1
+ (function (tree) {
2
+
3
+ tree.RulesetCall = function (variable) {
4
+ this.variable = variable;
5
+ };
6
+ tree.RulesetCall.prototype = {
7
+ type: "RulesetCall",
8
+ accept: function (visitor) {
9
+ },
10
+ eval: function (env) {
11
+ var detachedRuleset = new(tree.Variable)(this.variable).eval(env);
12
+ return detachedRuleset.callEval(env);
13
+ }
14
+ };
15
+
16
+ })(require('../tree'));
@@ -20,7 +20,8 @@ tree.Ruleset.prototype = {
20
20
  },
21
21
  eval: function (env) {
22
22
  var thisSelectors = this.selectors, selectors,
23
- selCnt, i, defaultFunc = tree.defaultFunc;
23
+ selCnt, selector, i, defaultFunc = tree.defaultFunc, hasOnePassingSelector = false;
24
+
24
25
  if (thisSelectors && (selCnt = thisSelectors.length)) {
25
26
  selectors = [];
26
27
  defaultFunc.error({
@@ -28,9 +29,15 @@ tree.Ruleset.prototype = {
28
29
  message: "it is currently only allowed in parametric mixin guards,"
29
30
  });
30
31
  for (i = 0; i < selCnt; i++) {
31
- selectors.push(thisSelectors[i].eval(env));
32
+ selector = thisSelectors[i].eval(env);
33
+ selectors.push(selector);
34
+ if (selector.evaldCondition) {
35
+ hasOnePassingSelector = true;
36
+ }
32
37
  }
33
38
  defaultFunc.reset();
39
+ } else {
40
+ hasOnePassingSelector = true;
34
41
  }
35
42
 
36
43
  var rules = this.rules ? this.rules.slice(0) : null,
@@ -45,6 +52,10 @@ tree.Ruleset.prototype = {
45
52
  if(this.debugInfo) {
46
53
  ruleset.debugInfo = this.debugInfo;
47
54
  }
55
+
56
+ if (!hasOnePassingSelector) {
57
+ rules.length = 0;
58
+ }
48
59
 
49
60
  // push the current ruleset to the frames stack
50
61
  var envFrames = env.frames;
@@ -66,8 +77,8 @@ tree.Ruleset.prototype = {
66
77
  // so they can be evaluated like closures when the time comes.
67
78
  var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0;
68
79
  for (i = 0; i < rsRuleCnt; i++) {
69
- if (rsRules[i] instanceof tree.mixin.Definition) {
70
- rsRules[i].frames = envFrames.slice(0);
80
+ if (rsRules[i] instanceof tree.mixin.Definition || rsRules[i] instanceof tree.DetachedRuleset) {
81
+ rsRules[i] = rsRules[i].eval(env);
71
82
  }
72
83
  }
73
84
 
@@ -90,28 +101,43 @@ tree.Ruleset.prototype = {
90
101
  rsRuleCnt += rules.length - 1;
91
102
  i += rules.length-1;
92
103
  ruleset.resetCache();
104
+ } else if (rsRules[i] instanceof tree.RulesetCall) {
105
+ /*jshint loopfunc:true */
106
+ rules = rsRules[i].eval(env).rules.filter(function(r) {
107
+ if ((r instanceof tree.Rule) && r.variable) {
108
+ // do not pollute the scope at all
109
+ return false;
110
+ }
111
+ return true;
112
+ });
113
+ rsRules.splice.apply(rsRules, [i, 1].concat(rules));
114
+ rsRuleCnt += rules.length - 1;
115
+ i += rules.length-1;
116
+ ruleset.resetCache();
93
117
  }
94
118
  }
95
119
 
96
120
  // Evaluate everything else
97
121
  for (i = 0; i < rsRules.length; i++) {
98
122
  rule = rsRules[i];
99
- if (! (rule instanceof tree.mixin.Definition)) {
123
+ if (! (rule instanceof tree.mixin.Definition || rule instanceof tree.DetachedRuleset)) {
100
124
  rsRules[i] = rule = rule.eval ? rule.eval(env) : rule;
101
- // for rulesets, check if it is a css guard and can be removed
102
- if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) {
103
- // check if it can be folded in (e.g. & where)
104
- if (rule.selectors[0].isJustParentSelector()) {
105
- rsRules.splice(i--, 1);
106
- // cannot call if there is no selector, so we can just continue
107
- if (!rule.selectors[0].evaldCondition) {
108
- continue;
109
- }
110
- for(var j = 0; j < rule.rules.length; j++) {
111
- subRule = rule.rules[j];
112
- if (!(subRule instanceof tree.Rule) || !subRule.variable) {
113
- rsRules.splice(++i, 0, subRule);
114
- }
125
+ }
126
+ }
127
+
128
+ // Evaluate everything else
129
+ for (i = 0; i < rsRules.length; i++) {
130
+ rule = rsRules[i];
131
+ // for rulesets, check if it is a css guard and can be removed
132
+ if (rule instanceof tree.Ruleset && rule.selectors && rule.selectors.length === 1) {
133
+ // check if it can be folded in (e.g. & where)
134
+ if (rule.selectors[0].isJustParentSelector()) {
135
+ rsRules.splice(i--, 1);
136
+
137
+ for(var j = 0; j < rule.rules.length; j++) {
138
+ subRule = rule.rules[j];
139
+ if (!(subRule instanceof tree.Rule) || !subRule.variable) {
140
+ rsRules.splice(++i, 0, subRule);
115
141
  }
116
142
  }
117
143
  }
@@ -345,6 +371,9 @@ tree.Ruleset.prototype = {
345
371
  toCSS: tree.toCSS,
346
372
 
347
373
  markReferenced: function () {
374
+ if (!this.selectors) {
375
+ return;
376
+ }
348
377
  for (var s = 0; s < this.selectors.length; s++) {
349
378
  this.selectors[s].markReferenced();
350
379
  }