less 3.0.0-pre.2 → 3.0.1

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 (205) hide show
  1. package/.eslintrc.json +88 -0
  2. package/.github/stale.yml +17 -0
  3. package/.project +17 -0
  4. package/.settings/org.eclipse.buildship.core.prefs +2 -0
  5. package/.travis.yml +22 -3
  6. package/.vscode/launch.json +14 -0
  7. package/CHANGELOG.md +27 -0
  8. package/Gruntfile.js +191 -86
  9. package/README.md +5 -7
  10. package/appveyor.yml +9 -10
  11. package/bin/lessc +37 -50
  12. package/dist/less.js +1357 -1269
  13. package/dist/less.min.js +7 -7
  14. package/lib/less/contexts.js +20 -16
  15. package/lib/less/default-options.js +65 -0
  16. package/lib/less/environment/abstract-file-manager.js +13 -14
  17. package/lib/less/environment/abstract-plugin-loader.js +45 -36
  18. package/lib/less/environment/environment.js +5 -0
  19. package/lib/less/functions/boolean.js +15 -0
  20. package/lib/less/functions/color.js +54 -24
  21. package/lib/less/functions/data-uri.js +5 -2
  22. package/lib/less/functions/function-registry.js +1 -1
  23. package/lib/less/functions/index.js +2 -1
  24. package/lib/less/functions/number.js +2 -2
  25. package/lib/less/functions/string.js +2 -2
  26. package/lib/less/functions/svg.js +4 -4
  27. package/lib/less/import-manager.js +41 -29
  28. package/lib/less/index.js +28 -3
  29. package/lib/less/less-error.js +112 -13
  30. package/lib/less/parse.js +18 -15
  31. package/lib/less/parser/parser-input.js +4 -4
  32. package/lib/less/parser/parser.js +208 -114
  33. package/lib/less/plugin-manager.js +8 -33
  34. package/lib/less/render.js +6 -2
  35. package/lib/less/source-map-builder.js +3 -0
  36. package/lib/less/source-map-output.js +13 -8
  37. package/lib/less/transform-tree.js +2 -2
  38. package/lib/less/tree/anonymous.js +4 -1
  39. package/lib/less/tree/atrule.js +7 -6
  40. package/lib/less/tree/call.js +25 -3
  41. package/lib/less/tree/color.js +2 -2
  42. package/lib/less/tree/debug-info.js +1 -1
  43. package/lib/less/tree/declaration.js +6 -5
  44. package/lib/less/tree/dimension.js +4 -1
  45. package/lib/less/tree/extend.js +2 -2
  46. package/lib/less/tree/import.js +17 -9
  47. package/lib/less/tree/index.js +3 -7
  48. package/lib/less/tree/js-eval-node.js +1 -1
  49. package/lib/less/tree/media.js +3 -14
  50. package/lib/less/tree/mixin-definition.js +3 -3
  51. package/lib/less/tree/node.js +9 -6
  52. package/lib/less/tree/operation.js +1 -1
  53. package/lib/less/tree/property.js +70 -0
  54. package/lib/less/tree/quoted.js +11 -10
  55. package/lib/less/tree/ruleset.js +115 -71
  56. package/lib/less/tree/selector.js +38 -23
  57. package/lib/less/tree/unit.js +1 -1
  58. package/lib/less/tree/value.js +6 -1
  59. package/lib/less/tree/{ruleset-call.js → variable-call.js} +5 -5
  60. package/lib/less/tree/variable.js +6 -6
  61. package/lib/less/utils.js +40 -0
  62. package/lib/less/visitors/extend-visitor.js +14 -14
  63. package/lib/less/visitors/import-visitor.js +2 -2
  64. package/lib/less/visitors/join-selector-visitor.js +1 -1
  65. package/lib/less/visitors/to-css-visitor.js +51 -84
  66. package/lib/less/visitors/visitor.js +15 -15
  67. package/lib/less-browser/bootstrap.js +21 -5
  68. package/lib/less-browser/cache.js +2 -2
  69. package/lib/less-browser/error-reporting.js +5 -5
  70. package/lib/less-browser/file-manager.js +27 -35
  71. package/lib/less-browser/index.js +12 -17
  72. package/lib/less-browser/plugin-loader.js +8 -45
  73. package/lib/less-browser/utils.js +1 -1
  74. package/lib/less-node/file-manager.js +99 -69
  75. package/lib/less-node/fs.js +1 -1
  76. package/lib/less-node/image-size.js +2 -2
  77. package/lib/less-node/index.js +7 -57
  78. package/lib/less-node/lessc-helper.js +38 -38
  79. package/lib/less-node/plugin-loader.js +18 -79
  80. package/lib/less-node/url-file-manager.js +1 -1
  81. package/lib/less-rhino/index.js +29 -44
  82. package/package.json +16 -9
  83. package/test/browser/common.js +54 -18
  84. package/test/browser/css/plugin/plugin.css +3 -0
  85. package/test/browser/jasmine-jsreporter.js +4 -4
  86. package/test/browser/less/plugin/plugin.js +7 -0
  87. package/test/browser/less/plugin/plugin.less +4 -0
  88. package/test/browser/less.js +11685 -13
  89. package/test/browser/runner-browser-options.js +6 -1
  90. package/test/browser/runner-filemanagerPlugin-options.js +3 -2
  91. package/test/browser/runner-legacy-options.js +2 -1
  92. package/test/browser/runner-main-options.js +0 -1
  93. package/test/browser/runner-modify-vars-spec.js +2 -2
  94. package/test/browser/test-runner-template.tmpl +38 -14
  95. package/test/copy-bom.js +1 -1
  96. package/test/css/calc.css +5 -0
  97. package/test/css/comments2.css +1 -1
  98. package/test/css/compression/compression.css +1 -1
  99. package/test/css/css-grid.css +11 -0
  100. package/test/css/debug/linenumbers-all.css +0 -1
  101. package/test/css/debug/linenumbers-comments.css +0 -1
  102. package/test/css/debug/linenumbers-mediaquery.css +0 -1
  103. package/test/css/functions.css +16 -3
  104. package/test/css/import-module.css +9 -0
  105. package/test/css/import.css +0 -1
  106. package/test/css/legacy/legacy.css +1 -1
  107. package/test/css/merge.css +5 -4
  108. package/test/css/plugin-module.css +1 -0
  109. package/test/css/plugin.css +20 -0
  110. package/test/css/property-accessors.css +49 -0
  111. package/test/css/{css.css → strict-math/css.css} +0 -0
  112. package/test/css/{mixins-args.css → strict-math/mixins-args.css} +0 -0
  113. package/test/css/{parens.css → strict-math/parens.css} +0 -0
  114. package/test/import-module/one/1.less +3 -0
  115. package/test/import-module/one/two/2.less +3 -0
  116. package/test/import-module/one/two/three/3.less +3 -0
  117. package/test/import-module/package.json +11 -0
  118. package/test/index.js +55 -49
  119. package/test/less/calc.less +7 -0
  120. package/test/less/css-grid.less +15 -0
  121. package/test/less/detached-rulesets.less +2 -2
  122. package/test/less/errors/color-invalid-hex-code.less +1 -0
  123. package/test/less/errors/color-invalid-hex-code.txt +1 -1
  124. package/test/less/errors/color-invalid-hex-code2.less +1 -0
  125. package/test/less/errors/color-invalid-hex-code2.txt +1 -1
  126. package/test/less/errors/import-missing.txt +1 -1
  127. package/test/less/errors/plugin/plugin-error-2.js +5 -0
  128. package/test/less/errors/plugin/plugin-error-3.js +5 -0
  129. package/test/less/errors/plugin/plugin-error.js +1 -0
  130. package/test/less/errors/plugin-1.less +1 -0
  131. package/test/less/errors/plugin-1.txt +2 -0
  132. package/test/less/errors/plugin-2.less +1 -0
  133. package/test/less/errors/plugin-2.txt +5 -0
  134. package/test/less/errors/plugin-3.less +1 -0
  135. package/test/less/errors/plugin-3.txt +5 -0
  136. package/test/less/functions.less +18 -0
  137. package/test/less/import-module.less +3 -0
  138. package/test/less/import.less +0 -2
  139. package/test/less/legacy/legacy.less +1 -1
  140. package/test/less/media.less +2 -2
  141. package/test/less/merge.less +6 -3
  142. package/test/less/plugin/plugin-collection.js +9 -0
  143. package/test/less/plugin/plugin-local.js +2 -2
  144. package/test/less/plugin/plugin-scope1.js +3 -0
  145. package/test/less/plugin/plugin-scope2.js +3 -0
  146. package/test/less/plugin/plugin-simple.js +7 -0
  147. package/test/less/plugin/plugin-tree-nodes.js +33 -31
  148. package/test/less/plugin-module.less +7 -0
  149. package/test/less/plugin.less +33 -3
  150. package/test/less/property-accessors.less +67 -0
  151. package/test/less/root-registry/file.less +1 -0
  152. package/test/less/root-registry/root.less +3 -0
  153. package/test/less/{css.less → strict-math/css.less} +0 -0
  154. package/test/less/{mixins-args.less → strict-math/mixins-args.less} +0 -0
  155. package/test/less/{parens.less → strict-math/parens.less} +0 -0
  156. package/test/less-bom/calc.less +7 -0
  157. package/test/less-bom/css-grid.less +15 -0
  158. package/test/less-bom/detached-rulesets.less +2 -2
  159. package/test/less-bom/errors/color-invalid-hex-code.less +1 -0
  160. package/test/less-bom/errors/color-invalid-hex-code.txt +1 -1
  161. package/test/less-bom/errors/color-invalid-hex-code2.less +1 -0
  162. package/test/less-bom/errors/color-invalid-hex-code2.txt +1 -1
  163. package/test/less-bom/errors/import-missing.txt +1 -1
  164. package/test/less-bom/errors/plugin/plugin-error-2.js +5 -0
  165. package/test/less-bom/errors/plugin/plugin-error-3.js +5 -0
  166. package/test/less-bom/errors/plugin/plugin-error.js +1 -0
  167. package/test/less-bom/errors/plugin-1.less +1 -0
  168. package/test/less-bom/errors/plugin-1.txt +2 -0
  169. package/test/less-bom/errors/plugin-2.less +1 -0
  170. package/test/less-bom/errors/plugin-2.txt +5 -0
  171. package/test/less-bom/errors/plugin-3.less +1 -0
  172. package/test/less-bom/errors/plugin-3.txt +5 -0
  173. package/test/less-bom/functions.less +18 -0
  174. package/test/less-bom/import-module.less +3 -0
  175. package/test/less-bom/import.less +0 -2
  176. package/test/less-bom/legacy/legacy.less +1 -1
  177. package/test/less-bom/media.less +2 -2
  178. package/test/less-bom/merge.less +6 -3
  179. package/test/less-bom/plugin/plugin-collection.js +9 -0
  180. package/test/less-bom/plugin/plugin-local.js +2 -2
  181. package/test/less-bom/plugin/plugin-scope1.js +3 -0
  182. package/test/less-bom/plugin/plugin-scope2.js +3 -0
  183. package/test/less-bom/plugin/plugin-simple.js +7 -0
  184. package/test/less-bom/plugin/plugin-tree-nodes.js +33 -31
  185. package/test/less-bom/plugin-module.less +7 -0
  186. package/test/less-bom/plugin.less +33 -3
  187. package/test/less-bom/property-accessors.less +67 -0
  188. package/test/less-bom/root-registry/file.less +1 -0
  189. package/test/less-bom/root-registry/root.less +3 -0
  190. package/test/less-bom/{css.less → strict-math/css.less} +0 -0
  191. package/test/less-bom/{mixins-args.less → strict-math/mixins-args.less} +0 -0
  192. package/test/less-bom/{parens.less → strict-math/parens.less} +0 -0
  193. package/test/less-test.js +75 -17
  194. package/test/modify-vars.js +1 -1
  195. package/test/plugins/visitor/index.js +1 -1
  196. package/.jscsrc +0 -73
  197. package/.jshintrc +0 -11
  198. package/lib/less/tree/alpha.js +0 -28
  199. package/lib/less/tree/directive.js +0 -12
  200. package/lib/less/tree/rule.js +0 -12
  201. package/test/less/errors/functions-2-alpha.less +0 -2
  202. package/test/less/errors/functions-2-alpha.txt +0 -3
  203. package/test/less-bom/errors/functions-2-alpha.less +0 -2
  204. package/test/less-bom/errors/functions-2-alpha.txt +0 -3
  205. package/test/sourcemaps/maps/import-map.map +0 -1
@@ -1,6 +1,6 @@
1
1
  var Node = require("./node"),
2
- JsEvalNode = require("./js-eval-node"),
3
- Variable = require("./variable");
2
+ Variable = require("./variable"),
3
+ Property = require("./property");
4
4
 
5
5
  var Quoted = function (str, content, escaped, index, currentFileInfo) {
6
6
  this.escaped = (escaped == null) ? true : escaped;
@@ -9,7 +9,7 @@ var Quoted = function (str, content, escaped, index, currentFileInfo) {
9
9
  this._index = index;
10
10
  this._fileInfo = currentFileInfo;
11
11
  };
12
- Quoted.prototype = new JsEvalNode();
12
+ Quoted.prototype = new Node();
13
13
  Quoted.prototype.type = "Quoted";
14
14
  Quoted.prototype.genCSS = function (context, output) {
15
15
  if (!this.escaped) {
@@ -21,17 +21,18 @@ Quoted.prototype.genCSS = function (context, output) {
21
21
  }
22
22
  };
23
23
  Quoted.prototype.containsVariables = function() {
24
- return this.value.match(/(`([^`]+)`)|@\{([\w-]+)\}/);
24
+ return this.value.match(/@\{([\w-]+)\}/);
25
25
  };
26
26
  Quoted.prototype.eval = function (context) {
27
27
  var that = this, value = this.value;
28
- var javascriptReplacement = function (_, exp) {
29
- return String(that.evaluateJavaScript(exp, context));
30
- };
31
- var interpolationReplacement = function (_, name) {
28
+ var variableReplacement = function (_, name) {
32
29
  var v = new Variable('@' + name, that.getIndex(), that.fileInfo()).eval(context, true);
33
30
  return (v instanceof Quoted) ? v.value : v.toCSS();
34
31
  };
32
+ var propertyReplacement = function (_, name) {
33
+ var v = new Property('$' + name, that.getIndex(), that.fileInfo()).eval(context, true);
34
+ return (v instanceof Quoted) ? v.value : v.toCSS();
35
+ };
35
36
  function iterativeReplace(value, regexp, replacementFnc) {
36
37
  var evaluatedValue = value;
37
38
  do {
@@ -40,8 +41,8 @@ Quoted.prototype.eval = function (context) {
40
41
  } while (value !== evaluatedValue);
41
42
  return evaluatedValue;
42
43
  }
43
- value = iterativeReplace(value, /`([^`]+)`/g, javascriptReplacement);
44
- value = iterativeReplace(value, /@\{([\w-]+)\}/g, interpolationReplacement);
44
+ value = iterativeReplace(value, /@\{([\w-]+)\}/g, variableReplacement);
45
+ value = iterativeReplace(value, /\$\{([\w-]+)\}/g, propertyReplacement);
45
46
  return new Quoted(this.quote + value + this.quote, value, this.escaped, this.getIndex(), this.fileInfo());
46
47
  };
47
48
  Quoted.prototype.compare = function (other) {
@@ -1,8 +1,11 @@
1
1
  var Node = require("./node"),
2
2
  Declaration = require("./declaration"),
3
+ Keyword = require("./keyword"),
4
+ Comment = require("./comment"),
5
+ Paren = require("./paren"),
3
6
  Selector = require("./selector"),
4
7
  Element = require("./element"),
5
- Paren = require("./paren"),
8
+ Anonymous = require("./anonymous"),
6
9
  contexts = require("../contexts"),
7
10
  globalFunctionRegistry = require("../functions/function-registry"),
8
11
  defaultFunc = require("../functions/default"),
@@ -13,9 +16,12 @@ var Ruleset = function (selectors, rules, strictImports, visibilityInfo) {
13
16
  this.selectors = selectors;
14
17
  this.rules = rules;
15
18
  this._lookups = {};
19
+ this._variables = null;
20
+ this._properties = null;
16
21
  this.strictImports = strictImports;
17
22
  this.copyVisibilityInfo(visibilityInfo);
18
23
  this.allowRoot = true;
24
+
19
25
  this.setParent(this.selectors, this);
20
26
  this.setParent(this.rules, this);
21
27
 
@@ -23,7 +29,7 @@ var Ruleset = function (selectors, rules, strictImports, visibilityInfo) {
23
29
  Ruleset.prototype = new Node();
24
30
  Ruleset.prototype.type = "Ruleset";
25
31
  Ruleset.prototype.isRuleset = true;
26
- Ruleset.prototype.isRulesetLike = true;
32
+ Ruleset.prototype.isRulesetLike = function() { return true; };
27
33
  Ruleset.prototype.accept = function (visitor) {
28
34
  if (this.paths) {
29
35
  this.paths = visitor.visitArray(this.paths, true);
@@ -104,20 +110,20 @@ Ruleset.prototype.eval = function (context) {
104
110
 
105
111
  // Store the frames around mixin definitions,
106
112
  // so they can be evaluated like closures when the time comes.
107
- var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0;
108
- for (i = 0; i < rsRuleCnt; i++) {
109
- if (rsRules[i].evalFirst) {
110
- rsRules[i] = rsRules[i].eval(context);
113
+ var rsRules = ruleset.rules;
114
+ for (i = 0; (rule = rsRules[i]); i++) {
115
+ if (rule.evalFirst) {
116
+ rsRules[i] = rule.eval(context);
111
117
  }
112
118
  }
113
119
 
114
120
  var mediaBlockCount = (context.mediaBlocks && context.mediaBlocks.length) || 0;
115
121
 
116
122
  // Evaluate mixin calls.
117
- for (i = 0; i < rsRuleCnt; i++) {
118
- if (rsRules[i].type === "MixinCall") {
119
- /*jshint loopfunc:true */
120
- rules = rsRules[i].eval(context).filter(function(r) {
123
+ for (i = 0; (rule = rsRules[i]); i++) {
124
+ if (rule.type === "MixinCall") {
125
+ /* jshint loopfunc:true */
126
+ rules = rule.eval(context).filter(function(r) {
121
127
  if ((r instanceof Declaration) && r.variable) {
122
128
  // do not pollute the scope if the variable is
123
129
  // already there. consider returning false here
@@ -127,12 +133,11 @@ Ruleset.prototype.eval = function (context) {
127
133
  return true;
128
134
  });
129
135
  rsRules.splice.apply(rsRules, [i, 1].concat(rules));
130
- rsRuleCnt += rules.length - 1;
131
136
  i += rules.length - 1;
132
137
  ruleset.resetCache();
133
- } else if (rsRules[i].type === "RulesetCall") {
134
- /*jshint loopfunc:true */
135
- rules = rsRules[i].eval(context).rules.filter(function(r) {
138
+ } else if (rule.type === "VariableCall") {
139
+ /* jshint loopfunc:true */
140
+ rules = rule.eval(context).rules.filter(function(r) {
136
141
  if ((r instanceof Declaration) && r.variable) {
137
142
  // do not pollute the scope at all
138
143
  return false;
@@ -140,31 +145,27 @@ Ruleset.prototype.eval = function (context) {
140
145
  return true;
141
146
  });
142
147
  rsRules.splice.apply(rsRules, [i, 1].concat(rules));
143
- rsRuleCnt += rules.length - 1;
144
148
  i += rules.length - 1;
145
149
  ruleset.resetCache();
146
150
  }
147
151
  }
148
152
 
149
153
  // Evaluate everything else
150
- for (i = 0; i < rsRules.length; i++) {
151
- rule = rsRules[i];
154
+ for (i = 0; (rule = rsRules[i]); i++) {
152
155
  if (!rule.evalFirst) {
153
156
  rsRules[i] = rule = rule.eval ? rule.eval(context) : rule;
154
157
  }
155
158
  }
156
159
 
157
160
  // Evaluate everything else
158
- for (i = 0; i < rsRules.length; i++) {
159
- rule = rsRules[i];
161
+ for (i = 0; (rule = rsRules[i]); i++) {
160
162
  // for rulesets, check if it is a css guard and can be removed
161
163
  if (rule instanceof Ruleset && rule.selectors && rule.selectors.length === 1) {
162
164
  // check if it can be folded in (e.g. & where)
163
165
  if (rule.selectors[0].isJustParentSelector()) {
164
166
  rsRules.splice(i--, 1);
165
167
 
166
- for (var j = 0; j < rule.rules.length; j++) {
167
- subRule = rule.rules[j];
168
+ for (var j = 0; (subRule = rule.rules[j]); j++) {
168
169
  if (subRule instanceof Node) {
169
170
  subRule.copyVisibilityInfo(rule.visibilityInfo());
170
171
  if (!(subRule instanceof Declaration) || !subRule.variable) {
@@ -197,7 +198,7 @@ Ruleset.prototype.evalImports = function(context) {
197
198
  importRules = rules[i].eval(context);
198
199
  if (importRules && (importRules.length || importRules.length === 0)) {
199
200
  rules.splice.apply(rules, [i, 1].concat(importRules));
200
- i+= importRules.length - 1;
201
+ i += importRules.length - 1;
201
202
  } else {
202
203
  rules.splice(i, 1, importRules);
203
204
  }
@@ -236,6 +237,7 @@ Ruleset.prototype.matchCondition = function (args, context) {
236
237
  Ruleset.prototype.resetCache = function () {
237
238
  this._rulesets = null;
238
239
  this._variables = null;
240
+ this._properties = null;
239
241
  this._lookups = {};
240
242
  };
241
243
  Ruleset.prototype.variables = function () {
@@ -260,17 +262,81 @@ Ruleset.prototype.variables = function () {
260
262
  }
261
263
  return this._variables;
262
264
  };
265
+ Ruleset.prototype.properties = function () {
266
+ if (!this._properties) {
267
+ this._properties = !this.rules ? {} : this.rules.reduce(function (hash, r) {
268
+ if (r instanceof Declaration && r.variable !== true) {
269
+ var name = (r.name.length === 1) && (r.name[0] instanceof Keyword) ?
270
+ r.name[0].value : r.name;
271
+ // Properties don't overwrite as they can merge
272
+ if (!hash['$' + name]) {
273
+ hash['$' + name] = [ r ];
274
+ }
275
+ else {
276
+ hash['$' + name].push(r);
277
+ }
278
+ }
279
+ return hash;
280
+ }, {});
281
+ }
282
+ return this._properties;
283
+ };
263
284
  Ruleset.prototype.variable = function (name) {
264
- return this.variables()[name];
285
+ var decl = this.variables()[name];
286
+ if (decl) {
287
+ return this.parseValue(decl);
288
+ }
289
+ };
290
+ Ruleset.prototype.property = function (name) {
291
+ var decl = this.properties()[name];
292
+ if (decl) {
293
+ return this.parseValue(decl);
294
+ }
295
+ };
296
+ Ruleset.prototype.parseValue = function(toParse) {
297
+ var self = this;
298
+ function transformDeclaration(decl) {
299
+ if (decl.value instanceof Anonymous && !decl.parsed) {
300
+ this.parse.parseNode(
301
+ decl.value.value,
302
+ ["value", "important"],
303
+ decl.value.getIndex(),
304
+ decl.fileInfo(),
305
+ function(err, result) {
306
+ if (err) {
307
+ decl.parsed = true;
308
+ }
309
+ if (result) {
310
+ decl.value = result[0];
311
+ decl.important = result[1] || '';
312
+ decl.parsed = true;
313
+ }
314
+ });
315
+
316
+ return decl;
317
+ }
318
+ else {
319
+ return decl;
320
+ }
321
+ }
322
+ if (!Array.isArray(toParse)) {
323
+ return transformDeclaration.call(self, toParse);
324
+ }
325
+ else {
326
+ var nodes = [];
327
+ toParse.forEach(function(n) {
328
+ nodes.push(transformDeclaration.call(self, n));
329
+ });
330
+ return nodes;
331
+ }
265
332
  };
266
333
  Ruleset.prototype.rulesets = function () {
267
334
  if (!this.rules) { return []; }
268
335
 
269
- var filtRules = [], rules = this.rules, cnt = rules.length,
336
+ var filtRules = [], rules = this.rules,
270
337
  i, rule;
271
338
 
272
- for (i = 0; i < cnt; i++) {
273
- rule = rules[i];
339
+ for (i = 0; (rule = rules[i]); i++) {
274
340
  if (rule.isRuleset) {
275
341
  filtRules.push(rule);
276
342
  }
@@ -336,41 +402,23 @@ Ruleset.prototype.genCSS = function (context, output) {
336
402
  tabSetStr = context.compress ? '' : Array(context.tabLevel).join(" "),
337
403
  sep;
338
404
 
339
- function isRulesetLikeNode(rule) {
340
- // if it has nested rules, then it should be treated like a ruleset
341
- // medias and comments do not have nested rules, but should be treated like rulesets anyway
342
- // some atrules and anonymous nodes are ruleset like, others are not
343
- if (typeof rule.isRulesetLike === "boolean") {
344
- return rule.isRulesetLike;
345
- } else if (typeof rule.isRulesetLike === "function") {
346
- return rule.isRulesetLike();
347
- }
348
-
349
- //anything else is assumed to be a rule
350
- return false;
351
- }
352
-
353
405
  var charsetNodeIndex = 0;
354
406
  var importNodeIndex = 0;
355
- for (i = 0; i < this.rules.length; i++) {
356
- rule = this.rules[i];
357
- // Plugins may return something other than Nodes
358
- if (rule instanceof Node) {
359
- if (rule.type === "Comment") {
360
- if (importNodeIndex === i) {
361
- importNodeIndex++;
362
- }
363
- ruleNodes.push(rule);
364
- } else if (rule.isCharset && rule.isCharset()) {
365
- ruleNodes.splice(charsetNodeIndex, 0, rule);
366
- charsetNodeIndex++;
407
+ for (i = 0; (rule = this.rules[i]); i++) {
408
+ if (rule instanceof Comment) {
409
+ if (importNodeIndex === i) {
367
410
  importNodeIndex++;
368
- } else if (rule.type === "Import") {
369
- ruleNodes.splice(importNodeIndex, 0, rule);
370
- importNodeIndex++;
371
- } else {
372
- ruleNodes.push(rule);
373
411
  }
412
+ ruleNodes.push(rule);
413
+ } else if (rule.isCharset && rule.isCharset()) {
414
+ ruleNodes.splice(charsetNodeIndex, 0, rule);
415
+ charsetNodeIndex++;
416
+ importNodeIndex++;
417
+ } else if (rule.type === "Import") {
418
+ ruleNodes.splice(importNodeIndex, 0, rule);
419
+ importNodeIndex++;
420
+ } else {
421
+ ruleNodes.push(rule);
374
422
  }
375
423
  }
376
424
  ruleNodes = charsetRuleNodes.concat(ruleNodes);
@@ -408,15 +456,14 @@ Ruleset.prototype.genCSS = function (context, output) {
408
456
  }
409
457
 
410
458
  // Compile rules and rulesets
411
- for (i = 0; i < ruleNodes.length; i++) {
412
- rule = ruleNodes[i];
459
+ for (i = 0; (rule = ruleNodes[i]); i++) {
413
460
 
414
461
  if (i + 1 === ruleNodes.length) {
415
462
  context.lastRule = true;
416
463
  }
417
464
 
418
465
  var currentLastRule = context.lastRule;
419
- if (isRulesetLikeNode(rule)) {
466
+ if (rule.isRulesetLike(rule)) {
420
467
  context.lastRule = false;
421
468
  }
422
469
 
@@ -428,7 +475,7 @@ Ruleset.prototype.genCSS = function (context, output) {
428
475
 
429
476
  context.lastRule = currentLastRule;
430
477
 
431
- if (!context.lastRule) {
478
+ if (!context.lastRule && rule.isVisible()) {
432
479
  output.add(context.compress ? '' : ('\n' + tabRuleStr));
433
480
  } else {
434
481
  context.lastRule = false;
@@ -482,7 +529,7 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
482
529
  // our new selector path
483
530
  newSelectorPath = [];
484
531
 
485
- //construct the joined selector - if & is the first thing this will be empty,
532
+ // construct the joined selector - if & is the first thing this will be empty,
486
533
  // if not newJoinedSelector will be the last set of elements in the selector
487
534
  if (beginningPath.length > 0) {
488
535
  newSelectorPath = utils.copyArray(beginningPath);
@@ -512,7 +559,7 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
512
559
  newSelectorPath.push(newJoinedSelector);
513
560
  }
514
561
 
515
- //put together the parent selectors after the join (e.g. the rest of the parent)
562
+ // put together the parent selectors after the join (e.g. the rest of the parent)
516
563
  if (addPath.length > 1) {
517
564
  var restOfPath = addPath.slice(1);
518
565
  restOfPath = restOfPath.map(function (selector) {
@@ -546,9 +593,7 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
546
593
  return;
547
594
  }
548
595
 
549
- for (i = 0; i < selectors.length; i++) {
550
- sel = selectors[i];
551
-
596
+ for (i = 0; (sel = selectors[i]); i++) {
552
597
  // if the previous thing in sel is a parent this needs to join on to it
553
598
  if (sel.length > 0) {
554
599
  sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements));
@@ -576,12 +621,12 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
576
621
  var i, j, k, currentElements, newSelectors, selectorsMultiplied, sel, el, hadParentSelector = false, length, lastSelector;
577
622
  function findNestedSelector(element) {
578
623
  var maybeSelector;
579
- if (element.value.type !== 'Paren') {
624
+ if (!(element.value instanceof Paren)) {
580
625
  return null;
581
626
  }
582
627
 
583
628
  maybeSelector = element.value.value;
584
- if (maybeSelector.type !== 'Selector') {
629
+ if (!(maybeSelector instanceof Selector)) {
585
630
  return null;
586
631
  }
587
632
 
@@ -597,8 +642,7 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
597
642
  []
598
643
  ];
599
644
 
600
- for (i = 0; i < inSelector.elements.length; i++) {
601
- el = inSelector.elements[i];
645
+ for (i = 0; (el = inSelector.elements[i]); i++) {
602
646
  // non parent reference elements just get added
603
647
  if (el.value !== "&") {
604
648
  var nestedSelector = findNestedSelector(el);
@@ -610,7 +654,7 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
610
654
  var nestedPaths = [], replaced, replacedNewSelectors = [];
611
655
  replaced = replaceParentSelector(nestedPaths, context, nestedSelector);
612
656
  hadParentSelector = hadParentSelector || replaced;
613
- //the nestedPaths array should have only one member - replaceParentSelector does not multiply selectors
657
+ // the nestedPaths array should have only one member - replaceParentSelector does not multiply selectors
614
658
  for (k = 0; k < nestedPaths.length; k++) {
615
659
  var replacementSelector = createSelector(createParenthesis(nestedPaths[k], el), el);
616
660
  addAllReplacementsIntoPath(newSelectors, [replacementSelector], el, inSelector, replacedNewSelectors);
@@ -1,15 +1,15 @@
1
1
  var Node = require("./node"),
2
- Element = require("./element");
2
+ Element = require("./element"),
3
+ LessError = require("../less-error");
3
4
 
4
5
  var Selector = function (elements, extendList, condition, index, currentFileInfo, visibilityInfo) {
5
- this.elements = elements;
6
6
  this.extendList = extendList;
7
7
  this.condition = condition;
8
+ this.evaldCondition = !condition;
8
9
  this._index = index;
9
10
  this._fileInfo = currentFileInfo;
10
- if (!condition) {
11
- this.evaldCondition = true;
12
- }
11
+ this.elements = this.getElements(elements);
12
+ this.mixinElements_ = undefined;
13
13
  this.copyVisibilityInfo(visibilityInfo);
14
14
  this.setParent(this.elements, this);
15
15
  };
@@ -27,13 +27,32 @@ Selector.prototype.accept = function (visitor) {
27
27
  }
28
28
  };
29
29
  Selector.prototype.createDerived = function(elements, extendList, evaldCondition) {
30
- var info = this.visibilityInfo();
31
- evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition;
32
- var newSelector = new Selector(elements, extendList || this.extendList, null, this.getIndex(), this.fileInfo(), info);
33
- newSelector.evaldCondition = evaldCondition;
30
+ elements = this.getElements(elements);
31
+ var newSelector = new Selector(elements, extendList || this.extendList,
32
+ null, this.getIndex(), this.fileInfo(), this.visibilityInfo());
33
+ newSelector.evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition;
34
34
  newSelector.mediaEmpty = this.mediaEmpty;
35
35
  return newSelector;
36
36
  };
37
+ Selector.prototype.getElements = function(els) {
38
+ if (typeof els === "string") {
39
+ this.parse.parseNode(
40
+ els,
41
+ ["selector"],
42
+ this._index,
43
+ this._fileInfo,
44
+ function(err, result) {
45
+ if (err) {
46
+ throw new LessError({
47
+ index: err.index,
48
+ message: err.message
49
+ }, this.parse.imports, this._fileInfo.filename);
50
+ }
51
+ els = result[0].elements;
52
+ });
53
+ }
54
+ return els;
55
+ };
37
56
  Selector.prototype.createEmptySelectors = function() {
38
57
  var el = new Element('', '&', this._index, this._fileInfo),
39
58
  sels = [new Selector([el], null, null, this._index, this._fileInfo)];
@@ -45,14 +64,13 @@ Selector.prototype.match = function (other) {
45
64
  len = elements.length,
46
65
  olen, i;
47
66
 
48
- other.CacheElements();
49
-
50
- olen = other._elements.length;
67
+ other = other.mixinElements();
68
+ olen = other.length;
51
69
  if (olen === 0 || len < olen) {
52
70
  return 0;
53
71
  } else {
54
72
  for (i = 0; i < olen; i++) {
55
- if (elements[i].value !== other._elements[i]) {
73
+ if (elements[i].value !== other[i]) {
56
74
  return 0;
57
75
  }
58
76
  }
@@ -60,9 +78,9 @@ Selector.prototype.match = function (other) {
60
78
 
61
79
  return olen; // return number of matched elements
62
80
  };
63
- Selector.prototype.CacheElements = function() {
64
- if (this._elements) {
65
- return;
81
+ Selector.prototype.mixinElements = function() {
82
+ if (this.mixinElements_) {
83
+ return this.mixinElements_;
66
84
  }
67
85
 
68
86
  var elements = this.elements.map( function(v) {
@@ -77,7 +95,7 @@ Selector.prototype.CacheElements = function() {
77
95
  elements = [];
78
96
  }
79
97
 
80
- this._elements = elements;
98
+ return (this.mixinElements_ = elements);
81
99
  };
82
100
  Selector.prototype.isJustParentSelector = function() {
83
101
  return !this.mediaEmpty &&
@@ -99,12 +117,9 @@ Selector.prototype.genCSS = function (context, output) {
99
117
  if ((!context || !context.firstSelector) && this.elements[0].combinator.value === "") {
100
118
  output.add(' ', this.fileInfo(), this.getIndex());
101
119
  }
102
- if (!this._css) {
103
- //TODO caching? speed comparison?
104
- for (i = 0; i < this.elements.length; i++) {
105
- element = this.elements[i];
106
- element.genCSS(context, output);
107
- }
120
+ for (i = 0; i < this.elements.length; i++) {
121
+ element = this.elements[i];
122
+ element.genCSS(context, output);
108
123
  }
109
124
  };
110
125
  Selector.prototype.getIsOutput = function() {
@@ -65,7 +65,7 @@ Unit.prototype.usedUnits = function() {
65
65
  var group, result = {}, mapUnit, groupName;
66
66
 
67
67
  mapUnit = function (atomicUnit) {
68
- /*jshint loopfunc:true */
68
+ /* jshint loopfunc:true */
69
69
  if (group.hasOwnProperty(atomicUnit) && !result[groupName]) {
70
70
  result[groupName] = atomicUnit;
71
71
  }
@@ -1,10 +1,15 @@
1
1
  var Node = require("./node");
2
2
 
3
3
  var Value = function (value) {
4
- this.value = value;
5
4
  if (!value) {
6
5
  throw new Error("Value requires an array argument");
7
6
  }
7
+ if (!Array.isArray(value)) {
8
+ this.value = [ value ];
9
+ }
10
+ else {
11
+ this.value = value;
12
+ }
8
13
  };
9
14
  Value.prototype = new Node();
10
15
  Value.prototype.type = "Value";
@@ -1,14 +1,14 @@
1
1
  var Node = require("./node"),
2
2
  Variable = require("./variable");
3
3
 
4
- var RulesetCall = function (variable) {
4
+ var VariableCall = function (variable) {
5
5
  this.variable = variable;
6
6
  this.allowRoot = true;
7
7
  };
8
- RulesetCall.prototype = new Node();
9
- RulesetCall.prototype.type = "RulesetCall";
10
- RulesetCall.prototype.eval = function (context) {
8
+ VariableCall.prototype = new Node();
9
+ VariableCall.prototype.type = "VariableCall";
10
+ VariableCall.prototype.eval = function (context) {
11
11
  var detachedRuleset = new Variable(this.variable).eval(context);
12
12
  return detachedRuleset.callEval(context);
13
13
  };
14
- module.exports = RulesetCall;
14
+ module.exports = VariableCall;
@@ -16,9 +16,9 @@ Variable.prototype.eval = function (context) {
16
16
 
17
17
  if (this.evaluating) {
18
18
  throw { type: 'Name',
19
- message: "Recursive variable definition for " + name,
20
- filename: this.fileInfo().filename,
21
- index: this.getIndex() };
19
+ message: "Recursive variable definition for " + name,
20
+ filename: this.fileInfo().filename,
21
+ index: this.getIndex() };
22
22
  }
23
23
 
24
24
  this.evaluating = true;
@@ -38,9 +38,9 @@ Variable.prototype.eval = function (context) {
38
38
  return variable;
39
39
  } else {
40
40
  throw { type: 'Name',
41
- message: "variable " + name + " is undefined",
42
- filename: this.fileInfo().filename,
43
- index: this.getIndex() };
41
+ message: "variable " + name + " is undefined",
42
+ filename: this.fileInfo().filename,
43
+ index: this.getIndex() };
44
44
  }
45
45
  };
46
46
  Variable.prototype.find = function (obj, fun) {
package/lib/less/utils.js CHANGED
@@ -1,3 +1,4 @@
1
+ /* jshint proto: true */
1
2
  module.exports = {
2
3
  getLocation: function(index, inputStream) {
3
4
  var n = index + 1,
@@ -25,5 +26,44 @@ module.exports = {
25
26
  copy[i] = arr[i];
26
27
  }
27
28
  return copy;
29
+ },
30
+ clone: function (obj) {
31
+ var cloned = {};
32
+ for (var prop in obj) {
33
+ if (obj.hasOwnProperty(prop)) {
34
+ cloned[prop] = obj[prop];
35
+ }
36
+ }
37
+ return cloned;
38
+ },
39
+ defaults: function(obj1, obj2) {
40
+ if (!obj2._defaults || obj2._defaults !== obj1) {
41
+ for (var prop in obj1) {
42
+ if (obj1.hasOwnProperty(prop)) {
43
+ if (!obj2.hasOwnProperty(prop)) {
44
+ obj2[prop] = obj1[prop];
45
+ }
46
+ else if (Array.isArray(obj1[prop])
47
+ && Array.isArray(obj2[prop])) {
48
+
49
+ obj1[prop].forEach(function(p) {
50
+ if (obj2[prop].indexOf(p) === -1) {
51
+ obj2[prop].push(p);
52
+ }
53
+ });
54
+ }
55
+ }
56
+ }
57
+ }
58
+ obj2._defaults = obj1;
59
+ return obj2;
60
+ },
61
+ merge: function(obj1, obj2) {
62
+ for (var prop in obj2) {
63
+ if (obj2.hasOwnProperty(prop)) {
64
+ obj1[prop] = obj2[prop];
65
+ }
66
+ }
67
+ return obj1;
28
68
  }
29
69
  };