less 2.3.3 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +5 -0
  3. data/less.gemspec +1 -1
  4. data/lib/less/js/.gitattributes +9 -0
  5. data/lib/less/js/.gitignore +1 -0
  6. data/lib/less/js/.npmignore +1 -1
  7. data/lib/less/js/CHANGELOG.md +68 -0
  8. data/lib/less/js/CONTRIBUTING.md +33 -34
  9. data/lib/less/js/Makefile +24 -9
  10. data/lib/less/js/README.md +2 -2
  11. data/lib/less/js/bin/lessc +102 -25
  12. data/lib/less/js/build/amd.js +1 -1
  13. data/lib/less/js/build/header.js +9 -7
  14. data/lib/less/js/dist/less-1.3.3.js +2 -2
  15. data/lib/less/js/dist/less-1.3.3.min.js +2 -2
  16. data/lib/less/js/dist/less-1.4.0-beta.js +5830 -0
  17. data/lib/less/js/dist/less-1.4.0-beta.min.js +11 -0
  18. data/lib/less/js/dist/less-1.4.0.js +5830 -0
  19. data/lib/less/js/dist/less-1.4.0.min.js +11 -0
  20. data/lib/less/js/dist/less-1.4.1.js +5837 -0
  21. data/lib/less/js/dist/less-1.4.1.min.js +11 -0
  22. data/lib/less/js/dist/less-1.4.2.js +5837 -0
  23. data/lib/less/js/dist/less-1.4.2.min.js +11 -0
  24. data/lib/less/js/dist/less-rhino-1.4.0.js +4273 -0
  25. data/lib/less/js/lib/less/browser.js +131 -101
  26. data/lib/less/js/lib/less/env.js +105 -0
  27. data/lib/less/js/lib/less/extend-visitor.js +391 -0
  28. data/lib/less/js/lib/less/functions.js +174 -19
  29. data/lib/less/js/lib/less/import-visitor.js +107 -0
  30. data/lib/less/js/lib/less/index.js +70 -63
  31. data/lib/less/js/lib/less/join-selector-visitor.js +37 -0
  32. data/lib/less/js/lib/less/lessc_helper.js +13 -4
  33. data/lib/less/js/lib/less/parser.js +353 -264
  34. data/lib/less/js/lib/less/rhino.js +5 -2
  35. data/lib/less/js/lib/less/tree.js +1 -1
  36. data/lib/less/js/lib/less/tree/alpha.js +7 -3
  37. data/lib/less/js/lib/less/tree/anonymous.js +1 -0
  38. data/lib/less/js/lib/less/tree/assignment.js +4 -0
  39. data/lib/less/js/lib/less/tree/call.js +14 -8
  40. data/lib/less/js/lib/less/tree/color.js +50 -5
  41. data/lib/less/js/lib/less/tree/comment.js +1 -0
  42. data/lib/less/js/lib/less/tree/condition.js +35 -28
  43. data/lib/less/js/lib/less/tree/dimension.js +270 -16
  44. data/lib/less/js/lib/less/tree/directive.js +7 -2
  45. data/lib/less/js/lib/less/tree/element.js +57 -21
  46. data/lib/less/js/lib/less/tree/expression.js +29 -4
  47. data/lib/less/js/lib/less/tree/extend.js +43 -0
  48. data/lib/less/js/lib/less/tree/import.js +49 -28
  49. data/lib/less/js/lib/less/tree/javascript.js +1 -0
  50. data/lib/less/js/lib/less/tree/keyword.js +3 -2
  51. data/lib/less/js/lib/less/tree/media.js +20 -4
  52. data/lib/less/js/lib/less/tree/mixin.js +38 -18
  53. data/lib/less/js/lib/less/tree/negative.js +22 -0
  54. data/lib/less/js/lib/less/tree/operation.js +32 -17
  55. data/lib/less/js/lib/less/tree/paren.js +5 -1
  56. data/lib/less/js/lib/less/tree/quoted.js +5 -3
  57. data/lib/less/js/lib/less/tree/rule.js +44 -31
  58. data/lib/less/js/lib/less/tree/ruleset.js +50 -23
  59. data/lib/less/js/lib/less/tree/selector.js +49 -39
  60. data/lib/less/js/lib/less/tree/unicode-descriptor.js +1 -0
  61. data/lib/less/js/lib/less/tree/url.js +9 -5
  62. data/lib/less/js/lib/less/tree/value.js +4 -1
  63. data/lib/less/js/lib/less/tree/variable.js +4 -3
  64. data/lib/less/js/lib/less/visitor.js +54 -0
  65. data/lib/less/js/package.json +69 -19
  66. data/lib/less/js/test/browser-test-prepare.js +23 -6
  67. data/lib/less/js/test/browser/common.js +55 -3
  68. data/lib/less/js/test/browser/css/urls.css +13 -0
  69. data/lib/less/js/test/browser/less/relative-urls/urls.less +1 -1
  70. data/lib/less/js/test/browser/less/urls.less +16 -0
  71. data/lib/less/js/test/browser/phantom-runner.js +7 -5
  72. data/lib/less/js/test/browser/runner-browser.js +5 -1
  73. data/lib/less/js/test/browser/runner-errors.js +5 -0
  74. data/lib/less/js/test/browser/runner-legacy.js +6 -0
  75. data/lib/less/js/test/browser/runner-production.js +7 -0
  76. data/lib/less/js/test/browser/template.htm +6 -6
  77. data/lib/less/js/test/css/comments.css +1 -0
  78. data/lib/less/js/test/css/compression/compression.css +2 -0
  79. data/lib/less/js/test/css/css-3.css +4 -0
  80. data/lib/less/js/test/css/css.css +9 -3
  81. data/lib/less/js/test/css/extend-chaining.css +72 -0
  82. data/lib/less/js/test/css/extend-clearfix.css +19 -0
  83. data/lib/less/js/test/css/extend-exact.css +37 -0
  84. data/lib/less/js/test/css/extend-media.css +24 -0
  85. data/lib/less/js/test/css/extend-nest.css +57 -0
  86. data/lib/less/js/test/css/extend-selector.css +72 -0
  87. data/lib/less/js/test/css/extend.css +76 -0
  88. data/lib/less/js/test/css/functions.css +28 -0
  89. data/lib/less/js/test/css/import-interpolation.css +6 -0
  90. data/lib/less/js/test/css/import.css +18 -1
  91. data/lib/less/js/test/css/legacy/legacy.css +7 -0
  92. data/lib/less/js/test/css/media.css +9 -1
  93. data/lib/less/js/test/css/mixins-args.css +18 -0
  94. data/lib/less/js/test/css/mixins-guards.css +5 -0
  95. data/lib/less/js/test/css/parens.css +18 -5
  96. data/lib/less/js/test/css/selectors.css +14 -6
  97. data/lib/less/js/test/css/urls.css +17 -0
  98. data/lib/less/js/test/css/variables.css +22 -3
  99. data/lib/less/js/test/data/data-uri-fail.png +0 -0
  100. data/lib/less/js/test/data/image.jpg +0 -0
  101. data/lib/less/js/test/data/page.html +1 -0
  102. data/lib/less/js/test/less-test.js +41 -9
  103. data/lib/less/js/test/less/colors.less +4 -4
  104. data/lib/less/js/test/less/comments.less +2 -2
  105. data/lib/less/js/test/less/compression/compression.less +16 -0
  106. data/lib/less/js/test/less/css-3.less +5 -1
  107. data/lib/less/js/test/less/css.less +9 -3
  108. data/lib/less/js/test/less/errors/add-mixed-units.less +3 -0
  109. data/lib/less/js/test/less/errors/add-mixed-units.txt +2 -0
  110. data/lib/less/js/test/less/errors/add-mixed-units2.less +3 -0
  111. data/lib/less/js/test/less/errors/add-mixed-units2.txt +2 -0
  112. data/lib/less/js/test/less/errors/bad-variable-declaration1.txt +1 -1
  113. data/lib/less/js/test/less/errors/color-operation-error.less +3 -0
  114. data/lib/less/js/test/less/errors/color-operation-error.txt +2 -0
  115. data/lib/less/js/test/less/errors/comment-in-selector.txt +1 -1
  116. data/lib/less/js/test/less/errors/divide-mixed-units.less +3 -0
  117. data/lib/less/js/test/less/errors/divide-mixed-units.txt +4 -0
  118. data/lib/less/js/test/less/errors/extend-no-selector.less +3 -0
  119. data/lib/less/js/test/less/errors/extend-no-selector.txt +3 -0
  120. data/lib/less/js/test/less/errors/extend-not-at-end.less +3 -0
  121. data/lib/less/js/test/less/errors/extend-not-at-end.txt +3 -0
  122. data/lib/less/js/test/less/errors/import-missing.less +5 -0
  123. data/lib/less/js/test/less/errors/import-missing.txt +3 -3
  124. data/lib/less/js/test/less/errors/import-no-semi.txt +1 -1
  125. data/lib/less/js/test/less/errors/import-subfolder1.txt +1 -1
  126. data/lib/less/js/test/less/errors/import-subfolder2.txt +1 -1
  127. data/lib/less/js/test/less/errors/javascript-error.txt +1 -1
  128. data/lib/less/js/test/less/errors/mixed-mixin-definition-args-1.txt +1 -1
  129. data/lib/less/js/test/less/errors/mixed-mixin-definition-args-2.txt +1 -1
  130. data/lib/less/js/test/less/errors/mixin-not-defined.txt +1 -1
  131. data/lib/less/js/test/less/errors/mixin-not-matched.txt +1 -1
  132. data/lib/less/js/test/less/errors/mixin-not-matched2.txt +1 -1
  133. data/lib/less/js/test/less/errors/multiply-mixed-units.less +7 -0
  134. data/lib/less/js/test/less/errors/multiply-mixed-units.txt +4 -0
  135. data/lib/less/js/test/less/errors/parens-error-1.less +3 -0
  136. data/lib/less/js/test/less/errors/parens-error-1.txt +4 -0
  137. data/lib/less/js/test/less/errors/parens-error-2.less +3 -0
  138. data/lib/less/js/test/less/errors/parens-error-2.txt +4 -0
  139. data/lib/less/js/test/less/errors/parens-error-3.less +3 -0
  140. data/lib/less/js/test/less/errors/parens-error-3.txt +4 -0
  141. data/lib/less/js/test/less/errors/parse-error-curly-bracket.txt +1 -1
  142. data/lib/less/js/test/less/errors/parse-error-missing-bracket.txt +2 -1
  143. data/lib/less/js/test/less/errors/parse-error-with-import.txt +1 -1
  144. data/lib/less/js/test/less/errors/property-ie5-hack.txt +1 -1
  145. data/lib/less/js/test/less/errors/property-in-root.less +4 -0
  146. data/lib/less/js/test/less/errors/property-in-root.txt +4 -0
  147. data/lib/less/js/test/less/errors/property-in-root2.less +1 -0
  148. data/lib/less/js/test/less/errors/property-in-root2.txt +4 -0
  149. data/lib/less/js/test/less/errors/property-in-root3.less +4 -0
  150. data/lib/less/js/test/less/errors/property-in-root3.txt +3 -0
  151. data/lib/less/js/test/less/errors/recursive-variable.txt +1 -1
  152. data/lib/less/js/test/less/extend-chaining.less +79 -0
  153. data/lib/less/js/test/less/extend-clearfix.less +19 -0
  154. data/lib/less/js/test/less/extend-exact.less +46 -0
  155. data/lib/less/js/test/less/extend-media.less +24 -0
  156. data/lib/less/js/test/less/extend-nest.less +65 -0
  157. data/lib/less/js/test/less/extend-selector.less +84 -0
  158. data/lib/less/js/test/less/extend.less +81 -0
  159. data/lib/less/js/test/less/functions.less +37 -6
  160. data/lib/less/js/test/less/import-interpolation.less +8 -0
  161. data/lib/less/js/test/less/import-once.less +4 -4
  162. data/lib/less/js/test/less/import.less +11 -2
  163. data/lib/less/js/test/less/import/deeper/import-once-test-a.less +1 -1
  164. data/lib/less/js/test/less/import/import-interpolation.less +1 -0
  165. data/lib/less/js/test/less/import/import-interpolation2.less +5 -0
  166. data/lib/less/js/test/less/javascript.less +1 -1
  167. data/lib/less/js/test/less/legacy/legacy.less +7 -0
  168. data/lib/less/js/test/less/media.less +14 -3
  169. data/lib/less/js/test/less/mixins-args.less +43 -5
  170. data/lib/less/js/test/less/mixins-guards.less +13 -0
  171. data/lib/less/js/test/less/mixins-named-args.less +5 -5
  172. data/lib/less/js/test/less/mixins-nested.less +2 -2
  173. data/lib/less/js/test/less/mixins-pattern.less +1 -1
  174. data/lib/less/js/test/less/mixins.less +1 -1
  175. data/lib/less/js/test/less/operations.less +27 -27
  176. data/lib/less/js/test/less/parens.less +20 -5
  177. data/lib/less/js/test/less/selectors.less +14 -7
  178. data/lib/less/js/test/less/urls.less +24 -0
  179. data/lib/less/js/test/less/variables.less +42 -12
  180. data/lib/less/loader.rb +33 -0
  181. data/lib/less/version.rb +1 -1
  182. data/spec/less/parser_spec.rb +5 -5
  183. metadata +76 -6
  184. data/lib/less/js/build/ecma-5.js +0 -120
  185. data/lib/less/js/lib/less/tree/ratio.js +0 -13
@@ -5,6 +5,8 @@ function loadStyleSheet(sheet, callback, reload, remaining) {
5
5
  sheetName = name.slice(0, endOfPath + 1) + sheet.href,
6
6
  contents = sheet.contents || {},
7
7
  input = readFile(sheetName);
8
+
9
+ input = input.replace(/^\xEF\xBB\xBF/, '');
8
10
 
9
11
  contents[sheetName] = input;
10
12
 
@@ -35,7 +37,8 @@ function writeFile(filename, content) {
35
37
  (function (args) {
36
38
  var output,
37
39
  compress = false,
38
- i;
40
+ i,
41
+ path;
39
42
 
40
43
  for(i = 0; i < args.length; i++) {
41
44
  switch(args[i]) {
@@ -119,5 +122,5 @@ function error(e, filename) {
119
122
  errorline(e, 1);
120
123
  errorline(e, 2);
121
124
  }
122
- print(content);
125
+ print(content);
123
126
  }
@@ -24,7 +24,7 @@ tree.debugInfo.asComment = function(ctx) {
24
24
 
25
25
  tree.debugInfo.asMediaQuery = function(ctx) {
26
26
  return '@media -sass-debug-info{filename{font-family:' +
27
- ('file://' + ctx.debugInfo.fileName).replace(/[\/:.]/g, '\\$&') +
27
+ ('file://' + ctx.debugInfo.fileName).replace(/([.:/\\])/g, function(a){if(a=='\\') a = '\/'; return '\\' + a}) +
28
28
  '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n';
29
29
  };
30
30
 
@@ -4,13 +4,17 @@ tree.Alpha = function (val) {
4
4
  this.value = val;
5
5
  };
6
6
  tree.Alpha.prototype = {
7
- toCSS: function () {
8
- return "alpha(opacity=" +
9
- (this.value.toCSS ? this.value.toCSS() : this.value) + ")";
7
+ type: "Alpha",
8
+ accept: function (visitor) {
9
+ this.value = visitor.visit(this.value);
10
10
  },
11
11
  eval: function (env) {
12
12
  if (this.value.eval) { this.value = this.value.eval(env) }
13
13
  return this;
14
+ },
15
+ toCSS: function () {
16
+ return "alpha(opacity=" +
17
+ (this.value.toCSS ? this.value.toCSS() : this.value) + ")";
14
18
  }
15
19
  };
16
20
 
@@ -4,6 +4,7 @@ tree.Anonymous = function (string) {
4
4
  this.value = string.value || string;
5
5
  };
6
6
  tree.Anonymous.prototype = {
7
+ type: "Anonymous",
7
8
  toCSS: function () {
8
9
  return this.value;
9
10
  },
@@ -5,6 +5,10 @@ tree.Assignment = function (key, val) {
5
5
  this.value = val;
6
6
  };
7
7
  tree.Assignment.prototype = {
8
+ type: "Assignment",
9
+ accept: function (visitor) {
10
+ this.value = visitor.visit(this.value);
11
+ },
8
12
  toCSS: function () {
9
13
  return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value);
10
14
  },
@@ -3,13 +3,17 @@
3
3
  //
4
4
  // A function call node.
5
5
  //
6
- tree.Call = function (name, args, index, filename) {
6
+ tree.Call = function (name, args, index, currentFileInfo) {
7
7
  this.name = name;
8
8
  this.args = args;
9
9
  this.index = index;
10
- this.filename = filename;
10
+ this.currentFileInfo = currentFileInfo;
11
11
  };
12
12
  tree.Call.prototype = {
13
+ type: "Call",
14
+ accept: function (visitor) {
15
+ this.args = visitor.visit(this.args);
16
+ },
13
17
  //
14
18
  // When evaluating a function call,
15
19
  // we either find the function in `tree.functions` [1],
@@ -24,12 +28,14 @@ tree.Call.prototype = {
24
28
  // The function should receive the value, not the variable.
25
29
  //
26
30
  eval: function (env) {
27
- var args = this.args.map(function (a) { return a.eval(env) }),
28
- result;
31
+ var args = this.args.map(function (a) { return a.eval(env); }),
32
+ nameLC = this.name.toLowerCase(),
33
+ result, func;
29
34
 
30
- if (this.name in tree.functions) { // 1.
35
+ if (nameLC in tree.functions) { // 1.
31
36
  try {
32
- result = tree.functions[this.name].apply(tree.functions, args);
37
+ func = new tree.functionCall(env, this.currentFileInfo);
38
+ result = func[nameLC].apply(func, args);
33
39
  if (result != null) {
34
40
  return result;
35
41
  }
@@ -37,13 +43,13 @@ tree.Call.prototype = {
37
43
  throw { type: e.type || "Runtime",
38
44
  message: "error evaluating function `" + this.name + "`" +
39
45
  (e.message ? ': ' + e.message : ''),
40
- index: this.index, filename: this.filename };
46
+ index: this.index, filename: this.currentFileInfo.filename };
41
47
  }
42
48
  }
43
49
 
44
50
  // 2.
45
51
  return new(tree.Anonymous)(this.name +
46
- "(" + args.map(function (a) { return a.toCSS(env) }).join(', ') + ")");
52
+ "(" + args.map(function (a) { return a.toCSS(env); }).join(', ') + ")");
47
53
  },
48
54
 
49
55
  toCSS: function (env) {
@@ -23,7 +23,9 @@ tree.Color = function (rgb, a) {
23
23
  this.alpha = typeof(a) === 'number' ? a : 1;
24
24
  };
25
25
  tree.Color.prototype = {
26
+ type: "Color",
26
27
  eval: function () { return this },
28
+ luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); },
27
29
 
28
30
  //
29
31
  // If we have some transparency, the only way to represent it
@@ -31,17 +33,31 @@ tree.Color.prototype = {
31
33
  // which has better compatibility with older browsers.
32
34
  // Values are capped between `0` and `255`, rounded and zero-padded.
33
35
  //
34
- toCSS: function () {
36
+ toCSS: function (env, doNotCompress) {
37
+ var compress = env && env.compress && !doNotCompress;
35
38
  if (this.alpha < 1.0) {
36
39
  return "rgba(" + this.rgb.map(function (c) {
37
40
  return Math.round(c);
38
- }).concat(this.alpha).join(', ') + ")";
41
+ }).concat(this.alpha).join(',' + (compress ? '' : ' ')) + ")";
39
42
  } else {
40
- return '#' + this.rgb.map(function (i) {
43
+ var color = this.rgb.map(function (i) {
41
44
  i = Math.round(i);
42
45
  i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16);
43
46
  return i.length === 1 ? '0' + i : i;
44
47
  }).join('');
48
+
49
+ if (compress) {
50
+ color = color.split('');
51
+
52
+ // Convert color to short format
53
+ if (color[0] == color[1] && color[2] == color[3] && color[4] == color[5]) {
54
+ color = color[0] + color[2] + color[4];
55
+ } else {
56
+ color = color.join('');
57
+ }
58
+ }
59
+
60
+ return '#' + color;
45
61
  }
46
62
  },
47
63
 
@@ -51,7 +67,7 @@ tree.Color.prototype = {
51
67
  // our result, in the form of an integer triplet,
52
68
  // we create a new Color node to hold the result.
53
69
  //
54
- operate: function (op, other) {
70
+ operate: function (env, op, other) {
55
71
  var result = [];
56
72
 
57
73
  if (! (other instanceof tree.Color)) {
@@ -59,7 +75,7 @@ tree.Color.prototype = {
59
75
  }
60
76
 
61
77
  for (var c = 0; c < 3; c++) {
62
- result[c] = tree.operate(op, this.rgb[c], other.rgb[c]);
78
+ result[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]);
63
79
  }
64
80
  return new(tree.Color)(result, this.alpha + other.alpha);
65
81
  },
@@ -87,6 +103,35 @@ tree.Color.prototype = {
87
103
  }
88
104
  return { h: h * 360, s: s, l: l, a: a };
89
105
  },
106
+ //Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
107
+ toHSV: function () {
108
+ var r = this.rgb[0] / 255,
109
+ g = this.rgb[1] / 255,
110
+ b = this.rgb[2] / 255,
111
+ a = this.alpha;
112
+
113
+ var max = Math.max(r, g, b), min = Math.min(r, g, b);
114
+ var h, s, v = max;
115
+
116
+ var d = max - min;
117
+ if (max === 0) {
118
+ s = 0;
119
+ } else {
120
+ s = d / max;
121
+ }
122
+
123
+ if (max === min) {
124
+ h = 0;
125
+ } else {
126
+ switch(max){
127
+ case r: h = (g - b) / d + (g < b ? 6 : 0); break;
128
+ case g: h = (b - r) / d + 2; break;
129
+ case b: h = (r - g) / d + 4; break;
130
+ }
131
+ h /= 6;
132
+ }
133
+ return { h: h * 360, s: s, v: v, a: a };
134
+ },
90
135
  toARGB: function () {
91
136
  var argb = [Math.round(this.alpha * 255)].concat(this.rgb);
92
137
  return '#' + argb.map(function (i) {
@@ -5,6 +5,7 @@ tree.Comment = function (value, silent) {
5
5
  this.silent = !!silent;
6
6
  };
7
7
  tree.Comment.prototype = {
8
+ type: "Comment",
8
9
  toCSS: function (env) {
9
10
  return env.compress ? '' : this.value;
10
11
  },
@@ -7,36 +7,43 @@ tree.Condition = function (op, l, r, i, negate) {
7
7
  this.index = i;
8
8
  this.negate = negate;
9
9
  };
10
- tree.Condition.prototype.eval = function (env) {
11
- var a = this.lvalue.eval(env),
12
- b = this.rvalue.eval(env);
10
+ tree.Condition.prototype = {
11
+ type: "Condition",
12
+ accept: function (visitor) {
13
+ this.lvalue = visitor.visit(this.lvalue);
14
+ this.rvalue = visitor.visit(this.rvalue);
15
+ },
16
+ eval: function (env) {
17
+ var a = this.lvalue.eval(env),
18
+ b = this.rvalue.eval(env);
13
19
 
14
- var i = this.index, result;
20
+ var i = this.index, result;
15
21
 
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;
22
+ var result = (function (op) {
23
+ switch (op) {
24
+ case 'and':
25
+ return a && b;
26
+ case 'or':
27
+ return a || b;
28
+ default:
29
+ if (a.compare) {
30
+ result = a.compare(b);
31
+ } else if (b.compare) {
32
+ result = b.compare(a);
33
+ } else {
34
+ throw { type: "Type",
35
+ message: "Unable to perform comparison",
36
+ index: i };
37
+ }
38
+ switch (result) {
39
+ case -1: return op === '<' || op === '=<';
40
+ case 0: return op === '=' || op === '>=' || op === '=<';
41
+ case 1: return op === '>' || op === '>=';
42
+ }
43
+ }
44
+ })(this.op);
45
+ return this.negate ? !result : result;
46
+ }
40
47
  };
41
48
 
42
49
  })(require('../tree'));
@@ -5,39 +5,95 @@
5
5
  //
6
6
  tree.Dimension = function (value, unit) {
7
7
  this.value = parseFloat(value);
8
- this.unit = unit || null;
8
+ this.unit = (unit && unit instanceof tree.Unit) ? unit :
9
+ new(tree.Unit)(unit ? [unit] : undefined);
9
10
  };
10
11
 
11
12
  tree.Dimension.prototype = {
12
- eval: function () { return this },
13
+ type: "Dimension",
14
+ accept: function (visitor) {
15
+ this.unit = visitor.visit(this.unit);
16
+ },
17
+ eval: function (env) {
18
+ return this;
19
+ },
13
20
  toColor: function () {
14
21
  return new(tree.Color)([this.value, this.value, this.value]);
15
22
  },
16
- toCSS: function () {
17
- var css = this.value + this.unit;
18
- return css;
23
+ toCSS: function (env) {
24
+ if ((env && env.strictUnits) && !this.unit.isSingular()) {
25
+ throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());
26
+ }
27
+
28
+ var value = this.value,
29
+ strValue = String(value);
30
+
31
+ if (value !== 0 && value < 0.000001 && value > -0.000001) {
32
+ // would be output 1e-6 etc.
33
+ strValue = value.toFixed(20).replace(/0+$/, "");
34
+ }
35
+
36
+ if (env && env.compress) {
37
+ // Zero values doesn't need a unit
38
+ if (value === 0 && !this.unit.isAngle()) {
39
+ return strValue;
40
+ }
41
+
42
+ // Float values doesn't need a leading zero
43
+ if (value > 0 && value < 1) {
44
+ strValue = (strValue).substr(1);
45
+ }
46
+ }
47
+
48
+ return strValue + this.unit.toCSS(env);
19
49
  },
20
50
 
21
51
  // In an operation between two Dimensions,
22
52
  // we default to the first Dimension's unit,
23
- // so `1px + 2em` will yield `3px`.
24
- // In the future, we could implement some unit
25
- // conversions such that `100cm + 10mm` would yield
26
- // `101cm`.
27
- operate: function (op, other) {
28
- return new(tree.Dimension)
29
- (tree.operate(op, this.value, other.value),
30
- this.unit || other.unit);
53
+ // so `1px + 2` will yield `3px`.
54
+ operate: function (env, op, other) {
55
+ var value = tree.operate(env, op, this.value, other.value),
56
+ unit = this.unit.clone();
57
+
58
+ if (op === '+' || op === '-') {
59
+ if (unit.numerator.length === 0 && unit.denominator.length === 0) {
60
+ unit.numerator = other.unit.numerator.slice(0);
61
+ unit.denominator = other.unit.denominator.slice(0);
62
+ } else if (other.unit.numerator.length == 0 && unit.denominator.length == 0) {
63
+ // do nothing
64
+ } else {
65
+ other = other.convertTo(this.unit.usedUnits());
66
+
67
+ if(env.strictUnits && other.unit.toString() !== unit.toString()) {
68
+ throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() +
69
+ "' and '" + other.unit.toString() + "'.");
70
+ }
71
+
72
+ value = tree.operate(env, op, this.value, other.value);
73
+ }
74
+ } else if (op === '*') {
75
+ unit.numerator = unit.numerator.concat(other.unit.numerator).sort();
76
+ unit.denominator = unit.denominator.concat(other.unit.denominator).sort();
77
+ unit.cancel();
78
+ } else if (op === '/') {
79
+ unit.numerator = unit.numerator.concat(other.unit.denominator).sort();
80
+ unit.denominator = unit.denominator.concat(other.unit.numerator).sort();
81
+ unit.cancel();
82
+ }
83
+ return new(tree.Dimension)(value, unit);
31
84
  },
32
85
 
33
86
  compare: function (other) {
34
87
  if (other instanceof tree.Dimension) {
35
- if (other.value > this.value) {
88
+ var a = this.unify(), b = other.unify(),
89
+ aValue = a.value, bValue = b.value;
90
+
91
+ if (bValue > aValue) {
36
92
  return -1;
37
- } else if (other.value < this.value) {
93
+ } else if (bValue < aValue) {
38
94
  return 1;
39
95
  } else {
40
- if (other.unit && this.unit !== other.unit) {
96
+ if (!b.unit.isEmpty() && a.unit.compare(b.unit) !== 0) {
41
97
  return -1;
42
98
  }
43
99
  return 0;
@@ -45,7 +101,205 @@ tree.Dimension.prototype = {
45
101
  } else {
46
102
  return -1;
47
103
  }
104
+ },
105
+
106
+ unify: function () {
107
+ return this.convertTo({ length: 'm', duration: 's', angle: 'rad' });
108
+ },
109
+
110
+ convertTo: function (conversions) {
111
+ var value = this.value, unit = this.unit.clone(),
112
+ i, groupName, group, conversion, targetUnit, derivedConversions = {};
113
+
114
+ if (typeof conversions === 'string') {
115
+ for(i in tree.UnitConversions) {
116
+ if (tree.UnitConversions[i].hasOwnProperty(conversions)) {
117
+ derivedConversions = {};
118
+ derivedConversions[i] = conversions;
119
+ }
120
+ }
121
+ conversions = derivedConversions;
122
+ }
123
+
124
+ for (groupName in conversions) {
125
+ if (conversions.hasOwnProperty(groupName)) {
126
+ targetUnit = conversions[groupName];
127
+ group = tree.UnitConversions[groupName];
128
+
129
+ unit.map(function (atomicUnit, denominator) {
130
+ if (group.hasOwnProperty(atomicUnit)) {
131
+ if (denominator) {
132
+ value = value / (group[atomicUnit] / group[targetUnit]);
133
+ } else {
134
+ value = value * (group[atomicUnit] / group[targetUnit]);
135
+ }
136
+
137
+ return targetUnit;
138
+ }
139
+
140
+ return atomicUnit;
141
+ });
142
+ }
143
+ }
144
+
145
+ unit.cancel();
146
+
147
+ return new(tree.Dimension)(value, unit);
148
+ }
149
+ };
150
+
151
+ // http://www.w3.org/TR/css3-values/#absolute-lengths
152
+ tree.UnitConversions = {
153
+ length: {
154
+ 'm': 1,
155
+ 'cm': 0.01,
156
+ 'mm': 0.001,
157
+ 'in': 0.0254,
158
+ 'pt': 0.0254 / 72,
159
+ 'pc': 0.0254 / 72 * 12
160
+ },
161
+ duration: {
162
+ 's': 1,
163
+ 'ms': 0.001
164
+ },
165
+ angle: {
166
+ 'rad': 1/(2*Math.PI),
167
+ 'deg': 1/360,
168
+ 'grad': 1/400,
169
+ 'turn': 1
170
+ }
171
+ };
172
+
173
+ tree.Unit = function (numerator, denominator, backupUnit) {
174
+ this.numerator = numerator ? numerator.slice(0).sort() : [];
175
+ this.denominator = denominator ? denominator.slice(0).sort() : [];
176
+ this.backupUnit = backupUnit;
177
+ };
178
+
179
+ tree.Unit.prototype = {
180
+ type: "Unit",
181
+ clone: function () {
182
+ return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit);
183
+ },
184
+
185
+ toCSS: function (env) {
186
+ if (this.numerator.length >= 1) {
187
+ return this.numerator[0];
188
+ }
189
+ if (this.denominator.length >= 1) {
190
+ return this.denominator[0];
191
+ }
192
+ if ((!env || !env.strictUnits) && this.backupUnit) {
193
+ return this.backupUnit;
194
+ }
195
+ return "";
196
+ },
197
+
198
+ toString: function () {
199
+ var i, returnStr = this.numerator.join("*");
200
+ for (i = 0; i < this.denominator.length; i++) {
201
+ returnStr += "/" + this.denominator[i];
202
+ }
203
+ return returnStr;
204
+ },
205
+
206
+ compare: function (other) {
207
+ return this.is(other.toString()) ? 0 : -1;
208
+ },
209
+
210
+ is: function (unitString) {
211
+ return this.toString() === unitString;
212
+ },
213
+
214
+ isAngle: function () {
215
+ return tree.UnitConversions.angle.hasOwnProperty(this.toCSS());
216
+ },
217
+
218
+ isEmpty: function () {
219
+ return this.numerator.length == 0 && this.denominator.length == 0;
220
+ },
221
+
222
+ isSingular: function() {
223
+ return this.numerator.length <= 1 && this.denominator.length == 0;
224
+ },
225
+
226
+ map: function(callback) {
227
+ var i;
228
+
229
+ for (i = 0; i < this.numerator.length; i++) {
230
+ this.numerator[i] = callback(this.numerator[i], false);
231
+ }
232
+
233
+ for (i = 0; i < this.denominator.length; i++) {
234
+ this.denominator[i] = callback(this.denominator[i], true);
235
+ }
236
+ },
237
+
238
+ usedUnits: function() {
239
+ var group, groupName, result = {};
240
+
241
+ for (groupName in tree.UnitConversions) {
242
+ if (tree.UnitConversions.hasOwnProperty(groupName)) {
243
+ group = tree.UnitConversions[groupName];
244
+
245
+ this.map(function (atomicUnit) {
246
+ if (group.hasOwnProperty(atomicUnit) && !result[groupName]) {
247
+ result[groupName] = atomicUnit;
248
+ }
249
+
250
+ return atomicUnit;
251
+ });
252
+ }
253
+ }
254
+
255
+ return result;
256
+ },
257
+
258
+ cancel: function () {
259
+ var counter = {}, atomicUnit, i, backup;
260
+
261
+ for (i = 0; i < this.numerator.length; i++) {
262
+ atomicUnit = this.numerator[i];
263
+ if (!backup) {
264
+ backup = atomicUnit;
265
+ }
266
+ counter[atomicUnit] = (counter[atomicUnit] || 0) + 1;
267
+ }
268
+
269
+ for (i = 0; i < this.denominator.length; i++) {
270
+ atomicUnit = this.denominator[i];
271
+ if (!backup) {
272
+ backup = atomicUnit;
273
+ }
274
+ counter[atomicUnit] = (counter[atomicUnit] || 0) - 1;
48
275
  }
276
+
277
+ this.numerator = [];
278
+ this.denominator = [];
279
+
280
+ for (atomicUnit in counter) {
281
+ if (counter.hasOwnProperty(atomicUnit)) {
282
+ var count = counter[atomicUnit];
283
+
284
+ if (count > 0) {
285
+ for (i = 0; i < count; i++) {
286
+ this.numerator.push(atomicUnit);
287
+ }
288
+ } else if (count < 0) {
289
+ for (i = 0; i < -count; i++) {
290
+ this.denominator.push(atomicUnit);
291
+ }
292
+ }
293
+ }
294
+ }
295
+
296
+ if (this.numerator.length === 0 && this.denominator.length === 0 && backup) {
297
+ this.backupUnit = backup;
298
+ }
299
+
300
+ this.numerator.sort();
301
+ this.denominator.sort();
302
+ }
49
303
  };
50
304
 
51
305
  })(require('../tree'));