less 2.1.2 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/.idea/inspectionProfiles/Project_Default.xml +1 -0
  2. package/.idea/jsLinters/jscs.xml +4 -0
  3. package/.idea/jsLinters/jshint.xml +58 -4
  4. package/.idea/workspace.xml +499 -760
  5. package/.jscsrc +73 -0
  6. package/.jshintrc +0 -2
  7. package/.travis.yml +1 -0
  8. package/CHANGELOG.md +59 -0
  9. package/Gruntfile.js +37 -3
  10. package/appveyor.yml +5 -0
  11. package/bin/lessc +383 -316
  12. package/bower.json +1 -1
  13. package/dist/less.js +1638 -1244
  14. package/dist/less.min.js +7 -7
  15. package/lib/less/contexts.js +3 -3
  16. package/lib/less/data/unit-conversions.js +3 -3
  17. package/lib/less/environment/abstract-file-manager.js +8 -8
  18. package/lib/less/environment/environment.js +2 -2
  19. package/lib/less/functions/color-blending.js +3 -3
  20. package/lib/less/functions/color.js +20 -12
  21. package/lib/less/functions/data-uri.js +26 -18
  22. package/lib/less/functions/math.js +1 -1
  23. package/lib/less/functions/number.js +2 -2
  24. package/lib/less/functions/string.js +3 -1
  25. package/lib/less/functions/svg.js +12 -15
  26. package/lib/less/functions/types.js +33 -10
  27. package/lib/less/import-manager.js +14 -9
  28. package/lib/less/index.js +2 -1
  29. package/lib/less/less-error.js +1 -1
  30. package/lib/less/logger.js +2 -2
  31. package/lib/less/parse-tree.js +48 -42
  32. package/lib/less/parse.js +68 -0
  33. package/lib/less/parser/parser-input.js +6 -5
  34. package/lib/less/parser/parser.js +76 -29
  35. package/lib/less/plugin-manager.js +30 -3
  36. package/lib/less/render.js +11 -37
  37. package/lib/less/source-map-builder.js +4 -0
  38. package/lib/less/source-map-output.js +6 -6
  39. package/lib/less/transform-tree.js +4 -4
  40. package/lib/less/tree/anonymous.js +1 -1
  41. package/lib/less/tree/assignment.js +0 -1
  42. package/lib/less/tree/call.js +1 -1
  43. package/lib/less/tree/color.js +6 -6
  44. package/lib/less/tree/condition.js +9 -5
  45. package/lib/less/tree/debug-info.js +6 -2
  46. package/lib/less/tree/dimension.js +5 -5
  47. package/lib/less/tree/directive.js +25 -6
  48. package/lib/less/tree/element.js +1 -1
  49. package/lib/less/tree/expression.js +2 -2
  50. package/lib/less/tree/extend.js +3 -3
  51. package/lib/less/tree/import.js +2 -2
  52. package/lib/less/tree/index.js +1 -0
  53. package/lib/less/tree/javascript.js +2 -2
  54. package/lib/less/tree/media.js +20 -19
  55. package/lib/less/tree/mixin-call.js +5 -6
  56. package/lib/less/tree/mixin-definition.js +33 -11
  57. package/lib/less/tree/quoted.js +3 -3
  58. package/lib/less/tree/rule.js +3 -4
  59. package/lib/less/tree/ruleset.js +290 -177
  60. package/lib/less/tree/selector.js +3 -2
  61. package/lib/less/tree/unit.js +2 -0
  62. package/lib/less/tree/url.js +5 -2
  63. package/lib/less/tree/value.js +2 -2
  64. package/lib/less/tree/variable.js +1 -1
  65. package/lib/less/visitors/extend-visitor.js +72 -36
  66. package/lib/less/visitors/import-sequencer.js +20 -14
  67. package/lib/less/visitors/import-visitor.js +15 -12
  68. package/lib/less/visitors/to-css-visitor.js +41 -9
  69. package/lib/less-browser/add-default-options.js +5 -1
  70. package/lib/less-browser/bootstrap.js +10 -8
  71. package/lib/less-browser/cache.js +3 -3
  72. package/lib/less-browser/file-manager.js +96 -96
  73. package/lib/less-browser/index.js +206 -205
  74. package/lib/less-browser/log-listener.js +2 -2
  75. package/lib/less-browser/utils.js +6 -6
  76. package/lib/less-node/file-manager.js +58 -30
  77. package/lib/less-node/image-size.js +34 -0
  78. package/lib/less-node/index.js +6 -3
  79. package/lib/less-node/lessc-helper.js +3 -2
  80. package/lib/less-node/plugin-loader.js +3 -3
  81. package/lib/less-node/url-file-manager.js +6 -3
  82. package/lib/less-rhino/index.js +15 -13
  83. package/lib/source-map/source-map-0.1.31.js +1 -1
  84. package/package.json +10 -7
  85. package/test/browser/common.js +13 -11
  86. package/test/browser/css/relative-urls/urls.css +1 -0
  87. package/test/browser/css/urls.css +6 -3
  88. package/test/browser/jasmine-jsreporter.js +2 -3
  89. package/test/browser/less/nested-gradient-with-svg-gradient/mixin-consumer.less +5 -0
  90. package/test/browser/less/nested-gradient-with-svg-gradient/svg-gradient-mixin.less +15 -0
  91. package/test/browser/less/relative-urls/urls.less +1 -0
  92. package/test/browser/less/urls.less +1 -0
  93. package/test/browser/runner-browser-options.js +23 -23
  94. package/test/browser/runner-errors-options.js +0 -1
  95. package/test/browser/runner-errors-spec.js +1 -2
  96. package/test/browser/runner-global-vars-spec.js +1 -1
  97. package/test/browser/runner-legacy-options.js +0 -1
  98. package/test/browser/runner-main-options.js +5 -5
  99. package/test/browser/runner-main-spec.js +1 -1
  100. package/test/browser/runner-no-js-errors-spec.js +0 -1
  101. package/test/browser/runner-production-options.js +0 -1
  102. package/test/browser/runner-relative-urls-options.js +0 -1
  103. package/test/browser/runner-rootpath-options.js +0 -1
  104. package/test/browser/runner-rootpath-relative-options.js +0 -1
  105. package/test/browser/runner-strict-units-options.js +0 -1
  106. package/test/copy-bom.js +72 -0
  107. package/test/css/comments.css +1 -0
  108. package/test/css/css-3.css +2 -0
  109. package/test/css/functions.css +10 -0
  110. package/test/css/import-interpolation.css +7 -0
  111. package/test/css/import-reference.css +25 -0
  112. package/test/css/include-path/include-path.css +6 -0
  113. package/test/css/mixins-important.css +12 -0
  114. package/test/css/selectors.css +15 -1
  115. package/test/css/static-urls/urls.css +1 -0
  116. package/test/css/url-args/urls.css +3 -3
  117. package/test/css/urls.css +13 -4
  118. package/test/css/variables.css +7 -1
  119. package/test/data/image.svg +3 -0
  120. package/test/index.js +15 -10
  121. package/test/less/comments.less +1 -0
  122. package/test/less/css-3.less +3 -0
  123. package/test/less/css-guards.less +2 -1
  124. package/test/less/errors/detached-ruleset-2.txt +1 -1
  125. package/test/less/errors/import-missing.txt +1 -1
  126. package/test/less/errors/parens-error-1.txt +1 -1
  127. package/test/less/errors/parens-error-2.txt +1 -1
  128. package/test/less/errors/parens-error-3.txt +1 -1
  129. package/test/less/errors/single-character.less +1 -0
  130. package/test/less/errors/single-character.txt +2 -0
  131. package/test/less/functions.less +18 -0
  132. package/test/less/import/import-interpolation.less +1 -0
  133. package/test/less/import/import-reference.less +38 -0
  134. package/test/less/import/imports/logo.less +1 -0
  135. package/test/less/import-reference.less +3 -1
  136. package/test/less/import.less +2 -0
  137. package/test/less/include-path/include-path.less +6 -0
  138. package/test/less/mixins-important.less +28 -0
  139. package/test/less/nested-gradient-with-svg-gradient/mixin-consumer.less +5 -0
  140. package/test/less/nested-gradient-with-svg-gradient/svg-gradient-mixin.less +15 -0
  141. package/test/less/selectors.less +29 -4
  142. package/test/less/sourcemaps-empty/empty.less +0 -0
  143. package/test/less/sourcemaps-empty/var-defs.less +1 -0
  144. package/test/less/urls.less +10 -1
  145. package/test/less/variables.less +17 -3
  146. package/test/less-test.js +133 -52
  147. package/test/modify-vars.js +14 -12
  148. package/test/browser/less.js +0 -9473
@@ -4,7 +4,7 @@ module.exports = contexts;
4
4
  var copyFromOriginal = function copyFromOriginal(original, destination, propertiesToCopy) {
5
5
  if (!original) { return; }
6
6
 
7
- for(var i = 0; i < propertiesToCopy.length; i++) {
7
+ for (var i = 0; i < propertiesToCopy.length; i++) {
8
8
  if (original.hasOwnProperty(propertiesToCopy[i])) {
9
9
  destination[propertiesToCopy[i]] = original[propertiesToCopy[i]];
10
10
  }
@@ -41,6 +41,7 @@ contexts.Parse = function(options) {
41
41
  };
42
42
 
43
43
  var evalCopyProperties = [
44
+ 'paths', // additional include paths
44
45
  'compress', // whether to compress
45
46
  'ieCompat', // whether to enforce IE compatibility (IE8 data-uri)
46
47
  'strictMath', // whether math has to be within parenthesis
@@ -76,7 +77,7 @@ contexts.Eval.prototype.isMathOn = function () {
76
77
  };
77
78
 
78
79
  contexts.Eval.prototype.isPathRelative = function (path) {
79
- return !/^(?:[a-z-]+:|\/)/i.test(path);
80
+ return !/^(?:[a-z-]+:|\/|#)/i.test(path);
80
81
  };
81
82
 
82
83
  contexts.Eval.prototype.normalizePath = function( path ) {
@@ -107,4 +108,3 @@ contexts.Eval.prototype.normalizePath = function( path ) {
107
108
  };
108
109
 
109
110
  //todo - do the same for the toCSS ?
110
-
@@ -13,9 +13,9 @@ module.exports = {
13
13
  'ms': 0.001
14
14
  },
15
15
  angle: {
16
- 'rad': 1/(2*Math.PI),
17
- 'deg': 1/360,
18
- 'grad': 1/400,
16
+ 'rad': 1 / (2 * Math.PI),
17
+ 'deg': 1 / 360,
18
+ 'grad': 1 / 400,
19
19
  'turn': 1
20
20
  }
21
21
  };
@@ -29,7 +29,7 @@ abstractFileManager.prototype.alwaysMakePathsAbsolute = function() {
29
29
  };
30
30
 
31
31
  abstractFileManager.prototype.isPathAbsolute = function(filename) {
32
- return (/^(?:[a-z-]+:|\/|\\)/i).test(filename);
32
+ return (/^(?:[a-z-]+:|\/|\\|#)/i).test(filename);
33
33
  };
34
34
 
35
35
  abstractFileManager.prototype.join = function(basePath, laterPath) {
@@ -48,15 +48,15 @@ abstractFileManager.prototype.pathDiff = function pathDiff(url, baseUrl) {
48
48
  return "";
49
49
  }
50
50
  max = Math.max(baseUrlParts.directories.length, urlParts.directories.length);
51
- for(i = 0; i < max; i++) {
51
+ for (i = 0; i < max; i++) {
52
52
  if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; }
53
53
  }
54
54
  baseUrlDirectories = baseUrlParts.directories.slice(i);
55
55
  urlDirectories = urlParts.directories.slice(i);
56
- for(i = 0; i < baseUrlDirectories.length-1; i++) {
56
+ for (i = 0; i < baseUrlDirectories.length - 1; i++) {
57
57
  diff += "../";
58
58
  }
59
- for(i = 0; i < urlDirectories.length-1; i++) {
59
+ for (i = 0; i < urlDirectories.length - 1; i++) {
60
60
  diff += urlDirectories[i] + "/";
61
61
  }
62
62
  return diff;
@@ -81,7 +81,7 @@ abstractFileManager.prototype.extractUrlParts = function extractUrlParts(url, ba
81
81
  if (baseUrl && (!urlParts[1] || urlParts[2])) {
82
82
  baseUrlParts = baseUrl.match(urlPartsRegex);
83
83
  if (!baseUrlParts) {
84
- throw new Error("Could not parse page url - '"+baseUrl+"'");
84
+ throw new Error("Could not parse page url - '" + baseUrl + "'");
85
85
  }
86
86
  urlParts[1] = urlParts[1] || baseUrlParts[1] || "";
87
87
  if (!urlParts[2]) {
@@ -93,16 +93,16 @@ abstractFileManager.prototype.extractUrlParts = function extractUrlParts(url, ba
93
93
  directories = urlParts[3].replace(/\\/g, "/").split("/");
94
94
 
95
95
  // extract out . before .. so .. doesn't absorb a non-directory
96
- for(i = 0; i < directories.length; i++) {
96
+ for (i = 0; i < directories.length; i++) {
97
97
  if (directories[i] === ".") {
98
98
  directories.splice(i, 1);
99
99
  i -= 1;
100
100
  }
101
101
  }
102
102
 
103
- for(i = 0; i < directories.length; i++) {
103
+ for (i = 0; i < directories.length; i++) {
104
104
  if (directories[i] === ".." && i > 0) {
105
- directories.splice(i-1, 2);
105
+ directories.splice(i - 1, 2);
106
106
  i -= 2;
107
107
  }
108
108
  }
@@ -7,7 +7,7 @@ var environment = function(externalEnvironment, fileManagers) {
7
7
  requiredFunctions = [],
8
8
  functions = requiredFunctions.concat(optionalFunctions);
9
9
 
10
- for(var i = 0; i < functions.length; i++) {
10
+ for (var i = 0; i < functions.length; i++) {
11
11
  var propName = functions[i],
12
12
  environmentFunc = externalEnvironment[propName];
13
13
  if (environmentFunc) {
@@ -31,7 +31,7 @@ environment.prototype.getFileManager = function (filename, currentDirectory, opt
31
31
  if (options.pluginManager) {
32
32
  fileManagers = [].concat(fileManagers).concat(options.pluginManager.getFileManagers());
33
33
  }
34
- for(var i = fileManagers.length - 1; i >= 0 ; i--) {
34
+ for (var i = fileManagers.length - 1; i >= 0 ; i--) {
35
35
  var fileManager = fileManagers[i];
36
36
  if (fileManager[isSync ? "supportsSync" : "supports"](filename, currentDirectory, options, environment)) {
37
37
  return fileManager;
@@ -33,9 +33,9 @@ var colorBlendModeFunctions = {
33
33
  },
34
34
  overlay: function(cb, cs) {
35
35
  cb *= 2;
36
- return (cb <= 1)
37
- ? colorBlendModeFunctions.multiply(cb, cs)
38
- : colorBlendModeFunctions.screen(cb - 1, cs);
36
+ return (cb <= 1) ?
37
+ colorBlendModeFunctions.multiply(cb, cs) :
38
+ colorBlendModeFunctions.screen(cb - 1, cs);
39
39
  },
40
40
  softlight: function(cb, cs) {
41
41
  var d = 1, e = cb;
@@ -14,7 +14,7 @@ function hsla(color) {
14
14
  function number(n) {
15
15
  if (n instanceof Dimension) {
16
16
  return parseFloat(n.unit.is('%') ? n.value / 100 : n.value);
17
- } else if (typeof(n) === 'number') {
17
+ } else if (typeof n === 'number') {
18
18
  return n;
19
19
  } else {
20
20
  throw {
@@ -45,10 +45,18 @@ colorFunctions = {
45
45
  hsla: function (h, s, l, a) {
46
46
  function hue(h) {
47
47
  h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
48
- if (h * 6 < 1) { return m1 + (m2 - m1) * h * 6; }
49
- else if (h * 2 < 1) { return m2; }
50
- else if (h * 3 < 2) { return m1 + (m2 - m1) * (2/3 - h) * 6; }
51
- else { return m1; }
48
+ if (h * 6 < 1) {
49
+ return m1 + (m2 - m1) * h * 6;
50
+ }
51
+ else if (h * 2 < 1) {
52
+ return m2;
53
+ }
54
+ else if (h * 3 < 2) {
55
+ return m1 + (m2 - m1) * (2 / 3 - h) * 6;
56
+ }
57
+ else {
58
+ return m1;
59
+ }
52
60
  }
53
61
 
54
62
  h = (number(h) % 360) / 360;
@@ -57,9 +65,9 @@ colorFunctions = {
57
65
  var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
58
66
  var m1 = l * 2 - m2;
59
67
 
60
- return colorFunctions.rgba(hue(h + 1/3) * 255,
68
+ return colorFunctions.rgba(hue(h + 1 / 3) * 255,
61
69
  hue(h) * 255,
62
- hue(h - 1/3) * 255,
70
+ hue(h - 1 / 3) * 255,
63
71
  a);
64
72
  },
65
73
 
@@ -127,9 +135,9 @@ colorFunctions = {
127
135
  },
128
136
  luminance: function (color) {
129
137
  var luminance =
130
- (0.2126 * color.rgb[0] / 255)
131
- + (0.7152 * color.rgb[1] / 255)
132
- + (0.0722 * color.rgb[2] / 255);
138
+ (0.2126 * color.rgb[0] / 255) +
139
+ (0.7152 * color.rgb[1] / 255) +
140
+ (0.0722 * color.rgb[2] / 255);
133
141
 
134
142
  return new Dimension(luminance * color.alpha * 100, '%');
135
143
  },
@@ -259,7 +267,7 @@ colorFunctions = {
259
267
  return new Color(c.value.slice(1));
260
268
  }
261
269
  if ((c instanceof Color) || (c = Color.fromKeyword(c.value))) {
262
- c.keyword = undefined;
270
+ c.value = undefined;
263
271
  return c;
264
272
  }
265
273
  throw {
@@ -268,7 +276,7 @@ colorFunctions = {
268
276
  };
269
277
  },
270
278
  tint: function(color, amount) {
271
- return colorFunctions.mix(colorFunctions.rgb(255,255,255), color, amount);
279
+ return colorFunctions.mix(colorFunctions.rgb(255, 255, 255), color, amount);
272
280
  },
273
281
  shade: function(color, amount) {
274
282
  return colorFunctions.mix(colorFunctions.rgb(0, 0, 0), color, amount);
@@ -1,5 +1,5 @@
1
1
  module.exports = function(environment) {
2
- var Anonymous = require("../tree/anonymous"),
2
+ var Quoted = require("../tree/quoted"),
3
3
  URL = require("../tree/url"),
4
4
  functionRegistry = require("./function-registry"),
5
5
  fallback = function(functionThis, node) {
@@ -16,12 +16,13 @@ module.exports = function(environment) {
16
16
 
17
17
  var mimetype = mimetypeNode && mimetypeNode.value;
18
18
  var filePath = filePathNode.value;
19
- var currentDirectory = filePathNode.currentFileInfo.relativeUrls ?
20
- filePathNode.currentFileInfo.currentDirectory : filePathNode.currentFileInfo.entryPath;
19
+ var currentFileInfo = this.currentFileInfo;
20
+ var currentDirectory = currentFileInfo.relativeUrls ?
21
+ currentFileInfo.currentDirectory : currentFileInfo.entryPath;
21
22
 
22
23
  var fragmentStart = filePath.indexOf('#');
23
24
  var fragment = '';
24
- if (fragmentStart!==-1) {
25
+ if (fragmentStart !== -1) {
25
26
  fragment = filePath.slice(fragmentStart);
26
27
  filePath = filePath.slice(0, fragmentStart);
27
28
  }
@@ -39,9 +40,13 @@ module.exports = function(environment) {
39
40
 
40
41
  mimetype = environment.mimeLookup(filePath);
41
42
 
42
- // use base 64 unless it's an ASCII or UTF-8 format
43
- var charset = environment.charsetLookup(mimetype);
44
- useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
43
+ if (mimetype === "image/svg+xml") {
44
+ useBase64 = false;
45
+ } else {
46
+ // use base 64 unless it's an ASCII or UTF-8 format
47
+ var charset = environment.charsetLookup(mimetype);
48
+ useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
49
+ }
45
50
  if (useBase64) { mimetype += ';base64'; }
46
51
  }
47
52
  else {
@@ -50,28 +55,31 @@ module.exports = function(environment) {
50
55
 
51
56
  var fileSync = fileManager.loadFileSync(filePath, currentDirectory, this.context, environment);
52
57
  if (!fileSync.contents) {
53
- logger.warn("Skipped data-uri embedding because file not found");
58
+ logger.warn("Skipped data-uri embedding of " + filePath + " because file not found");
54
59
  return fallback(this, filePathNode || mimetypeNode);
55
60
  }
56
61
  var buf = fileSync.contents;
62
+ if (useBase64 && !environment.encodeBase64) {
63
+ return fallback(this, filePathNode);
64
+ }
57
65
 
58
- // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded
66
+ buf = useBase64 ? environment.encodeBase64(buf) : encodeURIComponent(buf);
67
+
68
+ var uri = "data:" + mimetype + ',' + buf + fragment;
69
+
70
+ // IE8 cannot handle a data-uri larger than 32,768 characters. If this is exceeded
59
71
  // and the --ieCompat flag is enabled, return a normal url() instead.
60
- var DATA_URI_MAX_KB = 32,
61
- fileSizeInKB = parseInt((buf.length / 1024), 10);
62
- if (fileSizeInKB >= DATA_URI_MAX_KB) {
72
+ var DATA_URI_MAX = 32768;
73
+ if (uri.length >= DATA_URI_MAX) {
63
74
 
64
75
  if (this.context.ieCompat !== false) {
65
- logger.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB);
76
+ logger.warn("Skipped data-uri embedding of " + filePath + " because its size (" + uri.length +
77
+ " characters) exceeds IE8-safe " + DATA_URI_MAX + " characters!");
66
78
 
67
79
  return fallback(this, filePathNode || mimetypeNode);
68
80
  }
69
81
  }
70
82
 
71
- buf = useBase64 ? buf.toString('base64')
72
- : encodeURIComponent(buf);
73
-
74
- var uri = "\"data:" + mimetype + ',' + buf + fragment + "\"";
75
- return new URL(new Anonymous(uri), this.index, this.currentFileInfo);
83
+ return new URL(new Quoted('"' + uri + '"', uri, false, this.index, this.currentFileInfo), this.index, this.currentFileInfo);
76
84
  });
77
85
  };
@@ -34,7 +34,7 @@ for (var f in mathFunctions) {
34
34
  }
35
35
 
36
36
  mathFunctions.round = function (n, f) {
37
- var fraction = typeof(f) === "undefined" ? 0 : f.value;
37
+ var fraction = typeof f === "undefined" ? 0 : f.value;
38
38
  return _math(function(num) { return num.toFixed(fraction); }, null, n);
39
39
  };
40
40
 
@@ -14,7 +14,7 @@ var minMax = function (isMin, args) {
14
14
  for (i = 0; i < args.length; i++) {
15
15
  current = args[i];
16
16
  if (!(current instanceof Dimension)) {
17
- if(Array.isArray(args[i].value)) {
17
+ if (Array.isArray(args[i].value)) {
18
18
  Array.prototype.push.apply(args, Array.prototype.slice.call(args[i].value));
19
19
  }
20
20
  continue;
@@ -25,7 +25,7 @@ var minMax = function (isMin, args) {
25
25
  unitClone = unit !== "" && unitClone === undefined ? current.unit.toString() : unitClone;
26
26
  j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit];
27
27
  if (j === undefined) {
28
- if(unitStatic !== undefined && unit !== unitStatic) {
28
+ if (unitStatic !== undefined && unit !== unitStatic) {
29
29
  throw{ type: "Argument", message: "incompatible types" };
30
30
  }
31
31
  values[unit] = order.length;
@@ -8,7 +8,9 @@ functionRegistry.addMultiple({
8
8
  return new Anonymous(str instanceof JavaScript ? str.evaluated : str.value);
9
9
  },
10
10
  escape: function (str) {
11
- return new Anonymous(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29"));
11
+ return new Anonymous(
12
+ encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B")
13
+ .replace(/\(/g, "%28").replace(/\)/g, "%29"));
12
14
  },
13
15
  replace: function (string, pattern, replacement, flags) {
14
16
  var result = string.value;
@@ -1,14 +1,17 @@
1
1
  module.exports = function(environment) {
2
2
  var Dimension = require("../tree/dimension"),
3
3
  Color = require("../tree/color"),
4
- Anonymous = require("../tree/anonymous"),
4
+ Expression = require("../tree/expression"),
5
+ Quoted = require("../tree/quoted"),
5
6
  URL = require("../tree/url"),
6
7
  functionRegistry = require("./function-registry");
7
8
 
8
9
  functionRegistry.add("svg-gradient", function(direction) {
9
10
 
10
11
  function throwArgumentDescriptor() {
11
- throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" };
12
+ throw { type: "Argument",
13
+ message: "svg-gradient expects direction, start_color [start_position], [color position,]...," +
14
+ " end_color [end_position]" };
12
15
  }
13
16
 
14
17
  if (arguments.length < 3) {
@@ -18,7 +21,6 @@ module.exports = function(environment) {
18
21
  gradientDirectionSvg,
19
22
  gradientType = "linear",
20
23
  rectangleDimension = 'x="0" y="0" width="1" height="1"',
21
- useBase64 = true,
22
24
  renderEnv = {compress: false},
23
25
  returner,
24
26
  directionValue = direction.toCSS(renderEnv),
@@ -44,14 +46,15 @@ module.exports = function(environment) {
44
46
  rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
45
47
  break;
46
48
  default:
47
- throw { type: "Argument", message: "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'" };
49
+ throw { type: "Argument", message: "svg-gradient direction must be 'to bottom', 'to right'," +
50
+ " 'to bottom right', 'to top right' or 'ellipse at center'" };
48
51
  }
49
52
  returner = '<?xml version="1.0" ?>' +
50
53
  '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' +
51
54
  '<' + gradientType + 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' + gradientDirectionSvg + '>';
52
55
 
53
56
  for (i = 0; i < stops.length; i+= 1) {
54
- if (stops[i].value) {
57
+ if (stops[i] instanceof Expression) {
55
58
  color = stops[i].value[0];
56
59
  position = stops[i].value[1];
57
60
  } else {
@@ -59,7 +62,7 @@ module.exports = function(environment) {
59
62
  position = undefined;
60
63
  }
61
64
 
62
- if (!(color instanceof Color) || (!((i === 0 || i+1 === stops.length) && position === undefined) && !(position instanceof Dimension))) {
65
+ if (!(color instanceof Color) || (!((i === 0 || i + 1 === stops.length) && position === undefined) && !(position instanceof Dimension))) {
63
66
  throwArgumentDescriptor();
64
67
  }
65
68
  positionValue = position ? position.toCSS(renderEnv) : i === 0 ? "0%" : "100%";
@@ -69,15 +72,9 @@ module.exports = function(environment) {
69
72
  returner += '</' + gradientType + 'Gradient>' +
70
73
  '<rect ' + rectangleDimension + ' fill="url(#gradient)" /></svg>';
71
74
 
72
- if (useBase64) {
73
- try {
74
- returner = environment.encodeBase64(returner);
75
- } catch(e) {
76
- useBase64 = false;
77
- }
78
- }
75
+ returner = encodeURIComponent(returner);
79
76
 
80
- returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'";
81
- return new URL(new Anonymous(returner), this.index, this.currentFileInfo);
77
+ returner = "data:image/svg+xml," + returner;
78
+ return new URL(new Quoted("'" + returner + "'", returner, false, this.index, this.currentFileInfo), this.index, this.currentFileInfo);
82
79
  });
83
80
  };
@@ -1,4 +1,5 @@
1
1
  var Keyword = require("../tree/keyword"),
2
+ DetachedRuleset = require("../tree/detached-ruleset"),
2
3
  Dimension = require("../tree/dimension"),
3
4
  Color = require("../tree/color"),
4
5
  Quoted = require("../tree/quoted"),
@@ -8,12 +9,35 @@ var Keyword = require("../tree/keyword"),
8
9
  functionRegistry = require("./function-registry");
9
10
 
10
11
  var isa = function (n, Type) {
11
- return (n instanceof Type) ? Keyword.True : Keyword.False;
12
+ return (n instanceof Type) ? Keyword.True : Keyword.False;
12
13
  },
13
14
  isunit = function (n, unit) {
14
- return (n instanceof Dimension) && n.unit.is(unit.value || unit) ? Keyword.True : Keyword.False;
15
+ if (unit === undefined) {
16
+ throw { type: "Argument", message: "missing the required second argument to isunit." };
17
+ }
18
+ unit = typeof unit.value === "string" ? unit.value : unit;
19
+ if (typeof unit !== "string") {
20
+ throw { type: "Argument", message: "Second argument to isunit should be a unit or a string." };
21
+ }
22
+ return (n instanceof Dimension) && n.unit.is(unit) ? Keyword.True : Keyword.False;
23
+ },
24
+ getItemsFromNode = function(node) {
25
+ // handle non-array values as an array of length 1
26
+ // return 'undefined' if index is invalid
27
+ var items = Array.isArray(node.value) ?
28
+ node.value : Array(node);
29
+
30
+ return items.filter(function(item) {
31
+ if (item.type === "Comment") {
32
+ return false;
33
+ }
34
+ return true;
35
+ });
15
36
  };
16
37
  functionRegistry.addMultiple({
38
+ isruleset: function (n) {
39
+ return isa(n, DetachedRuleset);
40
+ },
17
41
  iscolor: function (n) {
18
42
  return isa(n, Color);
19
43
  },
@@ -40,8 +64,10 @@ functionRegistry.addMultiple({
40
64
  },
41
65
  isunit: isunit,
42
66
  unit: function (val, unit) {
43
- if(!(val instanceof Dimension)) {
44
- throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof Operation ? ". Have you forgotten parenthesis?" : "") };
67
+ if (!(val instanceof Dimension)) {
68
+ throw { type: "Argument",
69
+ message: "the first argument to unit must be a number" +
70
+ (val instanceof Operation ? ". Have you forgotten parenthesis?" : "") };
45
71
  }
46
72
  if (unit) {
47
73
  if (unit instanceof Keyword) {
@@ -59,13 +85,10 @@ functionRegistry.addMultiple({
59
85
  },
60
86
  extract: function(values, index) {
61
87
  index = index.value - 1; // (1-based index)
62
- // handle non-array values as an array of length 1
63
- // return 'undefined' if index is invalid
64
- return Array.isArray(values.value) ?
65
- values.value[index] : Array(values)[index];
88
+
89
+ return getItemsFromNode(values)[index];
66
90
  },
67
91
  length: function(values) {
68
- var n = Array.isArray(values.value) ? values.value.length : 1;
69
- return new Dimension(n);
92
+ return new Dimension(getItemsFromNode(values).length);
70
93
  }
71
94
  });
@@ -40,12 +40,14 @@ module.exports = function(environment) {
40
40
  importManager.queue.splice(importManager.queue.indexOf(path), 1); // Remove the path from the queue
41
41
 
42
42
  var importedEqualsRoot = fullPath === importManager.rootFilename;
43
-
44
- importManager.files[fullPath] = root;
45
-
46
- if (e && !importManager.error) { importManager.error = e; }
47
-
48
- callback(e, root, importedEqualsRoot, fullPath);
43
+ if (importOptions.optional && e) {
44
+ callback(null, {rules:[]}, false, null);
45
+ }
46
+ else {
47
+ importManager.files[fullPath] = root;
48
+ if (e && !importManager.error) { importManager.error = e; }
49
+ callback(e, root, importedEqualsRoot, fullPath);
50
+ }
49
51
  };
50
52
 
51
53
  var newFileInfo = {
@@ -68,7 +70,7 @@ module.exports = function(environment) {
68
70
 
69
71
  var loadFileCallback = function(loadedFile) {
70
72
  var resolvedFilename = loadedFile.filename,
71
- contents = loadedFile.contents;
73
+ contents = loadedFile.contents.replace(/^\uFEFF/, '');
72
74
 
73
75
  // Pass on an updated rootpath if path of imported file is relative and file
74
76
  // is in a (sub|sup) directory
@@ -79,8 +81,11 @@ module.exports = function(environment) {
79
81
  // - If path of imported file is '../mixins.less' and rootpath is 'less/',
80
82
  // then rootpath should become 'less/../'
81
83
  newFileInfo.currentDirectory = fileManager.getPath(resolvedFilename);
82
- if(newFileInfo.relativeUrls) {
83
- newFileInfo.rootpath = fileManager.join((importManager.context.rootpath || ""), fileManager.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath));
84
+ if (newFileInfo.relativeUrls) {
85
+ newFileInfo.rootpath = fileManager.join(
86
+ (importManager.context.rootpath || ""),
87
+ fileManager.pathDiff(newFileInfo.currentDirectory, newFileInfo.entryPath));
88
+
84
89
  if (!fileManager.isPathAbsolute(newFileInfo.rootpath) && fileManager.alwaysMakePathsAbsolute()) {
85
90
  newFileInfo.rootpath = fileManager.join(newFileInfo.entryPath, newFileInfo.rootpath);
86
91
  }
package/lib/less/index.js CHANGED
@@ -2,7 +2,7 @@ module.exports = function(environment, fileManagers) {
2
2
  var SourceMapOutput, SourceMapBuilder, ParseTree, ImportManager, Environment;
3
3
 
4
4
  var less = {
5
- version: [2, 1, 2],
5
+ version: [2, 4, 0],
6
6
  data: require('./data'),
7
7
  tree: require('./tree'),
8
8
  Environment: (Environment = require("./environment/environment")),
@@ -17,6 +17,7 @@ module.exports = function(environment, fileManagers) {
17
17
  ParseTree: (ParseTree = require('./parse-tree')(SourceMapBuilder)),
18
18
  ImportManager: (ImportManager = require('./import-manager')(environment)),
19
19
  render: require("./render")(environment, ParseTree, ImportManager),
20
+ parse: require("./parse")(environment, ParseTree, ImportManager),
20
21
  LessError: require('./less-error'),
21
22
  transformTree: require('./transform-tree'),
22
23
  utils: require('./utils'),
@@ -17,7 +17,7 @@ var LessError = module.exports = function LessError(e, importManager, currentFil
17
17
  this.type = e.type || 'Syntax';
18
18
  this.filename = filename;
19
19
  this.index = e.index;
20
- this.line = typeof(line) === 'number' ? line + 1 : null;
20
+ this.line = typeof line === 'number' ? line + 1 : null;
21
21
  this.callLine = callLine + 1;
22
22
  this.callExtract = lines[callLine];
23
23
  this.column = col;
@@ -15,7 +15,7 @@ module.exports = {
15
15
  this._listeners.push(listener);
16
16
  },
17
17
  removeListener: function(listener) {
18
- for(var i = 0; i < this._listeners.length; i++) {
18
+ for (var i = 0; i < this._listeners.length; i++) {
19
19
  if (this._listeners[i] === listener) {
20
20
  this._listeners.splice(i, 1);
21
21
  return;
@@ -23,7 +23,7 @@ module.exports = {
23
23
  }
24
24
  },
25
25
  _fireEvent: function(type, msg) {
26
- for(var i = 0; i < this._listeners.length; i++) {
26
+ for (var i = 0; i < this._listeners.length; i++) {
27
27
  var logFunction = this._listeners[i][type];
28
28
  if (logFunction) {
29
29
  logFunction(msg);