less 2.0.4 → 2.1.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 (112) hide show
  1. data/.gitmodules +0 -3
  2. data/.travis.yml +10 -0
  3. data/README.md +1 -1
  4. data/Rakefile +2 -0
  5. data/bin/lessc +21 -2
  6. data/less.gemspec +5 -3
  7. data/lib/less/defaults.rb +13 -0
  8. data/lib/less/js/CHANGELOG +41 -0
  9. data/lib/less/js/Makefile +21 -2
  10. data/lib/less/js/benchmark/benchmark.less +11 -11
  11. data/lib/less/js/benchmark/less-benchmark.js +3 -5
  12. data/lib/less/js/bin/lessc +52 -18
  13. data/lib/less/js/build/amd.js +6 -0
  14. data/lib/less/js/build/require-rhino.js +7 -0
  15. data/lib/less/js/dist/{less-1.0.44.js → less-1.1.4.js} +159 -45
  16. data/lib/less/js/dist/less-1.1.4.min.js +16 -0
  17. data/lib/less/js/dist/less-1.1.5.js +2805 -0
  18. data/lib/less/js/dist/less-1.1.5.min.js +9 -0
  19. data/lib/less/js/dist/less-1.1.6.js +3004 -0
  20. data/lib/less/js/dist/less-1.1.6.min.js +9 -0
  21. data/lib/less/js/dist/less-1.2.0.js +3293 -0
  22. data/lib/less/js/dist/less-1.2.0.min.js +9 -0
  23. data/lib/less/js/dist/less-1.2.1.js +3318 -0
  24. data/lib/less/js/dist/less-1.2.1.min.js +9 -0
  25. data/lib/less/js/dist/less-1.2.2.js +3337 -0
  26. data/lib/less/js/dist/less-1.2.2.min.js +9 -0
  27. data/lib/less/js/dist/less-1.3.0.js +3478 -0
  28. data/lib/less/js/dist/less-1.3.0.min.js +9 -0
  29. data/lib/less/js/dist/less-rhino-1.1.3.js +2460 -0
  30. data/lib/less/js/dist/less-rhino-1.1.5.js +2481 -0
  31. data/lib/less/js/lib/less/browser.js +38 -27
  32. data/lib/less/js/lib/less/colors.js +151 -0
  33. data/lib/less/js/lib/less/cssmin.js +355 -0
  34. data/lib/less/js/lib/less/functions.js +61 -7
  35. data/lib/less/js/lib/less/index.js +48 -37
  36. data/lib/less/js/lib/less/parser.js +327 -120
  37. data/lib/less/js/lib/less/rhino.js +62 -0
  38. data/lib/less/js/lib/less/tree/alpha.js +1 -1
  39. data/lib/less/js/lib/less/tree/anonymous.js +1 -1
  40. data/lib/less/js/lib/less/tree/assignment.js +17 -0
  41. data/lib/less/js/lib/less/tree/call.js +7 -4
  42. data/lib/less/js/lib/less/tree/color.js +9 -6
  43. data/lib/less/js/lib/less/tree/comment.js +1 -1
  44. data/lib/less/js/lib/less/tree/condition.js +42 -0
  45. data/lib/less/js/lib/less/tree/dimension.js +16 -1
  46. data/lib/less/js/lib/less/tree/directive.js +4 -2
  47. data/lib/less/js/lib/less/tree/element.js +20 -5
  48. data/lib/less/js/lib/less/tree/expression.js +2 -2
  49. data/lib/less/js/lib/less/tree/import.js +16 -14
  50. data/lib/less/js/lib/less/tree/javascript.js +1 -1
  51. data/lib/less/js/lib/less/tree/keyword.js +12 -2
  52. data/lib/less/js/lib/less/tree/media.js +114 -0
  53. data/lib/less/js/lib/less/tree/mixin.js +49 -20
  54. data/lib/less/js/lib/less/tree/operation.js +1 -1
  55. data/lib/less/js/lib/less/tree/paren.js +16 -0
  56. data/lib/less/js/lib/less/tree/quoted.js +2 -2
  57. data/lib/less/js/lib/less/tree/rule.js +8 -4
  58. data/lib/less/js/lib/less/tree/ruleset.js +49 -12
  59. data/lib/less/js/lib/less/tree/selector.js +18 -4
  60. data/lib/less/js/lib/less/tree/url.js +2 -2
  61. data/lib/less/js/lib/less/tree/value.js +1 -1
  62. data/lib/less/js/lib/less/tree/variable.js +5 -3
  63. data/lib/less/js/lib/less/tree.js +6 -2
  64. data/lib/less/js/package.json +2 -2
  65. data/lib/less/js/test/css/colors.css +16 -0
  66. data/lib/less/js/test/css/comments.css +9 -5
  67. data/lib/less/js/test/css/css-3.css +18 -2
  68. data/lib/less/js/test/css/css-escapes.css +1 -1
  69. data/lib/less/js/test/css/css.css +11 -4
  70. data/lib/less/js/test/css/functions.css +13 -0
  71. data/lib/less/js/test/css/ie-filters.css +5 -0
  72. data/lib/less/js/test/css/import.css +12 -3
  73. data/lib/less/js/test/css/media.css +60 -2
  74. data/lib/less/js/test/css/mixins-args.css +17 -2
  75. data/lib/less/js/test/css/mixins-guards.css +58 -0
  76. data/lib/less/js/test/css/mixins-important.css +17 -0
  77. data/lib/less/js/test/css/mixins-pattern.css +4 -6
  78. data/lib/less/js/test/css/mixins.css +21 -0
  79. data/lib/less/js/test/css/operations.css +3 -0
  80. data/lib/less/js/test/css/parens.css +1 -1
  81. data/lib/less/js/test/css/rulesets.css +6 -2
  82. data/lib/less/js/test/css/scope.css +6 -6
  83. data/lib/less/js/test/css/selectors.css +38 -1
  84. data/lib/less/js/test/css/strings.css +6 -4
  85. data/lib/less/js/test/css/variables.css +3 -0
  86. data/lib/less/js/test/css/whitespace.css +5 -3
  87. data/lib/less/js/test/less/colors.less +19 -0
  88. data/lib/less/js/test/less/comments.less +8 -6
  89. data/lib/less/js/test/less/css-3.less +10 -0
  90. data/lib/less/js/test/less/css.less +4 -0
  91. data/lib/less/js/test/less/functions.less +15 -1
  92. data/lib/less/js/test/less/ie-filters.less +8 -0
  93. data/lib/less/js/test/less/import/import-test-e.less +2 -0
  94. data/lib/less/js/test/less/import.less +4 -1
  95. data/lib/less/js/test/less/media.less +55 -1
  96. data/lib/less/js/test/less/mixins-args.less +20 -1
  97. data/lib/less/js/test/less/mixins-guards.less +94 -0
  98. data/lib/less/js/test/less/mixins-important.less +18 -0
  99. data/lib/less/js/test/less/mixins-pattern.less +3 -0
  100. data/lib/less/js/test/less/mixins.less +20 -0
  101. data/lib/less/js/test/less/operations.less +4 -0
  102. data/lib/less/js/test/less/selectors.less +31 -0
  103. data/lib/less/js/test/less/strings.less +2 -0
  104. data/lib/less/js/test/less/variables.less +4 -0
  105. data/lib/less/js/test/less-test.js +3 -5
  106. data/lib/less/loader.rb +24 -25
  107. data/lib/less/parser.rb +32 -6
  108. data/lib/less/version.rb +1 -1
  109. data/lib/less.rb +7 -8
  110. data/spec/less/faulty/faulty.less +3 -0
  111. data/spec/less/parser_spec.rb +28 -2
  112. metadata +321 -149
@@ -0,0 +1,62 @@
1
+ var name;
2
+
3
+ function loadStyleSheet(sheet, callback, reload, remaining) {
4
+ var sheetName = name.slice(0, name.lastIndexOf('/') + 1) + sheet.href;
5
+ var input = readFile(sheetName);
6
+ var parser = new less.Parser({
7
+ paths: [sheet.href.replace(/[\w\.-]+$/, '')]
8
+ });
9
+ parser.parse(input, function (e, root) {
10
+ if (e) {
11
+ print("Error: " + e);
12
+ quit(1);
13
+ }
14
+ callback(root, sheet, { local: false, lastModified: 0, remaining: remaining });
15
+ });
16
+
17
+ // callback({}, sheet, { local: true, remaining: remaining });
18
+ }
19
+
20
+ function writeFile(filename, content) {
21
+ var fstream = new java.io.FileWriter(filename);
22
+ var out = new java.io.BufferedWriter(fstream);
23
+ out.write(content);
24
+ out.close();
25
+ }
26
+
27
+ // Command line integration via Rhino
28
+ (function (args) {
29
+ name = args[0];
30
+ var output = args[1];
31
+
32
+ if (!name) {
33
+ print('No files present in the fileset; Check your pattern match in build.xml');
34
+ quit(1);
35
+ }
36
+ path = name.split("/");path.pop();path=path.join("/")
37
+
38
+ var input = readFile(name);
39
+
40
+ if (!input) {
41
+ print('lesscss: couldn\'t open file ' + name);
42
+ quit(1);
43
+ }
44
+
45
+ var result;
46
+ var parser = new less.Parser();
47
+ parser.parse(input, function (e, root) {
48
+ if (e) {
49
+ quit(1);
50
+ } else {
51
+ result = root.toCSS();
52
+ if (output) {
53
+ writeFile(output, result);
54
+ print("Written to " + output);
55
+ } else {
56
+ print(result);
57
+ }
58
+ quit(0);
59
+ }
60
+ });
61
+ print("done");
62
+ }(arguments));
@@ -14,4 +14,4 @@ tree.Alpha.prototype = {
14
14
  }
15
15
  };
16
16
 
17
- })(require('less/tree'));
17
+ })(require('../tree'));
@@ -10,4 +10,4 @@ tree.Anonymous.prototype = {
10
10
  eval: function () { return this }
11
11
  };
12
12
 
13
- })(require('less/tree'));
13
+ })(require('../tree'));
@@ -0,0 +1,17 @@
1
+ (function (tree) {
2
+
3
+ tree.Assignment = function (key, val) {
4
+ this.key = key;
5
+ this.value = val;
6
+ };
7
+ tree.Assignment.prototype = {
8
+ toCSS: function () {
9
+ return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value);
10
+ },
11
+ eval: function (env) {
12
+ if (this.value.eval) { this.value = this.value.eval(env) }
13
+ return this;
14
+ }
15
+ };
16
+
17
+ })(require('../tree'));
@@ -3,10 +3,11 @@
3
3
  //
4
4
  // A function call node.
5
5
  //
6
- tree.Call = function (name, args, index) {
6
+ tree.Call = function (name, args, index, filename) {
7
7
  this.name = name;
8
8
  this.args = args;
9
9
  this.index = index;
10
+ this.filename = filename;
10
11
  };
11
12
  tree.Call.prototype = {
12
13
  //
@@ -28,8 +29,10 @@ tree.Call.prototype = {
28
29
  try {
29
30
  return tree.functions[this.name].apply(tree.functions, args);
30
31
  } catch (e) {
31
- throw { message: "error evaluating function `" + this.name + "`",
32
- index: this.index };
32
+ throw { type: e.type || "Runtime",
33
+ message: "error evaluating function `" + this.name + "`" +
34
+ (e.message ? ': ' + e.message : ''),
35
+ index: this.index, filename: this.filename };
33
36
  }
34
37
  } else { // 2.
35
38
  return new(tree.Anonymous)(this.name +
@@ -42,4 +45,4 @@ tree.Call.prototype = {
42
45
  }
43
46
  };
44
47
 
45
- })(require('less/tree'));
48
+ })(require('../tree'));
@@ -15,11 +15,6 @@ tree.Color = function (rgb, a) {
15
15
  this.rgb = rgb.match(/.{2}/g).map(function (c) {
16
16
  return parseInt(c, 16);
17
17
  });
18
- } else if (rgb.length == 8) {
19
- this.alpha = parseInt(rgb.substring(0,2), 16) / 255.0;
20
- this.rgb = rgb.substr(2).match(/.{2}/g).map(function (c) {
21
- return parseInt(c, 16);
22
- });
23
18
  } else {
24
19
  this.rgb = rgb.split('').map(function (c) {
25
20
  return parseInt(c + c, 16);
@@ -91,8 +86,16 @@ tree.Color.prototype = {
91
86
  h /= 6;
92
87
  }
93
88
  return { h: h * 360, s: s, l: l, a: a };
89
+ },
90
+ toARGB: function () {
91
+ var argb = [Math.round(this.alpha * 255)].concat(this.rgb);
92
+ return '#' + argb.map(function (i) {
93
+ i = Math.round(i);
94
+ i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16);
95
+ return i.length === 1 ? '0' + i : i;
96
+ }).join('');
94
97
  }
95
98
  };
96
99
 
97
100
 
98
- })(require('less/tree'));
101
+ })(require('../tree'));
@@ -11,4 +11,4 @@ tree.Comment.prototype = {
11
11
  eval: function () { return this }
12
12
  };
13
13
 
14
- })(require('less/tree'));
14
+ })(require('../tree'));
@@ -0,0 +1,42 @@
1
+ (function (tree) {
2
+
3
+ tree.Condition = function (op, l, r, i, negate) {
4
+ this.op = op.trim();
5
+ this.lvalue = l;
6
+ this.rvalue = r;
7
+ this.index = i;
8
+ this.negate = negate;
9
+ };
10
+ tree.Condition.prototype.eval = function (env) {
11
+ var a = this.lvalue.eval(env),
12
+ b = this.rvalue.eval(env);
13
+
14
+ var i = this.index, result;
15
+
16
+ var result = (function (op) {
17
+ switch (op) {
18
+ case 'and':
19
+ return a && b;
20
+ case 'or':
21
+ return a || b;
22
+ default:
23
+ if (a.compare) {
24
+ result = a.compare(b);
25
+ } else if (b.compare) {
26
+ result = b.compare(a);
27
+ } else {
28
+ throw { type: "Type",
29
+ message: "Unable to perform comparison",
30
+ index: i };
31
+ }
32
+ switch (result) {
33
+ case -1: return op === '<' || op === '=<';
34
+ case 0: return op === '=' || op === '>=' || op === '=<';
35
+ case 1: return op === '>' || op === '>=';
36
+ }
37
+ }
38
+ })(this.op);
39
+ return this.negate ? !result : result;
40
+ };
41
+
42
+ })(require('../tree'));
@@ -28,7 +28,22 @@ tree.Dimension.prototype = {
28
28
  return new(tree.Dimension)
29
29
  (tree.operate(op, this.value, other.value),
30
30
  this.unit || other.unit);
31
+ },
32
+
33
+ // TODO: Perform unit conversion before comparing
34
+ compare: function (other) {
35
+ if (other instanceof tree.Dimension) {
36
+ if (other.value > this.value) {
37
+ return -1;
38
+ } else if (other.value < this.value) {
39
+ return 1;
40
+ } else {
41
+ return 0;
42
+ }
43
+ } else {
44
+ return -1;
45
+ }
31
46
  }
32
47
  };
33
48
 
34
- })(require('less/tree'));
49
+ })(require('../tree'));
@@ -1,9 +1,11 @@
1
1
  (function (tree) {
2
2
 
3
- tree.Directive = function (name, value) {
3
+ tree.Directive = function (name, value, features) {
4
4
  this.name = name;
5
+
5
6
  if (Array.isArray(value)) {
6
7
  this.ruleset = new(tree.Ruleset)([], value);
8
+ this.ruleset.allowImports = true;
7
9
  } else {
8
10
  this.value = value;
9
11
  }
@@ -30,4 +32,4 @@ tree.Directive.prototype = {
30
32
  rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }
31
33
  };
32
34
 
33
- })(require('less/tree'));
35
+ })(require('../tree'));
@@ -1,17 +1,32 @@
1
1
  (function (tree) {
2
2
 
3
- tree.Element = function (combinator, value) {
3
+ tree.Element = function (combinator, value, index) {
4
4
  this.combinator = combinator instanceof tree.Combinator ?
5
5
  combinator : new(tree.Combinator)(combinator);
6
- this.value = value.trim();
6
+
7
+ if (typeof(value) === 'string') {
8
+ this.value = value.trim();
9
+ } else if (value) {
10
+ this.value = value;
11
+ } else {
12
+ this.value = "";
13
+ }
14
+ this.index = index;
15
+ };
16
+ tree.Element.prototype.eval = function (env) {
17
+ return new(tree.Element)(this.combinator,
18
+ this.value.eval ? this.value.eval(env) : this.value,
19
+ this.index);
7
20
  };
8
21
  tree.Element.prototype.toCSS = function (env) {
9
- return this.combinator.toCSS(env || {}) + this.value;
22
+ return this.combinator.toCSS(env || {}) + (this.value.toCSS ? this.value.toCSS(env) : this.value);
10
23
  };
11
24
 
12
25
  tree.Combinator = function (value) {
13
26
  if (value === ' ') {
14
27
  this.value = ' ';
28
+ } else if (value === '& ') {
29
+ this.value = '& ';
15
30
  } else {
16
31
  this.value = value ? value.trim() : "";
17
32
  }
@@ -21,12 +36,12 @@ tree.Combinator.prototype.toCSS = function (env) {
21
36
  '' : '',
22
37
  ' ' : ' ',
23
38
  '&' : '',
39
+ '& ' : ' ',
24
40
  ':' : ' :',
25
- '::': '::',
26
41
  '+' : env.compress ? '+' : ' + ',
27
42
  '~' : env.compress ? '~' : ' ~ ',
28
43
  '>' : env.compress ? '>' : ' > '
29
44
  }[this.value];
30
45
  };
31
46
 
32
- })(require('less/tree'));
47
+ })(require('../tree'));
@@ -15,9 +15,9 @@ tree.Expression.prototype = {
15
15
  },
16
16
  toCSS: function (env) {
17
17
  return this.value.map(function (e) {
18
- return e.toCSS(env);
18
+ return e.toCSS ? e.toCSS(env) : '';
19
19
  }).join(' ');
20
20
  }
21
21
  };
22
22
 
23
- })(require('less/tree'));
23
+ })(require('../tree'));
@@ -11,27 +11,27 @@
11
11
  // `import,push`, we also pass it a callback, which it'll call once
12
12
  // the file has been fetched, and parsed.
13
13
  //
14
- tree.Import = function (path, imports) {
14
+ tree.Import = function (path, imports, features, index) {
15
15
  var that = this;
16
16
 
17
+ this.index = index;
17
18
  this._path = path;
19
+ this.features = features && new(tree.Value)(features);
18
20
 
19
21
  // The '.less' extension is optional
20
22
  if (path instanceof tree.Quoted) {
21
- this.path = /\.(le?|c)ss$/.test(path.value) ? path.value : path.value + '.less';
23
+ this.path = /\.(le?|c)ss(\?.*)?$/.test(path.value) ? path.value : path.value + '.less';
22
24
  } else {
23
25
  this.path = path.value.value || path.value;
24
26
  }
25
27
 
26
- this.css = /css$/.test(this.path);
28
+ this.css = /css(\?.*)?$/.test(this.path);
27
29
 
28
30
  // Only pre-compile .less files
29
31
  if (! this.css) {
30
- imports.push(this.path, function (root) {
31
- if (! root) {
32
- throw new(Error)("Error parsing " + that.path);
33
- }
34
- that.root = root;
32
+ imports.push(this.path, function (e, root) {
33
+ if (e) { e.index = index }
34
+ that.root = root || new(tree.Ruleset)([], []);
35
35
  });
36
36
  }
37
37
  };
@@ -46,20 +46,22 @@ tree.Import = function (path, imports) {
46
46
  // ruleset.
47
47
  //
48
48
  tree.Import.prototype = {
49
- toCSS: function () {
49
+ toCSS: function (env) {
50
+ var features = this.features ? ' ' + this.features.toCSS(env) : '';
51
+
50
52
  if (this.css) {
51
- return "@import " + this._path.toCSS() + ';\n';
53
+ return "@import " + this._path.toCSS() + features + ';\n';
52
54
  } else {
53
55
  return "";
54
56
  }
55
57
  },
56
58
  eval: function (env) {
57
- var ruleset;
59
+ var ruleset, features = this.features && this.features.eval(env);
58
60
 
59
61
  if (this.css) {
60
62
  return this;
61
63
  } else {
62
- ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0));
64
+ ruleset = new(tree.Ruleset)([], this.root.rules.slice(0));
63
65
 
64
66
  for (var i = 0; i < ruleset.rules.length; i++) {
65
67
  if (ruleset.rules[i] instanceof tree.Import) {
@@ -69,9 +71,9 @@ tree.Import.prototype = {
69
71
  [i, 1].concat(ruleset.rules[i].eval(env)));
70
72
  }
71
73
  }
72
- return ruleset.rules;
74
+ return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules;
73
75
  }
74
76
  }
75
77
  };
76
78
 
77
- })(require('less/tree'));
79
+ })(require('../tree'));
@@ -47,5 +47,5 @@ tree.JavaScript.prototype = {
47
47
  }
48
48
  };
49
49
 
50
- })(require('less/tree'));
50
+ })(require('../tree'));
51
51
 
@@ -3,7 +3,17 @@
3
3
  tree.Keyword = function (value) { this.value = value };
4
4
  tree.Keyword.prototype = {
5
5
  eval: function () { return this },
6
- toCSS: function () { return this.value }
6
+ toCSS: function () { return this.value },
7
+ compare: function (other) {
8
+ if (other instanceof tree.Keyword) {
9
+ return other.value === this.value ? 0 : 1;
10
+ } else {
11
+ return -1;
12
+ }
13
+ }
7
14
  };
8
15
 
9
- })(require('less/tree'));
16
+ tree.True = new(tree.Keyword)('true');
17
+ tree.False = new(tree.Keyword)('false');
18
+
19
+ })(require('../tree'));
@@ -0,0 +1,114 @@
1
+ (function (tree) {
2
+
3
+ tree.Media = function (value, features) {
4
+ var el = new(tree.Element)('&', null, 0),
5
+ selectors = [new(tree.Selector)([el])];
6
+
7
+ this.features = new(tree.Value)(features);
8
+ this.ruleset = new(tree.Ruleset)(selectors, value);
9
+ this.ruleset.allowImports = true;
10
+ };
11
+ tree.Media.prototype = {
12
+ toCSS: function (ctx, env) {
13
+ var features = this.features.toCSS(env);
14
+
15
+ this.ruleset.root = (ctx.length === 0 || ctx[0].multiMedia);
16
+ return '@media ' + features + (env.compress ? '{' : ' {\n ') +
17
+ this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n ') +
18
+ (env.compress ? '}': '\n}\n');
19
+ },
20
+ eval: function (env) {
21
+ if (!env.mediaBlocks) {
22
+ env.mediaBlocks = [];
23
+ env.mediaPath = [];
24
+ }
25
+
26
+ var blockIndex = env.mediaBlocks.length;
27
+ env.mediaPath.push(this);
28
+ env.mediaBlocks.push(this);
29
+
30
+ var media = new(tree.Media)([], []);
31
+ media.features = this.features.eval(env);
32
+
33
+ env.frames.unshift(this.ruleset);
34
+ media.ruleset = this.ruleset.eval(env);
35
+ env.frames.shift();
36
+
37
+ env.mediaBlocks[blockIndex] = media;
38
+ env.mediaPath.pop();
39
+
40
+ return env.mediaPath.length === 0 ? media.evalTop(env) :
41
+ media.evalNested(env)
42
+ },
43
+ variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) },
44
+ find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) },
45
+ rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) },
46
+
47
+ evalTop: function (env) {
48
+ var result = this;
49
+
50
+ // Render all dependent Media blocks.
51
+ if (env.mediaBlocks.length > 1) {
52
+ var el = new(tree.Element)('&', null, 0);
53
+ var selectors = [new(tree.Selector)([el])];
54
+ result = new(tree.Ruleset)(selectors, env.mediaBlocks);
55
+ result.multiMedia = true;
56
+ }
57
+
58
+ delete env.mediaBlocks;
59
+ delete env.mediaPath;
60
+
61
+ return result;
62
+ },
63
+ evalNested: function (env) {
64
+ var i, value,
65
+ path = env.mediaPath.concat([this]);
66
+
67
+ // Extract the media-query conditions separated with `,` (OR).
68
+ for (i = 0; i < path.length; i++) {
69
+ value = path[i].features instanceof tree.Value ?
70
+ path[i].features.value : path[i].features;
71
+ path[i] = Array.isArray(value) ? value : [value];
72
+ }
73
+
74
+ // Trace all permutations to generate the resulting media-query.
75
+ //
76
+ // (a, b and c) with nested (d, e) ->
77
+ // a and d
78
+ // a and e
79
+ // b and c and d
80
+ // b and c and e
81
+ this.features = new(tree.Value)(this.permute(path).map(function (path) {
82
+ path = path.map(function (fragment) {
83
+ return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment);
84
+ });
85
+
86
+ for(i = path.length - 1; i > 0; i--) {
87
+ path.splice(i, 0, new(tree.Anonymous)("and"));
88
+ }
89
+
90
+ return new(tree.Expression)(path);
91
+ }));
92
+
93
+ // Fake a tree-node that doesn't output anything.
94
+ return new(tree.Ruleset)([], []);
95
+ },
96
+ permute: function (arr) {
97
+ if (arr.length === 0) {
98
+ return [];
99
+ } else if (arr.length === 1) {
100
+ return arr[0];
101
+ } else {
102
+ var result = [];
103
+ var rest = this.permute(arr.slice(1));
104
+ for (var i = 0; i < rest.length; i++) {
105
+ for (var j = 0; j < arr[0].length; j++) {
106
+ result.push([arr[0][j]].concat(rest[i]));
107
+ }
108
+ }
109
+ return result;
110
+ }
111
+ }
112
+ };
113
+
114
+ })(require('../tree'));
@@ -1,10 +1,12 @@
1
1
  (function (tree) {
2
2
 
3
3
  tree.mixin = {};
4
- tree.mixin.Call = function (elements, args, index) {
4
+ tree.mixin.Call = function (elements, args, index, filename, important) {
5
5
  this.selector = new(tree.Selector)(elements);
6
6
  this.arguments = args;
7
7
  this.index = index;
8
+ this.filename = filename;
9
+ this.important = important;
8
10
  };
9
11
  tree.mixin.Call.prototype = {
10
12
  eval: function (env) {
@@ -17,34 +19,38 @@ tree.mixin.Call.prototype = {
17
19
  if (mixins[m].match(args, env)) {
18
20
  try {
19
21
  Array.prototype.push.apply(
20
- rules, mixins[m].eval(env, this.arguments).rules);
22
+ rules, mixins[m].eval(env, this.arguments, this.important).rules);
21
23
  match = true;
22
24
  } catch (e) {
23
- throw { message: e.message, index: e.index, stack: e.stack, call: this.index };
25
+ throw { message: e.message, index: this.index, filename: this.filename, stack: e.stack };
24
26
  }
25
27
  }
26
28
  }
27
29
  if (match) {
28
30
  return rules;
29
31
  } else {
30
- throw { message: 'No matching definition was found for `' +
32
+ throw { type: 'Runtime',
33
+ message: 'No matching definition was found for `' +
31
34
  this.selector.toCSS().trim() + '(' +
32
35
  this.arguments.map(function (a) {
33
36
  return a.toCSS();
34
37
  }).join(', ') + ")`",
35
- index: this.index };
38
+ index: this.index, filename: this.filename };
36
39
  }
37
40
  }
38
41
  }
39
- throw { message: this.selector.toCSS().trim() + " is undefined",
40
- index: this.index };
42
+ throw { type: 'Name',
43
+ message: this.selector.toCSS().trim() + " is undefined",
44
+ index: this.index, filename: this.filename };
41
45
  }
42
46
  };
43
47
 
44
- tree.mixin.Definition = function (name, params, rules) {
48
+ tree.mixin.Definition = function (name, params, rules, condition, variadic) {
45
49
  this.name = name;
46
50
  this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])];
47
51
  this.params = params;
52
+ this.condition = condition;
53
+ this.variadic = variadic;
48
54
  this.arity = params.length;
49
55
  this.rules = rules;
50
56
  this._lookups = {};
@@ -62,33 +68,56 @@ tree.mixin.Definition.prototype = {
62
68
  find: function () { return this.parent.find.apply(this, arguments) },
63
69
  rulesets: function () { return this.parent.rulesets.apply(this) },
64
70
 
65
- eval: function (env, args) {
66
- var frame = new(tree.Ruleset)(null, []), context, _arguments = [];
71
+ evalParams: function (env, args) {
72
+ var frame = new(tree.Ruleset)(null, []), varargs;
67
73
 
68
- for (var i = 0, val; i < this.params.length; i++) {
69
- if (this.params[i].name) {
70
- if (val = (args && args[i]) || this.params[i].value) {
71
- frame.rules.unshift(new(tree.Rule)(this.params[i].name, val.eval(env)));
74
+ for (var i = 0, val, name; i < this.params.length; i++) {
75
+ if (name = this.params[i].name) {
76
+ if (this.params[i].variadic && args) {
77
+ varargs = [];
78
+ for (var j = i; j < args.length; j++) {
79
+ varargs.push(args[j].eval(env));
80
+ }
81
+ frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env)));
82
+ } else if (val = (args && args[i]) || this.params[i].value) {
83
+ frame.rules.unshift(new(tree.Rule)(name, val.eval(env)));
72
84
  } else {
73
- throw { message: "wrong number of arguments for " + this.name +
85
+ throw { type: 'Runtime', message: "wrong number of arguments for " + this.name +
74
86
  ' (' + args.length + ' for ' + this.arity + ')' };
75
87
  }
76
88
  }
77
89
  }
90
+ return frame;
91
+ },
92
+ eval: function (env, args, important) {
93
+ var frame = this.evalParams(env, args), context, _arguments = [], rules, start;
94
+
78
95
  for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) {
79
96
  _arguments.push(args[i] || this.params[i].value);
80
97
  }
81
98
  frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env)));
82
99
 
83
- return new(tree.Ruleset)(null, this.rules.slice(0)).eval({
100
+ rules = important ?
101
+ this.rules.map(function (r) {
102
+ return new(tree.Rule)(r.name, r.value, '!important', r.index);
103
+ }) : this.rules.slice(0);
104
+
105
+ return new(tree.Ruleset)(null, rules).eval({
84
106
  frames: [this, frame].concat(this.frames, env.frames)
85
107
  });
86
108
  },
87
109
  match: function (args, env) {
88
- var argsLength = (args && args.length) || 0, len;
110
+ var argsLength = (args && args.length) || 0, len, frame;
111
+
112
+ if (! this.variadic) {
113
+ if (argsLength < this.required) { return false }
114
+ if (argsLength > this.params.length) { return false }
115
+ if ((this.required > 0) && (argsLength > this.params.length)) { return false }
116
+ }
89
117
 
90
- if (argsLength < this.required) { return false }
91
- if ((this.required > 0) && (argsLength > this.params.length)) { return false }
118
+ if (this.condition && !this.condition.eval({
119
+ frames: [this.evalParams(env, args)].concat(env.frames)
120
+ })) { return false }
92
121
 
93
122
  len = Math.min(argsLength, this.arity);
94
123
 
@@ -103,4 +132,4 @@ tree.mixin.Definition.prototype = {
103
132
  }
104
133
  };
105
134
 
106
- })(require('less/tree'));
135
+ })(require('../tree'));
@@ -29,4 +29,4 @@ tree.operate = function (op, a, b) {
29
29
  }
30
30
  };
31
31
 
32
- })(require('less/tree'));
32
+ })(require('../tree'));