less 1.7.0 → 1.7.5

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 (109) hide show
  1. package/.grunt/grunt-contrib-jasmine/es5-shim.js +1360 -0
  2. package/.idea/jsLibraryMappings.xml +0 -1
  3. package/.idea/workspace.xml +182 -183
  4. package/.travis.yml +0 -1
  5. package/CHANGELOG.md +60 -2
  6. package/CONTRIBUTING.md +1 -1
  7. package/Gruntfile.js +12 -24
  8. package/LICENSE +0 -2
  9. package/README.md +5 -3
  10. package/benchmark/less-benchmark.js +1 -2
  11. package/bin/lessc +13 -11
  12. package/bower.json +2 -2
  13. package/build/rhino-modules.js +1 -1
  14. package/dist/less-1.7.0.js +59 -59
  15. package/dist/less-1.7.0.min.js +12 -12
  16. package/dist/less-1.7.1.js +7936 -0
  17. package/dist/less-1.7.1.min.js +16 -0
  18. package/dist/less-1.7.2.js +7938 -0
  19. package/dist/less-1.7.2.min.js +16 -0
  20. package/dist/less-1.7.3.js +7942 -0
  21. package/dist/less-1.7.3.min.js +16 -0
  22. package/dist/less-1.7.4.js +7979 -0
  23. package/dist/less-1.7.4.min.js +16 -0
  24. package/dist/less-1.7.5.js +8008 -0
  25. package/dist/less-1.7.5.min.js +16 -0
  26. package/dist/less-rhino-1.7.0.js +48 -48
  27. package/dist/less-rhino-1.7.1.js +9311 -0
  28. package/dist/less-rhino-1.7.2.js +9313 -0
  29. package/dist/less-rhino-1.7.3.js +9317 -0
  30. package/dist/less-rhino-1.7.4.js +9354 -0
  31. package/dist/less-rhino-1.7.5.js +9383 -0
  32. package/dist/lessc-rhino-1.7.0.js +2 -2
  33. package/dist/lessc-rhino-1.7.1.js +449 -0
  34. package/dist/lessc-rhino-1.7.2.js +449 -0
  35. package/dist/lessc-rhino-1.7.3.js +449 -0
  36. package/dist/lessc-rhino-1.7.4.js +449 -0
  37. package/dist/lessc-rhino-1.7.5.js +449 -0
  38. package/lib/less/browser.js +14 -9
  39. package/lib/less/env.js +2 -0
  40. package/lib/less/fs.js +10 -0
  41. package/lib/less/functions.js +41 -38
  42. package/lib/less/import-visitor.js +9 -7
  43. package/lib/less/index.js +30 -18
  44. package/lib/less/lessc_helper.js +1 -0
  45. package/lib/less/parser/parser.js.orig +1747 -0
  46. package/lib/less/parser.js +81 -40
  47. package/lib/less/rhino.js +3 -3
  48. package/lib/less/source-map-output.js +1 -1
  49. package/lib/less/to-css-visitor.js +3 -0
  50. package/lib/less/tree/anonymous.js +7 -3
  51. package/lib/less/tree/directive.js +6 -0
  52. package/lib/less/tree/element.js +6 -22
  53. package/lib/less/tree/import.js +1 -1
  54. package/lib/less/tree/mixin.js +4 -3
  55. package/lib/less/tree/quoted.js +12 -4
  56. package/lib/less/tree/rule.js +7 -4
  57. package/lib/less/tree/rule.js.orig +164 -0
  58. package/lib/less/tree/ruleset.js +27 -2
  59. package/lib/less/tree/selector.js +1 -1
  60. package/lib/less/tree.js +2 -2
  61. package/package.json +13 -12
  62. package/test/browser/less.js +7317 -6931
  63. package/test/css/comments.css +8 -0
  64. package/test/css/css-3.css +9 -0
  65. package/test/css/functions.css +8 -8
  66. package/test/css/import-inline.css +4 -1
  67. package/test/css/import.css +6 -0
  68. package/test/css/javascript.css +5 -0
  69. package/test/css/mixins-guards.css +14 -3
  70. package/test/css/mixins.css +3 -0
  71. package/test/css/property-name-interp.css +1 -0
  72. package/test/css/urls.css +6 -0
  73. package/test/index.js +1 -1
  74. package/test/less/comments.less +14 -1
  75. package/test/less/css-3.less +12 -1
  76. package/test/less/errors/color-invalid-hex-code.less +3 -0
  77. package/test/less/errors/color-invalid-hex-code.txt +4 -0
  78. package/test/less/errors/color-invalid-hex-code2.less +3 -0
  79. package/test/less/errors/color-invalid-hex-code2.txt +4 -0
  80. package/test/less/errors/import-malformed.less +1 -0
  81. package/test/less/errors/import-malformed.txt +3 -0
  82. package/test/less/errors/import-no-semi.txt +1 -1
  83. package/test/less/errors/javascript-error.less +1 -1
  84. package/test/less/errors/javascript-error.txt +2 -2
  85. package/test/less/import-inline.less +1 -0
  86. package/test/less/import.less +8 -1
  87. package/test/less/javascript.less +9 -0
  88. package/test/less/mixins-guards.less +17 -3
  89. package/test/less/mixins.less +4 -0
  90. package/test/less/property-name-interp.less +4 -1
  91. package/test/less/property-name-interp.less.orig +59 -0
  92. package/test/less/urls.less +12 -0
  93. package/test/less-test.js +7 -8
  94. package/test/modify-vars.js +17 -0
  95. package/test/sourcemaps/basic.json +1 -1
  96. package/tmp/browser/test-runner-main.html +4 -0
  97. package/tmp/less.js +8295 -0
  98. package/build/README.md +0 -51
  99. package/tmp/browser/test-postProcessor.html +0 -59
  100. package/tmp/browser/test-runner-browser.html +0 -59
  101. package/tmp/browser/test-runner-errors.html +0 -267
  102. package/tmp/browser/test-runner-global-vars.html +0 -59
  103. package/tmp/browser/test-runner-legacy.html +0 -59
  104. package/tmp/browser/test-runner-modify-vars.html +0 -59
  105. package/tmp/browser/test-runner-no-js-errors.html +0 -59
  106. package/tmp/browser/test-runner-production.html +0 -55
  107. package/tmp/browser/test-runner-relative-urls.html +0 -59
  108. package/tmp/browser/test-runner-rootpath-relative.html +0 -59
  109. package/tmp/browser/test-runner-rootpath.html +0 -59
@@ -225,7 +225,7 @@ less.Parser = function Parser(env) {
225
225
  return oldi !== i || oldj !== j;
226
226
  }
227
227
 
228
- function expect(arg, msg) {
228
+ function expect(arg, msg, index) {
229
229
  // some older browsers return typeof 'function' for RegExp
230
230
  var result = (Object.prototype.toString.call(arg) === '[object Function]') ? arg.call(parsers) : $(arg);
231
231
  if (result) {
@@ -405,8 +405,8 @@ less.Parser = function Parser(env) {
405
405
 
406
406
  switch (cc) {
407
407
  case 40: // (
408
- parenLevel++;
409
- lastOpeningParen = parserCurrentIndex;
408
+ parenLevel++;
409
+ lastOpeningParen = parserCurrentIndex;
410
410
  continue;
411
411
  case 41: // )
412
412
  if (--parenLevel < 0) {
@@ -417,8 +417,8 @@ less.Parser = function Parser(env) {
417
417
  if (!parenLevel) { emitChunk(); }
418
418
  continue;
419
419
  case 123: // {
420
- level++;
421
- lastOpening = parserCurrentIndex;
420
+ level++;
421
+ lastOpening = parserCurrentIndex;
422
422
  continue;
423
423
  case 125: // }
424
424
  if (--level < 0) {
@@ -517,7 +517,7 @@ less.Parser = function Parser(env) {
517
517
  var evaldRoot,
518
518
  css,
519
519
  evalEnv = new tree.evalEnv(options);
520
-
520
+
521
521
  //
522
522
  // Allows setting variables with a hash, so:
523
523
  //
@@ -685,7 +685,7 @@ less.Parser = function Parser(env) {
685
685
  //
686
686
  // Ruleset -> Rule -> Value -> Expression -> Entity
687
687
  //
688
- // Here's some LESS code:
688
+ // Here's some Less code:
689
689
  //
690
690
  // .class {
691
691
  // color: #fff;
@@ -960,6 +960,11 @@ less.Parser = function Parser(env) {
960
960
  var rgb;
961
961
 
962
962
  if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) {
963
+ var colorCandidateString = rgb.input.match(/^#([\w]+).*/); // strip colons, brackets, whitespaces and other characters that should not definitely be part of color string
964
+ colorCandidateString = colorCandidateString[1];
965
+ if (!colorCandidateString.match(/^[A-Fa-f0-9]+$/)) { // verify if candidate consists only of allowed HEX characters
966
+ error("Invalid HEX color code");
967
+ }
963
968
  return new(tree.Color)(rgb[1]);
964
969
  }
965
970
  },
@@ -1038,8 +1043,8 @@ less.Parser = function Parser(env) {
1038
1043
  rulesetCall: function () {
1039
1044
  var name;
1040
1045
 
1041
- if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) {
1042
- return new tree.RulesetCall(name[1]);
1046
+ if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*\(\s*\)\s*;/))) {
1047
+ return new tree.RulesetCall(name[1]);
1043
1048
  }
1044
1049
  },
1045
1050
 
@@ -1061,12 +1066,13 @@ less.Parser = function Parser(env) {
1061
1066
  }
1062
1067
 
1063
1068
  option = option && option[1];
1064
-
1069
+ if (!elements)
1070
+ error("Missing target selector for :extend().");
1065
1071
  extend = new(tree.Extend)(new(tree.Selector)(elements), option, index);
1066
1072
  if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; }
1067
1073
 
1068
1074
  } while($char(","));
1069
-
1075
+
1070
1076
  expect(/^\)/);
1071
1077
 
1072
1078
  if (isRule) {
@@ -1082,7 +1088,7 @@ less.Parser = function Parser(env) {
1082
1088
  extendRule: function() {
1083
1089
  return this.extend(true);
1084
1090
  },
1085
-
1091
+
1086
1092
  //
1087
1093
  // Mixins
1088
1094
  //
@@ -1288,7 +1294,7 @@ less.Parser = function Parser(env) {
1288
1294
  variadic = argInfo.variadic;
1289
1295
 
1290
1296
  // .mixincall("@{a}");
1291
- // looks a bit like a mixin definition..
1297
+ // looks a bit like a mixin definition..
1292
1298
  // also
1293
1299
  // .mixincall(@a: {rule: set;});
1294
1300
  // so we have to be nice and restore
@@ -1297,7 +1303,7 @@ less.Parser = function Parser(env) {
1297
1303
  restore();
1298
1304
  return;
1299
1305
  }
1300
-
1306
+
1301
1307
  parsers.comments();
1302
1308
 
1303
1309
  if ($re(/^when/)) { // Guard
@@ -1404,10 +1410,20 @@ less.Parser = function Parser(env) {
1404
1410
  //
1405
1411
  combinator: function () {
1406
1412
  var c = input.charAt(i);
1407
-
1413
+
1414
+ if (c === '/') {
1415
+ save();
1416
+ var slashedCombinator = $re(/^\/[a-z]+\//i);
1417
+ if (slashedCombinator) {
1418
+ forget();
1419
+ return new(tree.Combinator)(slashedCombinator);
1420
+ }
1421
+ restore();
1422
+ }
1423
+
1408
1424
  if (c === '>' || c === '+' || c === '~' || c === '|' || c === '^') {
1409
1425
  i++;
1410
- if (input.charAt(i) === '^') {
1426
+ if (c === '^' && input.charAt(i) === '^') {
1411
1427
  c = '^^';
1412
1428
  i++;
1413
1429
  }
@@ -1497,7 +1513,7 @@ less.Parser = function Parser(env) {
1497
1513
  }
1498
1514
  return block;
1499
1515
  },
1500
-
1516
+
1501
1517
  detachedRuleset: function() {
1502
1518
  var blockRuleset = this.blockRuleset();
1503
1519
  if (blockRuleset) {
@@ -1510,7 +1526,7 @@ less.Parser = function Parser(env) {
1510
1526
  //
1511
1527
  ruleset: function () {
1512
1528
  var selectors, s, rules, debugInfo;
1513
-
1529
+
1514
1530
  save();
1515
1531
 
1516
1532
  if (env.dumpLineNumbers) {
@@ -1557,20 +1573,21 @@ less.Parser = function Parser(env) {
1557
1573
  name = this.variable() || this.ruleProperty();
1558
1574
  if (name) {
1559
1575
  isVariable = typeof name === "string";
1560
-
1576
+
1561
1577
  if (isVariable) {
1562
1578
  value = this.detachedRuleset();
1563
1579
  }
1564
-
1580
+
1581
+ this.comments();
1565
1582
  if (!value) {
1566
1583
  // prefer to try to parse first if its a variable or we are compressing
1567
1584
  // but always fallback on the other one
1568
1585
  value = !tryAnonymous && (env.compress || isVariable) ?
1569
1586
  (this.value() || this.anonymousValue()) :
1570
1587
  (this.anonymousValue() || this.value());
1571
-
1588
+
1572
1589
  important = this.important();
1573
-
1590
+
1574
1591
  // a name returned by this.ruleProperty() is always an array of the form:
1575
1592
  // [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"]
1576
1593
  // where each item is a tree.Keyword or tree.Variable
@@ -1605,7 +1622,7 @@ less.Parser = function Parser(env) {
1605
1622
  //
1606
1623
  // @import "lib";
1607
1624
  //
1608
- // Depending on our environemnt, importing is done differently:
1625
+ // Depending on our environment, importing is done differently:
1609
1626
  // In the browser, it's an XHR request, in Node, it would be a
1610
1627
  // file-system operation. The function used for importing is
1611
1628
  // stored in `import`, which we pass to the Import constructor.
@@ -1613,22 +1630,27 @@ less.Parser = function Parser(env) {
1613
1630
  "import": function () {
1614
1631
  var path, features, index = i;
1615
1632
 
1616
- save();
1617
-
1618
1633
  var dir = $re(/^@import?\s+/);
1619
1634
 
1620
- var options = (dir ? this.importOptions() : null) || {};
1635
+ if (dir) {
1636
+ var options = (dir ? this.importOptions() : null) || {};
1621
1637
 
1622
- if (dir && (path = this.entities.quoted() || this.entities.url())) {
1623
- features = this.mediaFeatures();
1624
- if ($char(';')) {
1625
- forget();
1638
+ if ((path = this.entities.quoted() || this.entities.url())) {
1639
+ features = this.mediaFeatures();
1640
+
1641
+ if (!$(';')) {
1642
+ i = index;
1643
+ error("missing semi-colon or unrecognised media features on import");
1644
+ }
1626
1645
  features = features && new(tree.Value)(features);
1627
1646
  return new(tree.Import)(path, features, options, index, env.currentFileInfo);
1628
1647
  }
1648
+ else
1649
+ {
1650
+ i = index;
1651
+ error("malformed import statement");
1652
+ }
1629
1653
  }
1630
-
1631
- restore();
1632
1654
  },
1633
1655
 
1634
1656
  importOptions: function() {
@@ -1751,7 +1773,7 @@ less.Parser = function Parser(env) {
1751
1773
  save();
1752
1774
 
1753
1775
  name = $re(/^@[a-z-]+/);
1754
-
1776
+
1755
1777
  if (!name) { return; }
1756
1778
 
1757
1779
  nonVendorSpecificName = name;
@@ -1801,6 +1823,8 @@ less.Parser = function Parser(env) {
1801
1823
  break;
1802
1824
  }
1803
1825
 
1826
+ this.comments();
1827
+
1804
1828
  if (hasIdentifier) {
1805
1829
  value = this.entity();
1806
1830
  if (!value) {
@@ -1818,13 +1842,15 @@ less.Parser = function Parser(env) {
1818
1842
  }
1819
1843
  }
1820
1844
 
1845
+ this.comments();
1846
+
1821
1847
  if (hasBlock) {
1822
1848
  rules = this.blockRuleset();
1823
1849
  }
1824
1850
 
1825
1851
  if (rules || (!hasBlock && value && $char(';'))) {
1826
1852
  forget();
1827
- return new(tree.Directive)(name, value, rules, index, env.currentFileInfo,
1853
+ return new(tree.Directive)(name, value, rules, index, env.currentFileInfo,
1828
1854
  env.dumpLineNumbers ? getDebugInfo(index, input, env) : null);
1829
1855
  }
1830
1856
 
@@ -1881,13 +1907,17 @@ less.Parser = function Parser(env) {
1881
1907
  if (peek(/^\/[*\/]/)) {
1882
1908
  break;
1883
1909
  }
1910
+
1911
+ save();
1912
+
1884
1913
  op = $char('/') || $char('*');
1885
1914
 
1886
- if (!op) { break; }
1915
+ if (!op) { forget(); break; }
1887
1916
 
1888
1917
  a = this.operand();
1889
1918
 
1890
- if (!a) { break; }
1919
+ if (!a) { restore(); break; }
1920
+ forget();
1891
1921
 
1892
1922
  m.parensInOp = true;
1893
1923
  a.parensInOp = true;
@@ -1911,7 +1941,7 @@ less.Parser = function Parser(env) {
1911
1941
  if (!a) {
1912
1942
  break;
1913
1943
  }
1914
-
1944
+
1915
1945
  m.parensInOp = true;
1916
1946
  a.parensInOp = true;
1917
1947
  operation = new(tree.Operation)(op, [operation || m, a], isSpaced);
@@ -2018,7 +2048,7 @@ less.Parser = function Parser(env) {
2018
2048
  },
2019
2049
  ruleProperty: function () {
2020
2050
  var c = current, name = [], index = [], length = 0, s, k;
2021
-
2051
+
2022
2052
  function match(re) {
2023
2053
  var a = re.exec(c);
2024
2054
  if (a) {
@@ -2028,11 +2058,22 @@ less.Parser = function Parser(env) {
2028
2058
  return name.push(a[1]);
2029
2059
  }
2030
2060
  }
2061
+ function cutOutBlockComments() {
2062
+ //match block comments
2063
+ var a = /^\s*\/\*(?:[^*]|\*+[^\/*])*\*+\//.exec(c);
2064
+ if (a) {
2065
+ length += a[0].length;
2066
+ c = c.slice(a[0].length);
2067
+ return true;
2068
+ }
2069
+ return false;
2070
+ }
2031
2071
 
2032
2072
  match(/^(\*?)/);
2033
2073
  while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // !
2074
+ while (cutOutBlockComments());
2034
2075
  if ((name.length > 1) && match(/^\s*((?:\+_|\+)?)\s*:/)) {
2035
- // at last, we have the complete match now. move forward,
2076
+ // at last, we have the complete match now. move forward,
2036
2077
  // convert name particles to tree objects and return:
2037
2078
  skipWhitespace(length);
2038
2079
  if (name[0] === '') {
@@ -2043,7 +2084,7 @@ less.Parser = function Parser(env) {
2043
2084
  s = name[k];
2044
2085
  name[k] = (s.charAt(0) !== '@')
2045
2086
  ? new(tree.Keyword)(s)
2046
- : new(tree.Variable)('@' + s.slice(2, -1),
2087
+ : new(tree.Variable)('@' + s.slice(2, -1),
2047
2088
  index[k], env.currentFileInfo);
2048
2089
  }
2049
2090
  return name;
package/lib/less/rhino.js CHANGED
@@ -56,7 +56,8 @@ function formatError(ctx, options) {
56
56
  function writeError(ctx, options) {
57
57
  options = options || {};
58
58
  if (options.silent) { return; }
59
- print(formatError(ctx, options));
59
+ var message = formatError(ctx, options);
60
+ throw new Error(message);
60
61
  }
61
62
 
62
63
  function loadStyleSheet(sheet, callback, reload, remaining) {
@@ -203,7 +204,7 @@ function writeFile(filename, content) {
203
204
  switch (arg) {
204
205
  case 'v':
205
206
  case 'version':
206
- console.log("lessc " + less.version.join('.') + " (LESS Compiler) [JavaScript]");
207
+ console.log("lessc " + less.version.join('.') + " (Less Compiler) [JavaScript]");
207
208
  continueProcessing = false;
208
209
  break;
209
210
  case 'verbose':
@@ -444,5 +445,4 @@ function writeFile(filename, content) {
444
445
  writeError(e, options);
445
446
  quit(1);
446
447
  }
447
- console.log("done");
448
448
  }(arguments));
@@ -127,7 +127,7 @@
127
127
  if (this._writeSourceMap) {
128
128
  this._writeSourceMap(sourceMapContent);
129
129
  } else {
130
- sourceMapURL = "data:application/json," + encodeURIComponent(sourceMapContent);
130
+ sourceMapURL = "data:application/json;base64," + require('./encoder.js').encodeBase64(sourceMapContent);
131
131
  }
132
132
 
133
133
  if (sourceMapURL) {
@@ -63,6 +63,9 @@
63
63
  }
64
64
  this.charset = true;
65
65
  }
66
+ if (directiveNode.rules && directiveNode.rules.rules) {
67
+ this._mergeRules(directiveNode.rules.rules);
68
+ }
66
69
  return directiveNode;
67
70
  },
68
71
 
@@ -1,15 +1,16 @@
1
1
  (function (tree) {
2
2
 
3
- tree.Anonymous = function (string, index, currentFileInfo, mapLines) {
4
- this.value = string.value || string;
3
+ tree.Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike) {
4
+ this.value = value;
5
5
  this.index = index;
6
6
  this.mapLines = mapLines;
7
7
  this.currentFileInfo = currentFileInfo;
8
+ this.rulesetLike = (typeof rulesetLike === 'undefined')? false : rulesetLike;
8
9
  };
9
10
  tree.Anonymous.prototype = {
10
11
  type: "Anonymous",
11
12
  eval: function () {
12
- return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines);
13
+ return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike);
13
14
  },
14
15
  compare: function (x) {
15
16
  if (!x.toCSS) {
@@ -25,6 +26,9 @@ tree.Anonymous.prototype = {
25
26
 
26
27
  return left < right ? -1 : 1;
27
28
  },
29
+ isRulesetLike: function() {
30
+ return this.rulesetLike;
31
+ },
28
32
  genCSS: function (env, output) {
29
33
  output.add(this.value, this.currentFileInfo, this.index, this.mapLines);
30
34
  },
@@ -23,6 +23,12 @@ tree.Directive.prototype = {
23
23
  value = visitor.visit(value);
24
24
  }
25
25
  },
26
+ isRulesetLike: function() {
27
+ return !this.isCharset();
28
+ },
29
+ isCharset: function() {
30
+ return "@charset" === this.name;
31
+ },
26
32
  genCSS: function (env, output) {
27
33
  var value = this.value, rules = this.rules;
28
34
  output.add(this.name, this.currentFileInfo, this.index);
@@ -77,30 +77,14 @@ tree.Combinator = function (value) {
77
77
  };
78
78
  tree.Combinator.prototype = {
79
79
  type: "Combinator",
80
- _outputMap: {
81
- '' : '',
82
- ' ' : ' ',
83
- ':' : ' :',
84
- '+' : ' + ',
85
- '~' : ' ~ ',
86
- '>' : ' > ',
87
- '|' : '|',
88
- '^' : ' ^ ',
89
- '^^' : ' ^^ '
90
- },
91
- _outputMapCompressed: {
92
- '' : '',
93
- ' ' : ' ',
94
- ':' : ' :',
95
- '+' : '+',
96
- '~' : '~',
97
- '>' : '>',
98
- '|' : '|',
99
- '^' : '^',
100
- '^^' : '^^'
80
+ _noSpaceCombinators: {
81
+ '': true,
82
+ ' ': true,
83
+ '|': true
101
84
  },
102
85
  genCSS: function (env, output) {
103
- output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]);
86
+ var spaceOrEmpty = (env.compress || this._noSpaceCombinators[this.value]) ? '' : ' ';
87
+ output.add(spaceOrEmpty + this.value + spaceOrEmpty);
104
88
  },
105
89
  toCSS: tree.toCSS
106
90
  };
@@ -103,7 +103,7 @@ tree.Import.prototype = {
103
103
 
104
104
  if (this.options.inline) {
105
105
  //todo needs to reference css file not import
106
- var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true);
106
+ var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true, true);
107
107
  return this.features ? new(tree.Media)([contents], this.features.value) : [contents];
108
108
  } else if (this.css) {
109
109
  var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index);
@@ -21,7 +21,7 @@ tree.mixin.Call.prototype = {
21
21
  eval: function (env) {
22
22
  var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule,
23
23
  candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc,
24
- defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count;
24
+ defaultResult, defNone = 0, defTrue = 1, defFalse = 2, count, originalRuleset;
25
25
 
26
26
  args = this.arguments && this.arguments.map(function (a) {
27
27
  return { name: a.name, value: a.value.eval(env) };
@@ -99,8 +99,9 @@ tree.mixin.Call.prototype = {
99
99
  try {
100
100
  mixin = candidates[m].mixin;
101
101
  if (!(mixin instanceof tree.mixin.Definition)) {
102
+ originalRuleset = mixin.originalRuleset || mixin;
102
103
  mixin = new tree.mixin.Definition("", [], mixin.rules, null, false);
103
- mixin.originalRuleset = mixins[m].originalRuleset || mixins[m];
104
+ mixin.originalRuleset = originalRuleset;
104
105
  }
105
106
  Array.prototype.push.apply(
106
107
  rules, mixin.evalCall(env, args, this.important).rules);
@@ -282,7 +283,7 @@ tree.mixin.Definition.prototype = {
282
283
  matchCondition: function (args, env) {
283
284
  if (this.condition && !this.condition.eval(
284
285
  new(tree.evalEnv)(env,
285
- [this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] // the parameter variables
286
+ [this.evalParams(env, new(tree.evalEnv)(env, this.frames ? this.frames.concat(env.frames) : env.frames), args, [])] // the parameter variables
286
287
  .concat(this.frames) // the parent namespace/mixin frames
287
288
  .concat(env.frames)))) { // the current environment frames
288
289
  return false;
@@ -33,14 +33,22 @@ tree.Quoted.prototype = {
33
33
  if (!x.toCSS) {
34
34
  return -1;
35
35
  }
36
-
37
- var left = this.toCSS(),
36
+
37
+ var left, right;
38
+
39
+ // when comparing quoted strings allow the quote to differ
40
+ if (x.type === "Quoted" && !this.escaped && !x.escaped) {
41
+ left = x.value;
42
+ right = this.value;
43
+ } else {
44
+ left = this.toCSS();
38
45
  right = x.toCSS();
39
-
46
+ }
47
+
40
48
  if (left === right) {
41
49
  return 0;
42
50
  }
43
-
51
+
44
52
  return left < right ? -1 : 1;
45
53
  }
46
54
  };
@@ -1,6 +1,6 @@
1
1
  (function (tree) {
2
2
 
3
- tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) {
3
+ tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline, variable) {
4
4
  this.name = name;
5
5
  this.value = (value instanceof tree.Value || value instanceof tree.Ruleset) ? value : new(tree.Value)([value]);
6
6
  this.important = important ? ' ' + important.trim() : '';
@@ -8,7 +8,8 @@ tree.Rule = function (name, value, important, merge, index, currentFileInfo, inl
8
8
  this.index = index;
9
9
  this.currentFileInfo = currentFileInfo;
10
10
  this.inline = inline || false;
11
- this.variable = name.charAt && (name.charAt(0) === '@');
11
+ this.variable = (variable !== undefined) ? variable
12
+ : (name.charAt && (name.charAt(0) === '@'));
12
13
  };
13
14
 
14
15
  tree.Rule.prototype = {
@@ -30,13 +31,14 @@ tree.Rule.prototype = {
30
31
  },
31
32
  toCSS: tree.toCSS,
32
33
  eval: function (env) {
33
- var strictMathBypass = false, name = this.name, evaldValue;
34
+ var strictMathBypass = false, name = this.name, variable = this.variable, evaldValue;
34
35
  if (typeof name !== "string") {
35
36
  // expand 'primitive' name directly to get
36
37
  // things faster (~10% for benchmark.less):
37
38
  name = (name.length === 1)
38
39
  && (name[0] instanceof tree.Keyword)
39
40
  ? name[0].value : evalName(env, name);
41
+ variable = false; // never treat expanded interpolation as new variable name
40
42
  }
41
43
  if (name === "font" && !env.strictMath) {
42
44
  strictMathBypass = true;
@@ -54,7 +56,8 @@ tree.Rule.prototype = {
54
56
  evaldValue,
55
57
  this.important,
56
58
  this.merge,
57
- this.index, this.currentFileInfo, this.inline);
59
+ this.index, this.currentFileInfo, this.inline,
60
+ variable);
58
61
  }
59
62
  catch(e) {
60
63
  if (typeof e.index !== 'number') {