less 2.3.0 → 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (172) hide show
  1. checksums.yaml +4 -4
  2. data/lib/less/js/.gitignore +7 -0
  3. data/lib/less/js/.npmignore +1 -0
  4. data/lib/less/js/CHANGELOG.md +118 -0
  5. data/lib/less/js/CONTRIBUTING.md +50 -0
  6. data/lib/less/js/Makefile +14 -2
  7. data/lib/less/js/bin/lessc +72 -21
  8. data/lib/less/js/dist/less-1.3.1.js +4011 -0
  9. data/lib/less/js/dist/less-1.3.1.min.js +9 -0
  10. data/lib/less/js/dist/less-1.3.2.js +4401 -0
  11. data/lib/less/js/dist/less-1.3.2.min.js +9 -0
  12. data/lib/less/js/dist/less-1.3.3.js +4413 -0
  13. data/lib/less/js/dist/less-1.3.3.min.js +9 -0
  14. data/lib/less/js/dist/less-rhino-1.3.1.js +3725 -0
  15. data/lib/less/js/dist/less-rhino-1.3.2.js +3990 -0
  16. data/lib/less/js/dist/less-rhino-1.3.3.js +4002 -0
  17. data/lib/less/js/lib/less/browser.js +192 -53
  18. data/lib/less/js/lib/less/colors.js +1 -0
  19. data/lib/less/js/lib/less/functions.js +159 -10
  20. data/lib/less/js/lib/less/index.js +124 -56
  21. data/lib/less/js/lib/less/lessc_helper.js +62 -0
  22. data/lib/less/js/lib/less/parser.js +352 -135
  23. data/lib/less/js/lib/less/rhino.js +84 -23
  24. data/lib/less/js/lib/less/tree.js +28 -0
  25. data/lib/less/js/lib/less/tree/anonymous.js +15 -1
  26. data/lib/less/js/lib/less/tree/assignment.js +3 -1
  27. data/lib/less/js/lib/less/tree/call.js +12 -6
  28. data/lib/less/js/lib/less/tree/color.js +10 -0
  29. data/lib/less/js/lib/less/tree/dimension.js +3 -1
  30. data/lib/less/js/lib/less/tree/directive.js +9 -5
  31. data/lib/less/js/lib/less/tree/element.js +8 -6
  32. data/lib/less/js/lib/less/tree/import.js +16 -13
  33. data/lib/less/js/lib/less/tree/media.js +16 -9
  34. data/lib/less/js/lib/less/tree/mixin.js +123 -46
  35. data/lib/less/js/lib/less/tree/operation.js +5 -0
  36. data/lib/less/js/lib/less/tree/quoted.js +15 -1
  37. data/lib/less/js/lib/less/tree/ratio.js +13 -0
  38. data/lib/less/js/lib/less/tree/rule.js +7 -0
  39. data/lib/less/js/lib/less/tree/ruleset.js +232 -34
  40. data/lib/less/js/lib/less/tree/selector.js +21 -11
  41. data/lib/less/js/lib/less/tree/unicode-descriptor.js +13 -0
  42. data/lib/less/js/lib/less/tree/url.js +16 -14
  43. data/lib/less/js/lib/less/tree/variable.js +13 -1
  44. data/lib/less/js/package.json +13 -3
  45. data/lib/less/js/test/browser-test-prepare.js +29 -0
  46. data/lib/less/js/test/browser/common.js +74 -0
  47. data/lib/less/js/test/browser/css/relative-urls/urls.css +36 -0
  48. data/lib/less/js/test/browser/css/rootpath-relative/urls.css +36 -0
  49. data/lib/less/js/test/browser/css/rootpath/urls.css +36 -0
  50. data/lib/less/js/test/browser/css/urls.css +36 -0
  51. data/lib/less/js/test/browser/jasmine-html.js +681 -0
  52. data/lib/less/js/test/browser/jasmine.css +82 -0
  53. data/lib/less/js/test/browser/jasmine.js +2600 -0
  54. data/lib/less/js/test/browser/less/imports/urls.less +4 -0
  55. data/lib/less/js/test/browser/less/imports/urls2.less +4 -0
  56. data/lib/less/js/test/browser/less/relative-urls/urls.less +33 -0
  57. data/lib/less/js/test/browser/less/rootpath-relative/urls.less +33 -0
  58. data/lib/less/js/test/browser/less/rootpath/urls.less +33 -0
  59. data/lib/less/js/test/browser/less/urls.less +33 -0
  60. data/lib/less/js/test/browser/phantom-runner.js +139 -0
  61. data/lib/less/js/test/browser/runner-browser.js +3 -0
  62. data/lib/less/js/test/browser/runner-main.js +15 -0
  63. data/lib/less/js/test/browser/runner-relative-urls.js +4 -0
  64. data/lib/less/js/test/browser/runner-rootpath-relative.js +5 -0
  65. data/lib/less/js/test/browser/runner-rootpath.js +4 -0
  66. data/lib/less/js/test/browser/template.htm +10 -0
  67. data/lib/less/js/test/css/charsets.css +1 -0
  68. data/lib/less/js/test/css/colors.css +22 -0
  69. data/lib/less/js/test/css/comments.css +7 -0
  70. data/lib/less/js/test/css/css-3.css +57 -2
  71. data/lib/less/js/test/css/css-escapes.css +4 -0
  72. data/lib/less/js/test/css/css.css +11 -11
  73. data/lib/less/js/test/css/debug/linenumbers-all.css +43 -0
  74. data/lib/less/js/test/css/debug/linenumbers-comments.css +35 -0
  75. data/lib/less/js/test/css/debug/linenumbers-mediaquery.css +35 -0
  76. data/lib/less/js/test/css/functions.css +59 -2
  77. data/lib/less/js/test/css/ie-filters.css +7 -3
  78. data/lib/less/js/test/css/import-once.css +3 -0
  79. data/lib/less/js/test/css/import.css +7 -9
  80. data/lib/less/js/test/css/javascript.css +3 -2
  81. data/lib/less/js/test/css/media.css +116 -0
  82. data/lib/less/js/test/css/mixins-args.css +23 -4
  83. data/lib/less/js/test/css/mixins-guards.css +13 -0
  84. data/lib/less/js/test/css/mixins-important.css +21 -0
  85. data/lib/less/js/test/css/mixins-named-args.css +27 -0
  86. data/lib/less/js/test/css/mixins.css +50 -0
  87. data/lib/less/js/test/css/scope.css +20 -0
  88. data/lib/less/js/test/css/selectors.css +64 -0
  89. data/lib/less/js/test/css/static-urls/urls.css +42 -0
  90. data/lib/less/js/test/css/strings.css +2 -2
  91. data/lib/less/js/test/css/urls.css +42 -0
  92. data/lib/less/js/test/css/variables.css +0 -1
  93. data/lib/less/js/test/css/whitespace.css +4 -0
  94. data/lib/less/js/test/less-test.js +145 -36
  95. data/lib/less/js/test/less/charsets.less +3 -0
  96. data/lib/less/js/test/less/colors.less +27 -0
  97. data/lib/less/js/test/less/comments.less +12 -0
  98. data/lib/less/js/test/less/css-3.less +54 -6
  99. data/lib/less/js/test/less/css-escapes.less +6 -1
  100. data/lib/less/js/test/less/css.less +14 -12
  101. data/lib/less/js/test/less/debug/import/test.less +25 -0
  102. data/lib/less/js/test/less/debug/linenumbers.less +23 -0
  103. data/lib/less/js/test/less/errors/bad-variable-declaration1.less +1 -0
  104. data/lib/less/js/test/less/errors/bad-variable-declaration1.txt +2 -0
  105. data/lib/less/js/test/less/errors/comment-in-selector.less +1 -0
  106. data/lib/less/js/test/less/errors/comment-in-selector.txt +2 -0
  107. data/lib/less/js/test/less/errors/import-missing.less +1 -0
  108. data/lib/less/js/test/less/errors/import-missing.txt +3 -0
  109. data/lib/less/js/test/less/errors/import-no-semi.less +1 -0
  110. data/lib/less/js/test/less/errors/import-no-semi.txt +2 -0
  111. data/lib/less/js/test/less/errors/import-subfolder1.less +1 -0
  112. data/lib/less/js/test/less/errors/import-subfolder1.txt +3 -0
  113. data/lib/less/js/test/less/errors/import-subfolder2.less +1 -0
  114. data/lib/less/js/test/less/errors/import-subfolder2.txt +2 -0
  115. data/lib/less/js/test/less/errors/imports/import-subfolder1.less +1 -0
  116. data/lib/less/js/test/less/errors/imports/import-subfolder2.less +1 -0
  117. data/lib/less/js/test/less/errors/imports/import-test.less +4 -0
  118. data/lib/less/js/test/less/errors/imports/subfolder/mixin-not-defined.less +1 -0
  119. data/lib/less/js/test/less/errors/imports/subfolder/parse-error-curly-bracket.less +1 -0
  120. data/lib/less/js/test/less/errors/javascript-error.less +3 -0
  121. data/lib/less/js/test/less/errors/javascript-error.txt +4 -0
  122. data/lib/less/js/test/less/errors/mixed-mixin-definition-args-1.less +6 -0
  123. data/lib/less/js/test/less/errors/mixed-mixin-definition-args-1.txt +4 -0
  124. data/lib/less/js/test/less/errors/mixed-mixin-definition-args-2.less +6 -0
  125. data/lib/less/js/test/less/errors/mixed-mixin-definition-args-2.txt +4 -0
  126. data/lib/less/js/test/less/errors/mixin-not-defined.less +11 -0
  127. data/lib/less/js/test/less/errors/mixin-not-defined.txt +3 -0
  128. data/lib/less/js/test/less/errors/mixin-not-matched.less +6 -0
  129. data/lib/less/js/test/less/errors/mixin-not-matched.txt +3 -0
  130. data/lib/less/js/test/less/errors/mixin-not-matched2.less +6 -0
  131. data/lib/less/js/test/less/errors/mixin-not-matched2.txt +3 -0
  132. data/lib/less/js/test/less/errors/parse-error-curly-bracket.less +1 -0
  133. data/lib/less/js/test/less/errors/parse-error-curly-bracket.txt +2 -0
  134. data/lib/less/js/test/less/errors/parse-error-missing-bracket.less +2 -0
  135. data/lib/less/js/test/less/errors/parse-error-missing-bracket.txt +2 -0
  136. data/lib/less/js/test/less/errors/parse-error-with-import.less +13 -0
  137. data/lib/less/js/test/less/errors/parse-error-with-import.txt +4 -0
  138. data/lib/less/js/test/less/errors/property-ie5-hack.less +3 -0
  139. data/lib/less/js/test/less/errors/property-ie5-hack.txt +4 -0
  140. data/lib/less/js/test/less/errors/recursive-variable.less +1 -0
  141. data/lib/less/js/test/less/errors/recursive-variable.txt +2 -0
  142. data/lib/less/js/test/less/functions.less +64 -2
  143. data/lib/less/js/test/less/ie-filters.less +7 -0
  144. data/lib/less/js/test/less/import-once.less +4 -0
  145. data/lib/less/js/test/less/import.less +2 -1
  146. data/lib/less/js/test/less/import/deeper/import-once-test-a.less +1 -0
  147. data/lib/less/js/test/less/import/import-and-relative-paths-test.less +6 -0
  148. data/lib/less/js/test/less/import/import-charset-test.less +1 -0
  149. data/lib/less/js/test/less/import/import-once-test-c.less +6 -0
  150. data/lib/less/js/test/less/import/import-test-a.less +1 -0
  151. data/lib/less/js/test/less/import/import-test-c.less +0 -1
  152. data/lib/less/js/test/less/import/imports/font.less +8 -0
  153. data/lib/less/js/test/less/import/imports/logo.less +5 -0
  154. data/lib/less/js/test/less/import/urls.less +1 -0
  155. data/lib/less/js/test/less/javascript.less +4 -2
  156. data/lib/less/js/test/less/media.less +120 -0
  157. data/lib/less/js/test/less/mixins-args.less +40 -10
  158. data/lib/less/js/test/less/mixins-guards.less +30 -0
  159. data/lib/less/js/test/less/mixins-important.less +4 -0
  160. data/lib/less/js/test/less/mixins-named-args.less +36 -0
  161. data/lib/less/js/test/less/mixins.less +47 -0
  162. data/lib/less/js/test/less/scope.less +48 -1
  163. data/lib/less/js/test/less/selectors.less +81 -0
  164. data/lib/less/js/test/less/static-urls/urls.less +33 -0
  165. data/lib/less/js/test/less/strings.less +1 -1
  166. data/lib/less/js/test/less/urls.less +33 -0
  167. data/lib/less/js/test/less/variables.less +0 -1
  168. data/lib/less/js/test/less/whitespace.less +7 -0
  169. data/lib/less/version.rb +1 -1
  170. metadata +101 -4
  171. data/lib/less/js/CHANGELOG +0 -41
  172. data/lib/less/js/lib/less/cssmin.js +0 -355
@@ -10,38 +10,66 @@ tree.mixin.Call = function (elements, args, index, filename, important) {
10
10
  };
11
11
  tree.mixin.Call.prototype = {
12
12
  eval: function (env) {
13
- var mixins, args, rules = [], match = false;
13
+ var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound;
14
14
 
15
- for (var i = 0; i < env.frames.length; i++) {
15
+ args = this.arguments && this.arguments.map(function (a) {
16
+ return { name: a.name, value: a.value.eval(env) };
17
+ });
18
+
19
+ for (i = 0; i < env.frames.length; i++) {
16
20
  if ((mixins = env.frames[i].find(this.selector)).length > 0) {
17
- args = this.arguments && this.arguments.map(function (a) { return a.eval(env) });
18
- for (var m = 0; m < mixins.length; m++) {
19
- if (mixins[m].match(args, env)) {
20
- try {
21
- Array.prototype.push.apply(
22
- rules, mixins[m].eval(env, this.arguments, this.important).rules);
23
- match = true;
24
- } catch (e) {
25
- throw { message: e.message, index: this.index, filename: this.filename, stack: e.stack };
21
+ isOneFound = true;
22
+ for (m = 0; m < mixins.length; m++) {
23
+ mixin = mixins[m];
24
+ isRecursive = false;
25
+ for(f = 0; f < env.frames.length; f++) {
26
+ if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) {
27
+ isRecursive = true;
28
+ break;
26
29
  }
27
30
  }
31
+ if (isRecursive) {
32
+ continue;
33
+ }
34
+ if (mixin.matchArgs(args, env)) {
35
+ if (!mixin.matchCondition || mixin.matchCondition(args, env)) {
36
+ try {
37
+ Array.prototype.push.apply(
38
+ rules, mixin.eval(env, args, this.important).rules);
39
+ } catch (e) {
40
+ throw { message: e.message, index: this.index, filename: this.filename, stack: e.stack };
41
+ }
42
+ }
43
+ match = true;
44
+ }
28
45
  }
29
46
  if (match) {
30
47
  return rules;
31
- } else {
32
- throw { type: 'Runtime',
33
- message: 'No matching definition was found for `' +
34
- this.selector.toCSS().trim() + '(' +
35
- this.arguments.map(function (a) {
36
- return a.toCSS();
37
- }).join(', ') + ")`",
38
- index: this.index, filename: this.filename };
39
48
  }
40
49
  }
41
50
  }
42
- throw { type: 'Name',
51
+ if (isOneFound) {
52
+ throw { type: 'Runtime',
53
+ message: 'No matching definition was found for `' +
54
+ this.selector.toCSS().trim() + '(' +
55
+ (args ? args.map(function (a) {
56
+ var argValue = "";
57
+ if (a.name) {
58
+ argValue += a.name + ":";
59
+ }
60
+ if (a.value.toCSS) {
61
+ argValue += a.value.toCSS();
62
+ } else {
63
+ argValue += "???";
64
+ }
65
+ return argValue;
66
+ }).join(', ') : "") + ")`",
67
+ index: this.index, filename: this.filename };
68
+ } else {
69
+ throw { type: 'Name',
43
70
  message: this.selector.toCSS().trim() + " is undefined",
44
71
  index: this.index, filename: this.filename };
72
+ }
45
73
  }
46
74
  };
47
75
 
@@ -68,45 +96,98 @@ tree.mixin.Definition.prototype = {
68
96
  find: function () { return this.parent.find.apply(this, arguments) },
69
97
  rulesets: function () { return this.parent.rulesets.apply(this) },
70
98
 
71
- evalParams: function (env, args) {
72
- var frame = new(tree.Ruleset)(null, []), varargs;
99
+ evalParams: function (env, mixinEnv, args, evaldArguments) {
100
+ var frame = new(tree.Ruleset)(null, []), varargs, arg, params = this.params.slice(0), i, j, val, name, isNamedFound, argIndex;
101
+
102
+ if (args) {
103
+ args = args.slice(0);
104
+
105
+ for(i = 0; i < args.length; i++) {
106
+ arg = args[i];
107
+ if (name = (arg && arg.name)) {
108
+ isNamedFound = false;
109
+ for(j = 0; j < params.length; j++) {
110
+ if (!evaldArguments[j] && name === params[j].name) {
111
+ evaldArguments[j] = arg.value.eval(env);
112
+ frame.rules.unshift(new(tree.Rule)(name, arg.value.eval(env)));
113
+ isNamedFound = true;
114
+ break;
115
+ }
116
+ }
117
+ if (isNamedFound) {
118
+ args.splice(i, 1);
119
+ i--;
120
+ continue;
121
+ } else {
122
+ throw { type: 'Runtime', message: "Named argument for " + this.name +
123
+ ' ' + args[i].name + ' not found' };
124
+ }
125
+ }
126
+ }
127
+ }
128
+ argIndex = 0;
129
+ for (i = 0; i < params.length; i++) {
130
+ if (evaldArguments[i]) continue;
131
+
132
+ arg = args && args[argIndex];
73
133
 
74
- for (var i = 0, val, name; i < this.params.length; i++) {
75
- if (name = this.params[i].name) {
76
- if (this.params[i].variadic && args) {
134
+ if (name = params[i].name) {
135
+ if (params[i].variadic && args) {
77
136
  varargs = [];
78
- for (var j = i; j < args.length; j++) {
79
- varargs.push(args[j].eval(env));
137
+ for (j = argIndex; j < args.length; j++) {
138
+ varargs.push(args[j].value.eval(env));
80
139
  }
81
140
  frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env)));
82
- } else if (val = (args && args[i]) || this.params[i].value) {
83
- frame.rules.unshift(new(tree.Rule)(name, val.eval(env)));
84
141
  } else {
85
- throw { type: 'Runtime', message: "wrong number of arguments for " + this.name +
142
+ val = arg && arg.value;
143
+ if (val) {
144
+ val = val.eval(env);
145
+ } else if (params[i].value) {
146
+ val = params[i].value.eval(mixinEnv);
147
+ } else {
148
+ throw { type: 'Runtime', message: "wrong number of arguments for " + this.name +
86
149
  ' (' + args.length + ' for ' + this.arity + ')' };
150
+ }
151
+
152
+ frame.rules.unshift(new(tree.Rule)(name, val));
153
+ evaldArguments[i] = val;
154
+ }
155
+ }
156
+
157
+ if (params[i].variadic && args) {
158
+ for (j = argIndex; j < args.length; j++) {
159
+ evaldArguments[j] = args[j].value.eval(env);
87
160
  }
88
161
  }
162
+ argIndex++;
89
163
  }
164
+
90
165
  return frame;
91
166
  },
92
167
  eval: function (env, args, important) {
93
- var frame = this.evalParams(env, args), context, _arguments = [], rules, start;
168
+ var _arguments = [],
169
+ mixinFrames = this.frames.concat(env.frames),
170
+ frame = this.evalParams(env, {frames: mixinFrames}, args, _arguments),
171
+ context, rules, start, ruleset;
94
172
 
95
- for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) {
96
- _arguments.push(args[i] || this.params[i].value);
97
- }
98
173
  frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env)));
99
174
 
100
175
  rules = important ?
101
- this.rules.map(function (r) {
102
- return new(tree.Rule)(r.name, r.value, '!important', r.index);
103
- }) : this.rules.slice(0);
176
+ this.parent.makeImportant.apply(this).rules : this.rules.slice(0);
104
177
 
105
- return new(tree.Ruleset)(null, rules).eval({
106
- frames: [this, frame].concat(this.frames, env.frames)
178
+ ruleset = new(tree.Ruleset)(null, rules).eval({
179
+ frames: [this, frame].concat(mixinFrames)
107
180
  });
181
+ ruleset.originalRuleset = this;
182
+ return ruleset;
108
183
  },
109
- match: function (args, env) {
184
+ matchCondition: function (args, env) {
185
+ if (this.condition && !this.condition.eval({
186
+ frames: [this.evalParams(env, {frames: this.frames.concat(env.frames)}, args, [])].concat(env.frames)
187
+ })) { return false }
188
+ return true;
189
+ },
190
+ matchArgs: function (args, env) {
110
191
  var argsLength = (args && args.length) || 0, len, frame;
111
192
 
112
193
  if (! this.variadic) {
@@ -115,15 +196,11 @@ tree.mixin.Definition.prototype = {
115
196
  if ((this.required > 0) && (argsLength > this.params.length)) { return false }
116
197
  }
117
198
 
118
- if (this.condition && !this.condition.eval({
119
- frames: [this.evalParams(env, args)].concat(env.frames)
120
- })) { return false }
121
-
122
199
  len = Math.min(argsLength, this.arity);
123
200
 
124
201
  for (var i = 0; i < len; i++) {
125
- if (!this.params[i].name) {
126
- if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) {
202
+ if (!this.params[i].name && !this.params[i].variadic) {
203
+ if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) {
127
204
  return false;
128
205
  }
129
206
  }
@@ -17,6 +17,11 @@ tree.Operation.prototype.eval = function (env) {
17
17
  message: "Can't substract or divide a color from a number" };
18
18
  }
19
19
  }
20
+ if (!a.operate) {
21
+ throw { name: "OperationError",
22
+ message: "Operation on an invalid type" };
23
+ }
24
+
20
25
  return a.operate(this.op, b);
21
26
  };
22
27
 
@@ -20,9 +20,23 @@ tree.Quoted.prototype = {
20
20
  return new(tree.JavaScript)(exp, that.index, true).eval(env).value;
21
21
  }).replace(/@\{([\w-]+)\}/g, function (_, name) {
22
22
  var v = new(tree.Variable)('@' + name, that.index).eval(env);
23
- return ('value' in v) ? v.value : v.toCSS();
23
+ return (v instanceof tree.Quoted) ? v.value : v.toCSS();
24
24
  });
25
25
  return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index);
26
+ },
27
+ compare: function (x) {
28
+ if (!x.toCSS) {
29
+ return -1;
30
+ }
31
+
32
+ var left = this.toCSS(),
33
+ right = x.toCSS();
34
+
35
+ if (left === right) {
36
+ return 0;
37
+ }
38
+
39
+ return left < right ? -1 : 1;
26
40
  }
27
41
  };
28
42
 
@@ -0,0 +1,13 @@
1
+ (function (tree) {
2
+
3
+ tree.Ratio = function (value) {
4
+ this.value = value;
5
+ };
6
+ tree.Ratio.prototype = {
7
+ toCSS: function (env) {
8
+ return this.value;
9
+ },
10
+ eval: function () { return this }
11
+ };
12
+
13
+ })(require('../tree'));
@@ -27,6 +27,13 @@ tree.Rule.prototype.eval = function (context) {
27
27
  this.index, this.inline);
28
28
  };
29
29
 
30
+ tree.Rule.prototype.makeImportant = function () {
31
+ return new(tree.Rule)(this.name,
32
+ this.value,
33
+ "!important",
34
+ this.index, this.inline);
35
+ };
36
+
30
37
  tree.Shorthand = function (a, b) {
31
38
  this.a = a;
32
39
  this.b = b;
@@ -10,21 +10,22 @@ tree.Ruleset.prototype = {
10
10
  eval: function (env) {
11
11
  var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) });
12
12
  var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports);
13
-
13
+ var rules;
14
+
15
+ ruleset.originalRuleset = this;
14
16
  ruleset.root = this.root;
15
17
  ruleset.allowImports = this.allowImports;
16
18
 
19
+ if(this.debugInfo) {
20
+ ruleset.debugInfo = this.debugInfo;
21
+ }
22
+
17
23
  // push the current ruleset to the frames stack
18
24
  env.frames.unshift(ruleset);
19
25
 
20
26
  // Evaluate imports
21
27
  if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) {
22
- for (var i = 0; i < ruleset.rules.length; i++) {
23
- if (ruleset.rules[i] instanceof tree.Import) {
24
- Array.prototype.splice
25
- .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env)));
26
- }
27
- }
28
+ ruleset.evalImports(env);
28
29
  }
29
30
 
30
31
  // Store the frames around mixin definitions,
@@ -34,12 +35,16 @@ tree.Ruleset.prototype = {
34
35
  ruleset.rules[i].frames = env.frames.slice(0);
35
36
  }
36
37
  }
38
+
39
+ var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0;
37
40
 
38
41
  // Evaluate mixin calls.
39
42
  for (var i = 0; i < ruleset.rules.length; i++) {
40
43
  if (ruleset.rules[i] instanceof tree.mixin.Call) {
41
- Array.prototype.splice
42
- .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env)));
44
+ rules = ruleset.rules[i].eval(env);
45
+ ruleset.rules.splice.apply(ruleset.rules, [i, 1].concat(rules));
46
+ i += rules.length-1;
47
+ ruleset.resetCache();
43
48
  }
44
49
  }
45
50
 
@@ -54,12 +59,47 @@ tree.Ruleset.prototype = {
54
59
 
55
60
  // Pop the stack
56
61
  env.frames.shift();
62
+
63
+ if (env.mediaBlocks) {
64
+ for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) {
65
+ env.mediaBlocks[i].bubbleSelectors(selectors);
66
+ }
67
+ }
57
68
 
58
69
  return ruleset;
59
70
  },
60
- match: function (args) {
71
+ evalImports: function(env) {
72
+ var i, rules;
73
+ for (i = 0; i < this.rules.length; i++) {
74
+ if (this.rules[i] instanceof tree.Import) {
75
+ rules = this.rules[i].eval(env);
76
+ if (typeof rules.length === "number") {
77
+ this.rules.splice.apply(this.rules, [i, 1].concat(rules));
78
+ i+= rules.length-1;
79
+ } else {
80
+ this.rules.splice(i, 1, rules);
81
+ }
82
+ this.resetCache();
83
+ }
84
+ }
85
+ },
86
+ makeImportant: function() {
87
+ return new tree.Ruleset(this.selectors, this.rules.map(function (r) {
88
+ if (r.makeImportant) {
89
+ return r.makeImportant();
90
+ } else {
91
+ return r;
92
+ }
93
+ }), this.strictImports);
94
+ },
95
+ matchArgs: function (args) {
61
96
  return !args || args.length === 0;
62
97
  },
98
+ resetCache: function () {
99
+ this._rulesets = null;
100
+ this._variables = null;
101
+ this._lookups = {};
102
+ },
63
103
  variables: function () {
64
104
  if (this._variables) { return this._variables }
65
105
  else {
@@ -114,25 +154,41 @@ tree.Ruleset.prototype = {
114
154
  toCSS: function (context, env) {
115
155
  var css = [], // The CSS output
116
156
  rules = [], // node.Rule instances
157
+ _rules = [], //
117
158
  rulesets = [], // node.Ruleset instances
118
159
  paths = [], // Current selectors
119
160
  selector, // The fully rendered selector
161
+ debugInfo, // Line number debugging
120
162
  rule;
121
163
 
122
164
  if (! this.root) {
123
- if (context.length === 0) {
124
- paths = this.selectors.map(function (s) { return [s] });
125
- } else {
126
- this.joinSelectors(paths, context, this.selectors);
127
- }
165
+ this.joinSelectors(paths, context, this.selectors);
128
166
  }
129
167
 
130
168
  // Compile rules and rulesets
131
169
  for (var i = 0; i < this.rules.length; i++) {
132
170
  rule = this.rules[i];
133
171
 
134
- if (rule.rules || (rule instanceof tree.Directive) || (rule instanceof tree.Media)) {
172
+ if (rule.rules || (rule instanceof tree.Media)) {
135
173
  rulesets.push(rule.toCSS(paths, env));
174
+ } else if (rule instanceof tree.Directive) {
175
+ var cssValue = rule.toCSS(paths, env);
176
+ // Output only the first @charset definition as such - convert the others
177
+ // to comments in case debug is enabled
178
+ if (rule.name === "@charset") {
179
+ // Only output the debug info together with subsequent @charset definitions
180
+ // a comment (or @media statement) before the actual @charset directive would
181
+ // be considered illegal css as it has to be on the first line
182
+ if (env.charset) {
183
+ if (rule.debugInfo) {
184
+ rulesets.push(tree.debugInfo(env, rule));
185
+ rulesets.push(new tree.Comment("/* "+cssValue.replace(/\n/g, "")+" */\n").toCSS(env));
186
+ }
187
+ continue;
188
+ }
189
+ env.charset = true;
190
+ }
191
+ rulesets.push(cssValue);
136
192
  } else if (rule instanceof tree.Comment) {
137
193
  if (!rule.silent) {
138
194
  if (this.root) {
@@ -159,13 +215,22 @@ tree.Ruleset.prototype = {
159
215
  css.push(rules.join(env.compress ? '' : '\n'));
160
216
  } else {
161
217
  if (rules.length > 0) {
218
+ debugInfo = tree.debugInfo(env, this);
162
219
  selector = paths.map(function (p) {
163
220
  return p.map(function (s) {
164
221
  return s.toCSS(env);
165
222
  }).join('').trim();
166
- }).join( env.compress ? ',' : ',\n');
223
+ }).join(env.compress ? ',' : ',\n');
224
+
225
+ // Remove duplicates
226
+ for (var i = rules.length - 1; i >= 0; i--) {
227
+ if (_rules.indexOf(rules[i]) === -1) {
228
+ _rules.unshift(rules[i]);
229
+ }
230
+ }
231
+ rules = _rules;
167
232
 
168
- css.push(selector,
233
+ css.push(debugInfo + selector +
169
234
  (env.compress ? '{' : ' {\n ') +
170
235
  rules.join(env.compress ? '' : '\n ') +
171
236
  (env.compress ? '}' : '\n}\n'));
@@ -173,7 +238,7 @@ tree.Ruleset.prototype = {
173
238
  }
174
239
  css.push(rulesets);
175
240
 
176
- return css.join('') + (env.compress ? '\n' : '');
241
+ return css.join('') + (env.compress ? '\n' : '');
177
242
  },
178
243
 
179
244
  joinSelectors: function (paths, context, selectors) {
@@ -183,33 +248,166 @@ tree.Ruleset.prototype = {
183
248
  },
184
249
 
185
250
  joinSelector: function (paths, context, selector) {
186
- var before = [], after = [], beforeElements = [],
187
- afterElements = [], hasParentSelector = false, el;
188
251
 
189
- for (var i = 0; i < selector.elements.length; i++) {
252
+ var i, j, k,
253
+ hasParentSelector, newSelectors, el, sel, parentSel,
254
+ newSelectorPath, afterParentJoin, newJoinedSelector,
255
+ newJoinedSelectorEmpty, lastSelector, currentElements,
256
+ selectorsMultiplied;
257
+
258
+ for (i = 0; i < selector.elements.length; i++) {
190
259
  el = selector.elements[i];
191
- if (el.combinator.value.charAt(0) === '&') {
260
+ if (el.value === '&') {
192
261
  hasParentSelector = true;
193
262
  }
194
- if (hasParentSelector) afterElements.push(el);
195
- else beforeElements.push(el);
196
263
  }
264
+
265
+ if (!hasParentSelector) {
266
+ if (context.length > 0) {
267
+ for(i = 0; i < context.length; i++) {
268
+ paths.push(context[i].concat(selector));
269
+ }
270
+ }
271
+ else {
272
+ paths.push([selector]);
273
+ }
274
+ return;
275
+ }
276
+
277
+ // The paths are [[Selector]]
278
+ // The first list is a list of comma seperated selectors
279
+ // The inner list is a list of inheritance seperated selectors
280
+ // e.g.
281
+ // .a, .b {
282
+ // .c {
283
+ // }
284
+ // }
285
+ // == [[.a] [.c]] [[.b] [.c]]
286
+ //
197
287
 
198
- if (! hasParentSelector) {
199
- afterElements = beforeElements;
200
- beforeElements = [];
288
+ // the elements from the current selector so far
289
+ currentElements = [];
290
+ // the current list of new selectors to add to the path.
291
+ // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
292
+ // by the parents
293
+ newSelectors = [[]];
294
+
295
+ for (i = 0; i < selector.elements.length; i++) {
296
+ el = selector.elements[i];
297
+ // non parent reference elements just get added
298
+ if (el.value !== "&") {
299
+ currentElements.push(el);
300
+ } else {
301
+ // the new list of selectors to add
302
+ selectorsMultiplied = [];
303
+
304
+ // merge the current list of non parent selector elements
305
+ // on to the current list of selectors to add
306
+ if (currentElements.length > 0) {
307
+ this.mergeElementsOnToSelectors(currentElements, newSelectors);
308
+ }
309
+
310
+ // loop through our current selectors
311
+ for(j = 0; j < newSelectors.length; j++) {
312
+ sel = newSelectors[j];
313
+ // if we don't have any parent paths, the & might be in a mixin so that it can be used
314
+ // whether there are parents or not
315
+ if (context.length == 0) {
316
+ // the combinator used on el should now be applied to the next element instead so that
317
+ // it is not lost
318
+ if (sel.length > 0) {
319
+ sel[0].elements = sel[0].elements.slice(0);
320
+ sel[0].elements.push(new(tree.Element)(el.combinator, '', 0)); //new Element(el.Combinator, ""));
321
+ }
322
+ selectorsMultiplied.push(sel);
323
+ }
324
+ else {
325
+ // and the parent selectors
326
+ for(k = 0; k < context.length; k++) {
327
+ parentSel = context[k];
328
+ // We need to put the current selectors
329
+ // then join the last selector's elements on to the parents selectors
330
+
331
+ // our new selector path
332
+ newSelectorPath = [];
333
+ // selectors from the parent after the join
334
+ afterParentJoin = [];
335
+ newJoinedSelectorEmpty = true;
336
+
337
+ //construct the joined selector - if & is the first thing this will be empty,
338
+ // if not newJoinedSelector will be the last set of elements in the selector
339
+ if (sel.length > 0) {
340
+ newSelectorPath = sel.slice(0);
341
+ lastSelector = newSelectorPath.pop();
342
+ newJoinedSelector = new(tree.Selector)(lastSelector.elements.slice(0));
343
+ newJoinedSelectorEmpty = false;
344
+ }
345
+ else {
346
+ newJoinedSelector = new(tree.Selector)([]);
347
+ }
348
+
349
+ //put together the parent selectors after the join
350
+ if (parentSel.length > 1) {
351
+ afterParentJoin = afterParentJoin.concat(parentSel.slice(1));
352
+ }
353
+
354
+ if (parentSel.length > 0) {
355
+ newJoinedSelectorEmpty = false;
356
+
357
+ // join the elements so far with the first part of the parent
358
+ newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, 0));
359
+ newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1));
360
+ }
361
+
362
+ if (!newJoinedSelectorEmpty) {
363
+ // now add the joined selector
364
+ newSelectorPath.push(newJoinedSelector);
365
+ }
366
+
367
+ // and the rest of the parent
368
+ newSelectorPath = newSelectorPath.concat(afterParentJoin);
369
+
370
+ // add that to our new set of selectors
371
+ selectorsMultiplied.push(newSelectorPath);
372
+ }
373
+ }
374
+ }
375
+
376
+ // our new selectors has been multiplied, so reset the state
377
+ newSelectors = selectorsMultiplied;
378
+ currentElements = [];
379
+ }
201
380
  }
202
381
 
203
- if (beforeElements.length > 0) {
204
- before.push(new(tree.Selector)(beforeElements));
382
+ // if we have any elements left over (e.g. .a& .b == .b)
383
+ // add them on to all the current selectors
384
+ if (currentElements.length > 0) {
385
+ this.mergeElementsOnToSelectors(currentElements, newSelectors);
205
386
  }
206
387
 
207
- if (afterElements.length > 0) {
208
- after.push(new(tree.Selector)(afterElements));
388
+ for(i = 0; i < newSelectors.length; i++) {
389
+ paths.push(newSelectors[i]);
390
+ }
391
+ },
392
+
393
+ mergeElementsOnToSelectors: function(elements, selectors) {
394
+ var i, sel;
395
+
396
+ if (selectors.length == 0) {
397
+ selectors.push([ new(tree.Selector)(elements) ]);
398
+ return;
209
399
  }
210
400
 
211
- for (var c = 0; c < context.length; c++) {
212
- paths.push(before.concat(context[c]).concat(after));
401
+ for(i = 0; i < selectors.length; i++) {
402
+ sel = selectors[i];
403
+
404
+ // if the previous thing in sel is a parent this needs to join on to it
405
+ if (sel.length > 0) {
406
+ sel[sel.length - 1] = new(tree.Selector)(sel[sel.length - 1].elements.concat(elements));
407
+ }
408
+ else {
409
+ sel.push(new(tree.Selector)(elements));
410
+ }
213
411
  }
214
412
  }
215
413
  };