less 1.2.21 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. data/.gitignore +4 -3
  2. data/.gitmodules +6 -0
  3. data/Gemfile +3 -0
  4. data/README.md +39 -46
  5. data/Rakefile +5 -50
  6. data/bin/lessc +1 -100
  7. data/less.gemspec +21 -125
  8. data/lib/less.rb +11 -30
  9. data/{LICENSE → lib/less/js/LICENSE} +1 -1
  10. data/lib/less/js/Makefile +56 -0
  11. data/lib/less/js/README.md +20 -0
  12. data/lib/less/js/benchmark/benchmark.less +3979 -0
  13. data/lib/less/js/benchmark/less-benchmark.js +49 -0
  14. data/lib/less/js/bin/lessc +105 -0
  15. data/lib/less/js/build/ecma-5.js +120 -0
  16. data/lib/less/js/build/header.js +7 -0
  17. data/lib/less/js/build/require.js +7 -0
  18. data/lib/less/js/dist/less-1.0.44.js +2655 -0
  19. data/lib/less/js/dist/less-1.1.0.js +2695 -0
  20. data/lib/less/js/dist/less-1.1.0.min.js +16 -0
  21. data/lib/less/js/dist/less-1.1.1.js +2710 -0
  22. data/lib/less/js/dist/less-1.1.1.min.js +16 -0
  23. data/lib/less/js/dist/less-1.1.2.js +2712 -0
  24. data/lib/less/js/dist/less-1.1.2.min.js +16 -0
  25. data/lib/less/js/dist/less-1.1.3.js +2721 -0
  26. data/lib/less/js/dist/less-1.1.3.min.js +16 -0
  27. data/lib/less/js/lib/less/browser.js +369 -0
  28. data/lib/less/js/lib/less/functions.js +174 -0
  29. data/lib/less/js/lib/less/index.js +137 -0
  30. data/lib/less/js/lib/less/parser.js +1098 -0
  31. data/lib/less/js/lib/less/tree.js +13 -0
  32. data/lib/less/js/lib/less/tree/alpha.js +17 -0
  33. data/lib/less/js/lib/less/tree/anonymous.js +13 -0
  34. data/lib/less/js/lib/less/tree/call.js +45 -0
  35. data/lib/less/js/lib/less/tree/color.js +98 -0
  36. data/lib/less/js/lib/less/tree/comment.js +14 -0
  37. data/lib/less/js/lib/less/tree/dimension.js +34 -0
  38. data/lib/less/js/lib/less/tree/directive.js +33 -0
  39. data/lib/less/js/lib/less/tree/element.js +32 -0
  40. data/lib/less/js/lib/less/tree/expression.js +23 -0
  41. data/lib/less/js/lib/less/tree/import.js +77 -0
  42. data/lib/less/js/lib/less/tree/javascript.js +51 -0
  43. data/lib/less/js/lib/less/tree/keyword.js +9 -0
  44. data/lib/less/js/lib/less/tree/mixin.js +106 -0
  45. data/lib/less/js/lib/less/tree/operation.js +32 -0
  46. data/lib/less/js/lib/less/tree/quoted.js +29 -0
  47. data/lib/less/js/lib/less/tree/rule.js +38 -0
  48. data/lib/less/js/lib/less/tree/ruleset.js +179 -0
  49. data/lib/less/js/lib/less/tree/selector.js +28 -0
  50. data/lib/less/js/lib/less/tree/url.js +25 -0
  51. data/lib/less/js/lib/less/tree/value.js +24 -0
  52. data/lib/less/js/lib/less/tree/variable.js +24 -0
  53. data/lib/less/js/package.json +13 -0
  54. data/lib/less/js/test/css/colors.css +42 -0
  55. data/lib/less/js/test/css/comments.css +52 -0
  56. data/lib/less/js/test/css/css-3.css +42 -0
  57. data/lib/less/js/test/css/css-escapes.css +20 -0
  58. data/lib/less/js/test/css/css.css +82 -0
  59. data/lib/less/js/test/css/functions.css +30 -0
  60. data/{spec → lib/less/js/test}/css/import.css +4 -2
  61. data/lib/less/js/test/css/javascript.css +22 -0
  62. data/lib/less/js/test/css/lazy-eval.css +3 -0
  63. data/lib/less/js/test/css/media.css +21 -0
  64. data/lib/less/js/test/css/mixins-args.css +61 -0
  65. data/lib/less/js/test/css/mixins-closure.css +9 -0
  66. data/lib/less/js/test/css/mixins-nested.css +14 -0
  67. data/lib/less/js/test/css/mixins-pattern.css +49 -0
  68. data/lib/less/js/test/css/mixins.css +50 -0
  69. data/{spec → lib/less/js/test}/css/operations.css +20 -2
  70. data/{spec → lib/less/js/test}/css/parens.css +0 -0
  71. data/lib/less/js/test/css/rulesets.css +29 -0
  72. data/{spec → lib/less/js/test}/css/scope.css +6 -2
  73. data/lib/less/js/test/css/selectors.css +32 -0
  74. data/lib/less/js/test/css/strings.css +38 -0
  75. data/lib/less/js/test/css/variables.css +24 -0
  76. data/lib/less/js/test/css/whitespace.css +36 -0
  77. data/lib/less/js/test/less-test.js +75 -0
  78. data/{spec → lib/less/js/test}/less/colors.less +13 -2
  79. data/{spec → lib/less/js/test}/less/comments.less +19 -2
  80. data/{spec → lib/less/js/test}/less/css-3.less +4 -1
  81. data/lib/less/js/test/less/css-escapes.less +28 -0
  82. data/{spec → lib/less/js/test}/less/css.less +10 -18
  83. data/lib/less/js/test/less/functions.less +35 -0
  84. data/{spec → lib/less/js/test}/less/import.less +1 -1
  85. data/{spec → lib/less/js/test}/less/import/import-test-a.less +0 -0
  86. data/{spec → lib/less/js/test}/less/import/import-test-b.less +0 -0
  87. data/{spec → lib/less/js/test}/less/import/import-test-c.less +0 -0
  88. data/{spec → lib/less/js/test}/less/import/import-test-d.css +0 -0
  89. data/lib/less/js/test/less/javascript.less +27 -0
  90. data/{spec → lib/less/js/test}/less/lazy-eval.less +0 -0
  91. data/lib/less/js/test/less/media.less +25 -0
  92. data/lib/less/js/test/less/mixins-args.less +118 -0
  93. data/lib/less/js/test/less/mixins-closure.less +26 -0
  94. data/lib/less/js/test/less/mixins-nested.less +22 -0
  95. data/lib/less/js/test/less/mixins-pattern.less +96 -0
  96. data/{spec → lib/less/js/test}/less/mixins.less +8 -4
  97. data/{spec → lib/less/js/test}/less/operations.less +19 -0
  98. data/{spec → lib/less/js/test}/less/parens.less +0 -0
  99. data/{spec → lib/less/js/test}/less/rulesets.less +2 -2
  100. data/{spec → lib/less/js/test}/less/scope.less +1 -1
  101. data/{spec → lib/less/js/test}/less/selectors.less +1 -1
  102. data/lib/less/js/test/less/strings.less +49 -0
  103. data/lib/less/js/test/less/variables.less +50 -0
  104. data/{spec → lib/less/js/test}/less/whitespace.less +3 -0
  105. data/lib/less/loader.rb +67 -0
  106. data/lib/less/parser.rb +46 -0
  107. data/lib/less/version.rb +3 -0
  108. data/spec/less/one/one.less +1 -0
  109. data/spec/less/parser_spec.rb +30 -0
  110. data/spec/less/two/two.less +1 -0
  111. data/spec/spec_helper.rb +2 -7
  112. metadata +156 -106
  113. data/CHANGELOG +0 -62
  114. data/VERSION +0 -1
  115. data/lib/less/command.rb +0 -110
  116. data/lib/less/engine.rb +0 -52
  117. data/lib/less/engine/grammar/common.tt +0 -29
  118. data/lib/less/engine/grammar/entity.tt +0 -144
  119. data/lib/less/engine/grammar/less.tt +0 -341
  120. data/lib/less/engine/nodes.rb +0 -9
  121. data/lib/less/engine/nodes/element.rb +0 -281
  122. data/lib/less/engine/nodes/entity.rb +0 -79
  123. data/lib/less/engine/nodes/function.rb +0 -93
  124. data/lib/less/engine/nodes/literal.rb +0 -171
  125. data/lib/less/engine/nodes/property.rb +0 -232
  126. data/lib/less/engine/nodes/ruleset.rb +0 -12
  127. data/lib/less/engine/nodes/selector.rb +0 -44
  128. data/lib/less/ext.rb +0 -60
  129. data/spec/command_spec.rb +0 -102
  130. data/spec/css/accessors.css +0 -18
  131. data/spec/css/big.css +0 -3768
  132. data/spec/css/colors.css +0 -14
  133. data/spec/css/comments.css +0 -9
  134. data/spec/css/css-3.css +0 -21
  135. data/spec/css/css.css +0 -50
  136. data/spec/css/dash-prefix.css +0 -12
  137. data/spec/css/functions.css +0 -6
  138. data/spec/css/import-with-extra-paths.css +0 -8
  139. data/spec/css/import-with-partial-in-extra-path.css +0 -6
  140. data/spec/css/lazy-eval.css +0 -1
  141. data/spec/css/mixins-args.css +0 -32
  142. data/spec/css/mixins.css +0 -28
  143. data/spec/css/rulesets.css +0 -17
  144. data/spec/css/selectors.css +0 -13
  145. data/spec/css/strings.css +0 -12
  146. data/spec/css/variables.css +0 -8
  147. data/spec/css/whitespace.css +0 -7
  148. data/spec/engine_spec.rb +0 -127
  149. data/spec/less/accessors.less +0 -20
  150. data/spec/less/big.less +0 -1264
  151. data/spec/less/dash-prefix.less +0 -21
  152. data/spec/less/exceptions/mixed-units-error.less +0 -3
  153. data/spec/less/exceptions/name-error-1.0.less +0 -3
  154. data/spec/less/exceptions/syntax-error-1.0.less +0 -3
  155. data/spec/less/extra_import_path/extra.less +0 -1
  156. data/spec/less/extra_import_path/import/import-test-a.css +0 -1
  157. data/spec/less/extra_import_path/import/import-test-a.less +0 -4
  158. data/spec/less/functions.less +0 -6
  159. data/spec/less/hidden.less +0 -25
  160. data/spec/less/import-with-extra-paths.less +0 -4
  161. data/spec/less/literal-css.less +0 -11
  162. data/spec/less/mixins-args.less +0 -59
  163. data/spec/less/strings.less +0 -14
  164. data/spec/less/variables.less +0 -29
  165. data/spec/spec.css +0 -50
@@ -0,0 +1,137 @@
1
+ var path = require('path'),
2
+ sys = require('sys'),
3
+ fs = require('fs');
4
+
5
+ require.paths.unshift(path.join(__dirname, '..'));
6
+
7
+ var less = {
8
+ version: [1, 1, 3],
9
+ Parser: require('less/parser').Parser,
10
+ importer: require('less/parser').importer,
11
+ tree: require('less/tree'),
12
+ render: function (input, options, callback) {
13
+ options = options || {};
14
+
15
+ if (typeof(options) === 'function') {
16
+ callback = options, options = {};
17
+ }
18
+
19
+ var parser = new(this.Parser)(options),
20
+ ee;
21
+
22
+ if (callback) {
23
+ parser.parse(input, function (e, root) {
24
+ callback(e, root.toCSS(options));
25
+ });
26
+ } else {
27
+ ee = new(require('events').EventEmitter);
28
+
29
+ process.nextTick(function () {
30
+ parser.parse(input, function (e, root) {
31
+ if (e) { ee.emit('error', e) }
32
+ else { ee.emit('success', root.toCSS(options)) }
33
+ });
34
+ });
35
+ return ee;
36
+ }
37
+ },
38
+ writeError: function (ctx, options) {
39
+ var message = "";
40
+ var extract = ctx.extract;
41
+ var error = [];
42
+
43
+ options = options || {};
44
+
45
+ if (options.silent) { return }
46
+
47
+ if (!ctx.index) {
48
+ return sys.error(ctx.stack || ctx.message);
49
+ }
50
+
51
+ if (typeof(extract[0]) === 'string') {
52
+ error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey'));
53
+ }
54
+
55
+ error.push(ctx.line + ' ' + extract[1].slice(0, ctx.column)
56
+ + stylize(stylize(extract[1][ctx.column], 'bold')
57
+ + extract[1].slice(ctx.column + 1), 'yellow'));
58
+
59
+ if (typeof(extract[2]) === 'string') {
60
+ error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey'));
61
+ }
62
+ error = error.join('\n') + '\033[0m\n';
63
+
64
+ message += stylize(ctx.message, 'red');
65
+ ctx.filename && (message += stylize(' in ', 'red') + ctx.filename);
66
+
67
+ sys.error(message, error);
68
+
69
+ if (ctx.callLine) {
70
+ sys.error(stylize('from ', 'red') + (ctx.filename || ''));
71
+ sys.error(stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract);
72
+ }
73
+ if (ctx.stack) { sys.error(stylize(ctx.stack, 'red')) }
74
+ }
75
+ };
76
+
77
+ ['color', 'directive', 'operation', 'dimension',
78
+ 'keyword', 'variable', 'ruleset', 'element',
79
+ 'selector', 'quoted', 'expression', 'rule',
80
+ 'call', 'url', 'alpha', 'import',
81
+ 'mixin', 'comment', 'anonymous', 'value', 'javascript'
82
+ ].forEach(function (n) {
83
+ require(path.join('less', 'tree', n));
84
+ });
85
+
86
+ less.Parser.importer = function (file, paths, callback) {
87
+ var pathname;
88
+
89
+ paths.unshift('.');
90
+
91
+ for (var i = 0; i < paths.length; i++) {
92
+ try {
93
+ pathname = path.join(paths[i], file);
94
+ fs.statSync(pathname);
95
+ break;
96
+ } catch (e) {
97
+ pathname = null;
98
+ }
99
+ }
100
+
101
+ if (pathname) {
102
+ fs.readFile(pathname, 'utf-8', function(e, data) {
103
+ if (e) sys.error(e);
104
+
105
+ new(less.Parser)({
106
+ paths: [path.dirname(pathname)],
107
+ filename: pathname
108
+ }).parse(data, function (e, root) {
109
+ if (e) less.writeError(e);
110
+ callback(root);
111
+ });
112
+ });
113
+ } else {
114
+ sys.error("file '" + file + "' wasn't found.\n");
115
+ process.exit(1);
116
+ }
117
+ }
118
+
119
+ require('less/functions');
120
+
121
+ for (var k in less) { exports[k] = less[k] }
122
+
123
+ // Stylize a string
124
+ function stylize(str, style) {
125
+ var styles = {
126
+ 'bold' : [1, 22],
127
+ 'inverse' : [7, 27],
128
+ 'underline' : [4, 24],
129
+ 'yellow' : [33, 39],
130
+ 'green' : [32, 39],
131
+ 'red' : [31, 39],
132
+ 'grey' : [90, 39]
133
+ };
134
+ return '\033[' + styles[style][0] + 'm' + str +
135
+ '\033[' + styles[style][1] + 'm';
136
+ }
137
+
@@ -0,0 +1,1098 @@
1
+ var less, tree;
2
+
3
+ if (typeof(window) === 'undefined') {
4
+ less = exports,
5
+ tree = require('less/tree');
6
+ } else {
7
+ if (typeof(window.less) === 'undefined') { window.less = {} }
8
+ less = window.less,
9
+ tree = window.less.tree = {};
10
+ }
11
+ //
12
+ // less.js - parser
13
+ //
14
+ // A relatively straight-forward predictive parser.
15
+ // There is no tokenization/lexing stage, the input is parsed
16
+ // in one sweep.
17
+ //
18
+ // To make the parser fast enough to run in the browser, several
19
+ // optimization had to be made:
20
+ //
21
+ // - Matching and slicing on a huge input is often cause of slowdowns.
22
+ // The solution is to chunkify the input into smaller strings.
23
+ // The chunks are stored in the `chunks` var,
24
+ // `j` holds the current chunk index, and `current` holds
25
+ // the index of the current chunk in relation to `input`.
26
+ // This gives us an almost 4x speed-up.
27
+ //
28
+ // - In many cases, we don't need to match individual tokens;
29
+ // for example, if a value doesn't hold any variables, operations
30
+ // or dynamic references, the parser can effectively 'skip' it,
31
+ // treating it as a literal.
32
+ // An example would be '1px solid #000' - which evaluates to itself,
33
+ // we don't need to know what the individual components are.
34
+ // The drawback, of course is that you don't get the benefits of
35
+ // syntax-checking on the CSS. This gives us a 50% speed-up in the parser,
36
+ // and a smaller speed-up in the code-gen.
37
+ //
38
+ //
39
+ // Token matching is done with the `$` function, which either takes
40
+ // a terminal string or regexp, or a non-terminal function to call.
41
+ // It also takes care of moving all the indices forwards.
42
+ //
43
+ //
44
+ less.Parser = function Parser(env) {
45
+ var input, // LeSS input string
46
+ i, // current index in `input`
47
+ j, // current chunk
48
+ temp, // temporarily holds a chunk's state, for backtracking
49
+ memo, // temporarily holds `i`, when backtracking
50
+ furthest, // furthest index the parser has gone to
51
+ chunks, // chunkified input
52
+ current, // index of current chunk, in `input`
53
+ parser;
54
+
55
+ var that = this;
56
+
57
+ // This function is called after all files
58
+ // have been imported through `@import`.
59
+ var finish = function () {};
60
+
61
+ var imports = this.imports = {
62
+ paths: env && env.paths || [], // Search paths, when importing
63
+ queue: [], // Files which haven't been imported yet
64
+ files: {}, // Holds the imported parse trees
65
+ mime: env && env.mime, // MIME type of .less files
66
+ push: function (path, callback) {
67
+ var that = this;
68
+ this.queue.push(path);
69
+
70
+ //
71
+ // Import a file asynchronously
72
+ //
73
+ less.Parser.importer(path, this.paths, function (root) {
74
+ that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue
75
+ that.files[path] = root; // Store the root
76
+
77
+ callback(root);
78
+
79
+ if (that.queue.length === 0) { finish() } // Call `finish` if we're done importing
80
+ }, env);
81
+ }
82
+ };
83
+
84
+ function save() { temp = chunks[j], memo = i, current = i }
85
+ function restore() { chunks[j] = temp, i = memo, current = i }
86
+
87
+ function sync() {
88
+ if (i > current) {
89
+ chunks[j] = chunks[j].slice(i - current);
90
+ current = i;
91
+ }
92
+ }
93
+ //
94
+ // Parse from a token, regexp or string, and move forward if match
95
+ //
96
+ function $(tok) {
97
+ var match, args, length, c, index, endIndex, k, mem;
98
+
99
+ //
100
+ // Non-terminal
101
+ //
102
+ if (tok instanceof Function) {
103
+ return tok.call(parser.parsers);
104
+ //
105
+ // Terminal
106
+ //
107
+ // Either match a single character in the input,
108
+ // or match a regexp in the current chunk (chunk[j]).
109
+ //
110
+ } else if (typeof(tok) === 'string') {
111
+ match = input.charAt(i) === tok ? tok : null;
112
+ length = 1;
113
+ sync ();
114
+ } else {
115
+ sync ();
116
+
117
+ if (match = tok.exec(chunks[j])) {
118
+ length = match[0].length;
119
+ } else {
120
+ return null;
121
+ }
122
+ }
123
+
124
+ // The match is confirmed, add the match length to `i`,
125
+ // and consume any extra white-space characters (' ' || '\n')
126
+ // which come after that. The reason for this is that LeSS's
127
+ // grammar is mostly white-space insensitive.
128
+ //
129
+ if (match) {
130
+ mem = i += length;
131
+ endIndex = i + chunks[j].length - length;
132
+
133
+ while (i < endIndex) {
134
+ c = input.charCodeAt(i);
135
+ if (! (c === 32 || c === 10 || c === 9)) { break }
136
+ i++;
137
+ }
138
+ chunks[j] = chunks[j].slice(length + (i - mem));
139
+ current = i;
140
+
141
+ if (chunks[j].length === 0 && j < chunks.length - 1) { j++ }
142
+
143
+ if(typeof(match) === 'string') {
144
+ return match;
145
+ } else {
146
+ return match.length === 1 ? match[0] : match;
147
+ }
148
+ }
149
+ }
150
+
151
+ // Same as $(), but don't change the state of the parser,
152
+ // just return the match.
153
+ function peek(tok) {
154
+ if (typeof(tok) === 'string') {
155
+ return input.charAt(i) === tok;
156
+ } else {
157
+ if (tok.test(chunks[j])) {
158
+ return true;
159
+ } else {
160
+ return false;
161
+ }
162
+ }
163
+ }
164
+
165
+ this.env = env = env || {};
166
+
167
+ // The optimization level dictates the thoroughness of the parser,
168
+ // the lower the number, the less nodes it will create in the tree.
169
+ // This could matter for debugging, or if you want to access
170
+ // the individual nodes in the tree.
171
+ this.optimization = ('optimization' in this.env) ? this.env.optimization : 1;
172
+
173
+ this.env.filename = this.env.filename || null;
174
+
175
+ //
176
+ // The Parser
177
+ //
178
+ return parser = {
179
+
180
+ imports: imports,
181
+ //
182
+ // Parse an input string into an abstract syntax tree,
183
+ // call `callback` when done.
184
+ //
185
+ parse: function (str, callback) {
186
+ var root, start, end, zone, line, lines, buff = [], c, error = null;
187
+
188
+ i = j = current = furthest = 0;
189
+ chunks = [];
190
+ input = str.replace(/\r\n/g, '\n');
191
+
192
+ // Split the input into chunks.
193
+ chunks = (function (chunks) {
194
+ var j = 0,
195
+ skip = /[^"'`\{\}\/\(\)]+/g,
196
+ comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,
197
+ level = 0,
198
+ match,
199
+ chunk = chunks[0],
200
+ inParam,
201
+ inString;
202
+
203
+ for (var i = 0, c, cc; i < input.length; i++) {
204
+ skip.lastIndex = i;
205
+ if (match = skip.exec(input)) {
206
+ if (match.index === i) {
207
+ i += match[0].length;
208
+ chunk.push(match[0]);
209
+ }
210
+ }
211
+ c = input.charAt(i);
212
+ comment.lastIndex = i;
213
+
214
+ if (!inString && !inParam && c === '/') {
215
+ cc = input.charAt(i + 1);
216
+ if (cc === '/' || cc === '*') {
217
+ if (match = comment.exec(input)) {
218
+ if (match.index === i) {
219
+ i += match[0].length;
220
+ chunk.push(match[0]);
221
+ c = input.charAt(i);
222
+ }
223
+ }
224
+ }
225
+ }
226
+
227
+ if (c === '{' && !inString && !inParam) { level ++;
228
+ chunk.push(c);
229
+ } else if (c === '}' && !inString && !inParam) { level --;
230
+ chunk.push(c);
231
+ chunks[++j] = chunk = [];
232
+ } else if (c === '(' && !inString && !inParam) {
233
+ chunk.push(c);
234
+ inParam = true;
235
+ } else if (c === ')' && !inString && inParam) {
236
+ chunk.push(c);
237
+ inParam = false;
238
+ } else {
239
+ if (c === '"' || c === "'" || c === '`') {
240
+ if (! inString) {
241
+ inString = c;
242
+ } else {
243
+ inString = inString === c ? false : inString;
244
+ }
245
+ }
246
+ chunk.push(c);
247
+ }
248
+ }
249
+ if (level > 0) {
250
+ throw {
251
+ type: 'Syntax',
252
+ message: "Missing closing `}`",
253
+ filename: env.filename
254
+ };
255
+ }
256
+
257
+ return chunks.map(function (c) { return c.join('') });;
258
+ })([[]]);
259
+
260
+ // Start with the primary rule.
261
+ // The whole syntax tree is held under a Ruleset node,
262
+ // with the `root` property set to true, so no `{}` are
263
+ // output. The callback is called when the input is parsed.
264
+ root = new(tree.Ruleset)([], $(this.parsers.primary));
265
+ root.root = true;
266
+
267
+ root.toCSS = (function (evaluate) {
268
+ var line, lines, column;
269
+
270
+ return function (options, variables) {
271
+ var frames = [];
272
+
273
+ options = options || {};
274
+ //
275
+ // Allows setting variables with a hash, so:
276
+ //
277
+ // `{ color: new(tree.Color)('#f01') }` will become:
278
+ //
279
+ // new(tree.Rule)('@color',
280
+ // new(tree.Value)([
281
+ // new(tree.Expression)([
282
+ // new(tree.Color)('#f01')
283
+ // ])
284
+ // ])
285
+ // )
286
+ //
287
+ if (typeof(variables) === 'object' && !Array.isArray(variables)) {
288
+ variables = Object.keys(variables).map(function (k) {
289
+ var value = variables[k];
290
+
291
+ if (! (value instanceof tree.Value)) {
292
+ if (! (value instanceof tree.Expression)) {
293
+ value = new(tree.Expression)([value]);
294
+ }
295
+ value = new(tree.Value)([value]);
296
+ }
297
+ return new(tree.Rule)('@' + k, value, false, 0);
298
+ });
299
+ frames = [new(tree.Ruleset)(null, variables)];
300
+ }
301
+
302
+ try {
303
+ var css = evaluate.call(this, { frames: frames })
304
+ .toCSS([], { compress: options.compress || false });
305
+ } catch (e) {
306
+ lines = input.split('\n');
307
+ line = getLine(e.index);
308
+
309
+ for (var n = e.index, column = -1;
310
+ n >= 0 && input.charAt(n) !== '\n';
311
+ n--) { column++ }
312
+
313
+ throw {
314
+ type: e.type,
315
+ message: e.message,
316
+ filename: env.filename,
317
+ index: e.index,
318
+ line: typeof(line) === 'number' ? line + 1 : null,
319
+ callLine: e.call && (getLine(e.call) + 1),
320
+ callExtract: lines[getLine(e.call)],
321
+ stack: e.stack,
322
+ column: column,
323
+ extract: [
324
+ lines[line - 1],
325
+ lines[line],
326
+ lines[line + 1]
327
+ ]
328
+ };
329
+ }
330
+ if (options.compress) {
331
+ return css.replace(/(\s)+/g, "$1");
332
+ } else {
333
+ return css;
334
+ }
335
+
336
+ function getLine(index) {
337
+ return index ? (input.slice(0, index).match(/\n/g) || "").length : null;
338
+ }
339
+ };
340
+ })(root.eval);
341
+
342
+ // If `i` is smaller than the `input.length - 1`,
343
+ // it means the parser wasn't able to parse the whole
344
+ // string, so we've got a parsing error.
345
+ //
346
+ // We try to extract a \n delimited string,
347
+ // showing the line where the parse error occured.
348
+ // We split it up into two parts (the part which parsed,
349
+ // and the part which didn't), so we can color them differently.
350
+ if (i < input.length - 1) {
351
+ i = furthest;
352
+ lines = input.split('\n');
353
+ line = (input.slice(0, i).match(/\n/g) || "").length + 1;
354
+
355
+ for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ }
356
+
357
+ error = {
358
+ name: "ParseError",
359
+ message: "Syntax Error on line " + line,
360
+ index: i,
361
+ filename: env.filename,
362
+ line: line,
363
+ column: column,
364
+ extract: [
365
+ lines[line - 2],
366
+ lines[line - 1],
367
+ lines[line]
368
+ ]
369
+ };
370
+ }
371
+
372
+ if (this.imports.queue.length > 0) {
373
+ finish = function () { callback(error, root) };
374
+ } else {
375
+ callback(error, root);
376
+ }
377
+ },
378
+
379
+ //
380
+ // Here in, the parsing rules/functions
381
+ //
382
+ // The basic structure of the syntax tree generated is as follows:
383
+ //
384
+ // Ruleset -> Rule -> Value -> Expression -> Entity
385
+ //
386
+ // Here's some LESS code:
387
+ //
388
+ // .class {
389
+ // color: #fff;
390
+ // border: 1px solid #000;
391
+ // width: @w + 4px;
392
+ // > .child {...}
393
+ // }
394
+ //
395
+ // And here's what the parse tree might look like:
396
+ //
397
+ // Ruleset (Selector '.class', [
398
+ // Rule ("color", Value ([Expression [Color #fff]]))
399
+ // Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
400
+ // Rule ("width", Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]]))
401
+ // Ruleset (Selector [Element '>', '.child'], [...])
402
+ // ])
403
+ //
404
+ // In general, most rules will try to parse a token with the `$()` function, and if the return
405
+ // value is truly, will return a new node, of the relevant type. Sometimes, we need to check
406
+ // first, before parsing, that's when we use `peek()`.
407
+ //
408
+ parsers: {
409
+ //
410
+ // The `primary` rule is the *entry* and *exit* point of the parser.
411
+ // The rules here can appear at any level of the parse tree.
412
+ //
413
+ // The recursive nature of the grammar is an interplay between the `block`
414
+ // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
415
+ // as represented by this simplified grammar:
416
+ //
417
+ // primary → (ruleset | rule)+
418
+ // ruleset → selector+ block
419
+ // block → '{' primary '}'
420
+ //
421
+ // Only at one point is the primary rule not called from the
422
+ // block rule: at the root level.
423
+ //
424
+ primary: function () {
425
+ var node, root = [];
426
+
427
+ while ((node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) ||
428
+ $(this.mixin.call) || $(this.comment) || $(this.directive))
429
+ || $(/^[\s\n]+/)) {
430
+ node && root.push(node);
431
+ }
432
+ return root;
433
+ },
434
+
435
+ // We create a Comment node for CSS comments `/* */`,
436
+ // but keep the LeSS comments `//` silent, by just skipping
437
+ // over them.
438
+ comment: function () {
439
+ var comment;
440
+
441
+ if (input.charAt(i) !== '/') return;
442
+
443
+ if (input.charAt(i + 1) === '/') {
444
+ return new(tree.Comment)($(/^\/\/.*/), true);
445
+ } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) {
446
+ return new(tree.Comment)(comment);
447
+ }
448
+ },
449
+
450
+ //
451
+ // Entities are tokens which can be found inside an Expression
452
+ //
453
+ entities: {
454
+ //
455
+ // A string, which supports escaping " and '
456
+ //
457
+ // "milky way" 'he\'s the one!'
458
+ //
459
+ quoted: function () {
460
+ var str, j = i, e;
461
+
462
+ if (input.charAt(j) === '~') { j++, e = true } // Escaped strings
463
+ if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return;
464
+
465
+ e && $('~');
466
+
467
+ if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) {
468
+ return new(tree.Quoted)(str[0], str[1] || str[2], e);
469
+ }
470
+ },
471
+
472
+ //
473
+ // A catch-all word, such as:
474
+ //
475
+ // black border-collapse
476
+ //
477
+ keyword: function () {
478
+ var k;
479
+ if (k = $(/^[A-Za-z-]+/)) { return new(tree.Keyword)(k) }
480
+ },
481
+
482
+ //
483
+ // A function call
484
+ //
485
+ // rgb(255, 0, 255)
486
+ //
487
+ // We also try to catch IE's `alpha()`, but let the `alpha` parser
488
+ // deal with the details.
489
+ //
490
+ // The arguments are parsed with the `entities.arguments` parser.
491
+ //
492
+ call: function () {
493
+ var name, args, index = i;
494
+
495
+ if (! (name = /^([\w-]+|%)\(/.exec(chunks[j]))) return;
496
+
497
+ name = name[1].toLowerCase();
498
+
499
+ if (name === 'url') { return null }
500
+ else { i += name.length }
501
+
502
+ if (name === 'alpha') { return $(this.alpha) }
503
+
504
+ $('('); // Parse the '(' and consume whitespace.
505
+
506
+ args = $(this.entities.arguments);
507
+
508
+ if (! $(')')) return;
509
+
510
+ if (name) { return new(tree.Call)(name, args, index) }
511
+ },
512
+ arguments: function () {
513
+ var args = [], arg;
514
+
515
+ while (arg = $(this.expression)) {
516
+ args.push(arg);
517
+ if (! $(',')) { break }
518
+ }
519
+ return args;
520
+ },
521
+ literal: function () {
522
+ return $(this.entities.dimension) ||
523
+ $(this.entities.color) ||
524
+ $(this.entities.quoted);
525
+ },
526
+
527
+ //
528
+ // Parse url() tokens
529
+ //
530
+ // We use a specific rule for urls, because they don't really behave like
531
+ // standard function calls. The difference is that the argument doesn't have
532
+ // to be enclosed within a string, so it can't be parsed as an Expression.
533
+ //
534
+ url: function () {
535
+ var value;
536
+
537
+ if (input.charAt(i) !== 'u' || !$(/^url\(/)) return;
538
+ value = $(this.entities.quoted) || $(this.entities.variable) ||
539
+ $(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || "";
540
+ if (! $(')')) throw new(Error)("missing closing ) for url()");
541
+
542
+ return new(tree.URL)((value.value || value.data || value instanceof tree.Variable)
543
+ ? value : new(tree.Anonymous)(value), imports.paths);
544
+ },
545
+
546
+ dataURI: function () {
547
+ var obj;
548
+
549
+ if ($(/^data:/)) {
550
+ obj = {};
551
+ obj.mime = $(/^[^\/]+\/[^,;)]+/) || '';
552
+ obj.charset = $(/^;\s*charset=[^,;)]+/) || '';
553
+ obj.base64 = $(/^;\s*base64/) || '';
554
+ obj.data = $(/^,\s*[^)]+/);
555
+
556
+ if (obj.data) { return obj }
557
+ }
558
+ },
559
+
560
+ //
561
+ // A Variable entity, such as `@fink`, in
562
+ //
563
+ // width: @fink + 2px
564
+ //
565
+ // We use a different parser for variable definitions,
566
+ // see `parsers.variable`.
567
+ //
568
+ variable: function () {
569
+ var name, index = i;
570
+
571
+ if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) {
572
+ return new(tree.Variable)(name, index);
573
+ }
574
+ },
575
+
576
+ //
577
+ // A Hexadecimal color
578
+ //
579
+ // #4F3C2F
580
+ //
581
+ // `rgb` and `hsl` colors are parsed through the `entities.call` parser.
582
+ //
583
+ color: function () {
584
+ var rgb;
585
+
586
+ if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) {
587
+ return new(tree.Color)(rgb[1]);
588
+ }
589
+ },
590
+
591
+ //
592
+ // A Dimension, that is, a number and a unit
593
+ //
594
+ // 0.5em 95%
595
+ //
596
+ dimension: function () {
597
+ var value, c = input.charCodeAt(i);
598
+ if ((c > 57 || c < 45) || c === 47) return;
599
+
600
+ if (value = $(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/)) {
601
+ return new(tree.Dimension)(value[1], value[2]);
602
+ }
603
+ },
604
+
605
+ //
606
+ // JavaScript code to be evaluated
607
+ //
608
+ // `window.location.href`
609
+ //
610
+ javascript: function () {
611
+ var str, j = i, e;
612
+
613
+ if (input.charAt(j) === '~') { j++, e = true } // Escaped strings
614
+ if (input.charAt(j) !== '`') { return }
615
+
616
+ e && $('~');
617
+
618
+ if (str = $(/^`([^`]*)`/)) {
619
+ return new(tree.JavaScript)(str[1], i, e);
620
+ }
621
+ }
622
+ },
623
+
624
+ //
625
+ // The variable part of a variable definition. Used in the `rule` parser
626
+ //
627
+ // @fink:
628
+ //
629
+ variable: function () {
630
+ var name;
631
+
632
+ if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] }
633
+ },
634
+
635
+ //
636
+ // A font size/line-height shorthand
637
+ //
638
+ // small/12px
639
+ //
640
+ // We need to peek first, or we'll match on keywords and dimensions
641
+ //
642
+ shorthand: function () {
643
+ var a, b;
644
+
645
+ if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return;
646
+
647
+ if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) {
648
+ return new(tree.Shorthand)(a, b);
649
+ }
650
+ },
651
+
652
+ //
653
+ // Mixins
654
+ //
655
+ mixin: {
656
+ //
657
+ // A Mixin call, with an optional argument list
658
+ //
659
+ // #mixins > .square(#fff);
660
+ // .rounded(4px, black);
661
+ // .button;
662
+ //
663
+ // The `while` loop is there because mixins can be
664
+ // namespaced, but we only support the child and descendant
665
+ // selector for now.
666
+ //
667
+ call: function () {
668
+ var elements = [], e, c, args, index = i, s = input.charAt(i);
669
+
670
+ if (s !== '.' && s !== '#') { return }
671
+
672
+ while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) {
673
+ elements.push(new(tree.Element)(c, e));
674
+ c = $('>');
675
+ }
676
+ $('(') && (args = $(this.entities.arguments)) && $(')');
677
+
678
+ if (elements.length > 0 && ($(';') || peek('}'))) {
679
+ return new(tree.mixin.Call)(elements, args, index);
680
+ }
681
+ },
682
+
683
+ //
684
+ // A Mixin definition, with a list of parameters
685
+ //
686
+ // .rounded (@radius: 2px, @color) {
687
+ // ...
688
+ // }
689
+ //
690
+ // Until we have a finer grained state-machine, we have to
691
+ // do a look-ahead, to make sure we don't have a mixin call.
692
+ // See the `rule` function for more information.
693
+ //
694
+ // We start by matching `.rounded (`, and then proceed on to
695
+ // the argument list, which has optional default values.
696
+ // We store the parameters in `params`, with a `value` key,
697
+ // if there is a value, such as in the case of `@radius`.
698
+ //
699
+ // Once we've got our params list, and a closing `)`, we parse
700
+ // the `{...}` block.
701
+ //
702
+ definition: function () {
703
+ var name, params = [], match, ruleset, param, value;
704
+
705
+ if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') ||
706
+ peek(/^[^{]*(;|})/)) return;
707
+
708
+ if (match = $(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)) {
709
+ name = match[1];
710
+
711
+ while (param = $(this.entities.variable) || $(this.entities.literal)
712
+ || $(this.entities.keyword)) {
713
+ // Variable
714
+ if (param instanceof tree.Variable) {
715
+ if ($(':')) {
716
+ if (value = $(this.expression)) {
717
+ params.push({ name: param.name, value: value });
718
+ } else {
719
+ throw new(Error)("Expected value");
720
+ }
721
+ } else {
722
+ params.push({ name: param.name });
723
+ }
724
+ } else {
725
+ params.push({ value: param });
726
+ }
727
+ if (! $(',')) { break }
728
+ }
729
+ if (! $(')')) throw new(Error)("Expected )");
730
+
731
+ ruleset = $(this.block);
732
+
733
+ if (ruleset) {
734
+ return new(tree.mixin.Definition)(name, params, ruleset);
735
+ }
736
+ }
737
+ }
738
+ },
739
+
740
+ //
741
+ // Entities are the smallest recognized token,
742
+ // and can be found inside a rule's value.
743
+ //
744
+ entity: function () {
745
+ return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) ||
746
+ $(this.entities.call) || $(this.entities.keyword) || $(this.entities.javascript) ||
747
+ $(this.comment);
748
+ },
749
+
750
+ //
751
+ // A Rule terminator. Note that we use `peek()` to check for '}',
752
+ // because the `block` rule will be expecting it, but we still need to make sure
753
+ // it's there, if ';' was ommitted.
754
+ //
755
+ end: function () {
756
+ return $(';') || peek('}');
757
+ },
758
+
759
+ //
760
+ // IE's alpha function
761
+ //
762
+ // alpha(opacity=88)
763
+ //
764
+ alpha: function () {
765
+ var value;
766
+
767
+ if (! $(/^\(opacity=/i)) return;
768
+ if (value = $(/^\d+/) || $(this.entities.variable)) {
769
+ if (! $(')')) throw new(Error)("missing closing ) for alpha()");
770
+ return new(tree.Alpha)(value);
771
+ }
772
+ },
773
+
774
+ //
775
+ // A Selector Element
776
+ //
777
+ // div
778
+ // + h1
779
+ // #socks
780
+ // input[type="text"]
781
+ //
782
+ // Elements are the building blocks for Selectors,
783
+ // they are made out of a `Combinator` (see combinator rule),
784
+ // and an element name, such as a tag a class, or `*`.
785
+ //
786
+ element: function () {
787
+ var e, t, c;
788
+
789
+ c = $(this.combinator);
790
+ e = $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) || $('*') || $(this.attribute) || $(/^\([^)@]+\)/);
791
+
792
+ if (e) { return new(tree.Element)(c, e) }
793
+ },
794
+
795
+ //
796
+ // Combinators combine elements together, in a Selector.
797
+ //
798
+ // Because our parser isn't white-space sensitive, special care
799
+ // has to be taken, when parsing the descendant combinator, ` `,
800
+ // as it's an empty space. We have to check the previous character
801
+ // in the input, to see if it's a ` ` character. More info on how
802
+ // we deal with this in *combinator.js*.
803
+ //
804
+ combinator: function () {
805
+ var match, c = input.charAt(i);
806
+
807
+ if (c === '>' || c === '&' || c === '+' || c === '~') {
808
+ i++;
809
+ while (input.charAt(i) === ' ') { i++ }
810
+ return new(tree.Combinator)(c);
811
+ } else if (c === ':' && input.charAt(i + 1) === ':') {
812
+ i += 2;
813
+ while (input.charAt(i) === ' ') { i++ }
814
+ return new(tree.Combinator)('::');
815
+ } else if (input.charAt(i - 1) === ' ') {
816
+ return new(tree.Combinator)(" ");
817
+ } else {
818
+ return new(tree.Combinator)(null);
819
+ }
820
+ },
821
+
822
+ //
823
+ // A CSS Selector
824
+ //
825
+ // .class > div + h1
826
+ // li a:hover
827
+ //
828
+ // Selectors are made out of one or more Elements, see above.
829
+ //
830
+ selector: function () {
831
+ var sel, e, elements = [], c, match;
832
+
833
+ while (e = $(this.element)) {
834
+ c = input.charAt(i);
835
+ elements.push(e)
836
+ if (c === '{' || c === '}' || c === ';' || c === ',') { break }
837
+ }
838
+
839
+ if (elements.length > 0) { return new(tree.Selector)(elements) }
840
+ },
841
+ tag: function () {
842
+ return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*');
843
+ },
844
+ attribute: function () {
845
+ var attr = '', key, val, op;
846
+
847
+ if (! $('[')) return;
848
+
849
+ if (key = $(/^[a-zA-Z-]+/) || $(this.entities.quoted)) {
850
+ if ((op = $(/^[|~*$^]?=/)) &&
851
+ (val = $(this.entities.quoted) || $(/^[\w-]+/))) {
852
+ attr = [key, op, val.toCSS ? val.toCSS() : val].join('');
853
+ } else { attr = key }
854
+ }
855
+
856
+ if (! $(']')) return;
857
+
858
+ if (attr) { return "[" + attr + "]" }
859
+ },
860
+
861
+ //
862
+ // The `block` rule is used by `ruleset` and `mixin.definition`.
863
+ // It's a wrapper around the `primary` rule, with added `{}`.
864
+ //
865
+ block: function () {
866
+ var content;
867
+
868
+ if ($('{') && (content = $(this.primary)) && $('}')) {
869
+ return content;
870
+ }
871
+ },
872
+
873
+ //
874
+ // div, .class, body > p {...}
875
+ //
876
+ ruleset: function () {
877
+ var selectors = [], s, rules, match;
878
+ save();
879
+
880
+ if (match = /^([.#: \w-]+)[\s\n]*\{/.exec(chunks[j])) {
881
+ i += match[0].length - 1;
882
+ selectors = [new(tree.Selector)([new(tree.Element)(null, match[1])])];
883
+ } else {
884
+ while (s = $(this.selector)) {
885
+ selectors.push(s);
886
+ $(this.comment);
887
+ if (! $(',')) { break }
888
+ $(this.comment);
889
+ }
890
+ }
891
+
892
+ if (selectors.length > 0 && (rules = $(this.block))) {
893
+ return new(tree.Ruleset)(selectors, rules);
894
+ } else {
895
+ // Backtrack
896
+ furthest = i;
897
+ restore();
898
+ }
899
+ },
900
+ rule: function () {
901
+ var name, value, c = input.charAt(i), important, match;
902
+ save();
903
+
904
+ if (c === '.' || c === '#' || c === '&') { return }
905
+
906
+ if (name = $(this.variable) || $(this.property)) {
907
+ if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) {
908
+ i += match[0].length - 1;
909
+ value = new(tree.Anonymous)(match[1]);
910
+ } else if (name === "font") {
911
+ value = $(this.font);
912
+ } else {
913
+ value = $(this.value);
914
+ }
915
+ important = $(this.important);
916
+
917
+ if (value && $(this.end)) {
918
+ return new(tree.Rule)(name, value, important, memo);
919
+ } else {
920
+ furthest = i;
921
+ restore();
922
+ }
923
+ }
924
+ },
925
+
926
+ //
927
+ // An @import directive
928
+ //
929
+ // @import "lib";
930
+ //
931
+ // Depending on our environemnt, importing is done differently:
932
+ // In the browser, it's an XHR request, in Node, it would be a
933
+ // file-system operation. The function used for importing is
934
+ // stored in `import`, which we pass to the Import constructor.
935
+ //
936
+ "import": function () {
937
+ var path;
938
+ if ($(/^@import\s+/) &&
939
+ (path = $(this.entities.quoted) || $(this.entities.url)) &&
940
+ $(';')) {
941
+ return new(tree.Import)(path, imports);
942
+ }
943
+ },
944
+
945
+ //
946
+ // A CSS Directive
947
+ //
948
+ // @charset "utf-8";
949
+ //
950
+ directive: function () {
951
+ var name, value, rules, types;
952
+
953
+ if (input.charAt(i) !== '@') return;
954
+
955
+ if (value = $(this['import'])) {
956
+ return value;
957
+ } else if (name = $(/^@media|@page|@-[-a-z]+/)) {
958
+ types = ($(/^[^{]+/) || '').trim();
959
+ if (rules = $(this.block)) {
960
+ return new(tree.Directive)(name + " " + types, rules);
961
+ }
962
+ } else if (name = $(/^@[-a-z]+/)) {
963
+ if (name === '@font-face') {
964
+ if (rules = $(this.block)) {
965
+ return new(tree.Directive)(name, rules);
966
+ }
967
+ } else if ((value = $(this.entity)) && $(';')) {
968
+ return new(tree.Directive)(name, value);
969
+ }
970
+ }
971
+ },
972
+ font: function () {
973
+ var value = [], expression = [], weight, shorthand, font, e;
974
+
975
+ while (e = $(this.shorthand) || $(this.entity)) {
976
+ expression.push(e);
977
+ }
978
+ value.push(new(tree.Expression)(expression));
979
+
980
+ if ($(',')) {
981
+ while (e = $(this.expression)) {
982
+ value.push(e);
983
+ if (! $(',')) { break }
984
+ }
985
+ }
986
+ return new(tree.Value)(value);
987
+ },
988
+
989
+ //
990
+ // A Value is a comma-delimited list of Expressions
991
+ //
992
+ // font-family: Baskerville, Georgia, serif;
993
+ //
994
+ // In a Rule, a Value represents everything after the `:`,
995
+ // and before the `;`.
996
+ //
997
+ value: function () {
998
+ var e, expressions = [], important;
999
+
1000
+ while (e = $(this.expression)) {
1001
+ expressions.push(e);
1002
+ if (! $(',')) { break }
1003
+ }
1004
+
1005
+ if (expressions.length > 0) {
1006
+ return new(tree.Value)(expressions);
1007
+ }
1008
+ },
1009
+ important: function () {
1010
+ if (input.charAt(i) === '!') {
1011
+ return $(/^! *important/);
1012
+ }
1013
+ },
1014
+ sub: function () {
1015
+ var e;
1016
+
1017
+ if ($('(') && (e = $(this.expression)) && $(')')) {
1018
+ return e;
1019
+ }
1020
+ },
1021
+ multiplication: function () {
1022
+ var m, a, op, operation;
1023
+ if (m = $(this.operand)) {
1024
+ while ((op = ($('/') || $('*'))) && (a = $(this.operand))) {
1025
+ operation = new(tree.Operation)(op, [operation || m, a]);
1026
+ }
1027
+ return operation || m;
1028
+ }
1029
+ },
1030
+ addition: function () {
1031
+ var m, a, op, operation;
1032
+ if (m = $(this.multiplication)) {
1033
+ while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) &&
1034
+ (a = $(this.multiplication))) {
1035
+ operation = new(tree.Operation)(op, [operation || m, a]);
1036
+ }
1037
+ return operation || m;
1038
+ }
1039
+ },
1040
+
1041
+ //
1042
+ // An operand is anything that can be part of an operation,
1043
+ // such as a Color, or a Variable
1044
+ //
1045
+ operand: function () {
1046
+ var negate, p = input.charAt(i + 1);
1047
+
1048
+ if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') }
1049
+ var o = $(this.sub) || $(this.entities.dimension) ||
1050
+ $(this.entities.color) || $(this.entities.variable) ||
1051
+ $(this.entities.call);
1052
+ return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o])
1053
+ : o;
1054
+ },
1055
+
1056
+ //
1057
+ // Expressions either represent mathematical operations,
1058
+ // or white-space delimited Entities.
1059
+ //
1060
+ // 1px solid black
1061
+ // @var * 2
1062
+ //
1063
+ expression: function () {
1064
+ var e, delim, entities = [], d;
1065
+
1066
+ while (e = $(this.addition) || $(this.entity)) {
1067
+ entities.push(e);
1068
+ }
1069
+ if (entities.length > 0) {
1070
+ return new(tree.Expression)(entities);
1071
+ }
1072
+ },
1073
+ property: function () {
1074
+ var name;
1075
+
1076
+ if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) {
1077
+ return name[1];
1078
+ }
1079
+ }
1080
+ }
1081
+ };
1082
+ };
1083
+
1084
+ if (typeof(window) !== 'undefined') {
1085
+ //
1086
+ // Used by `@import` directives
1087
+ //
1088
+ less.Parser.importer = function (path, paths, callback, env) {
1089
+ if (path.charAt(0) !== '/' && paths.length > 0) {
1090
+ path = paths[0] + path;
1091
+ }
1092
+ // We pass `true` as 3rd argument, to force the reload of the import.
1093
+ // This is so we can get the syntax tree as opposed to just the CSS output,
1094
+ // as we need this to evaluate the current stylesheet.
1095
+ loadStyleSheet({ href: path, title: path, type: env.mime }, callback, true);
1096
+ };
1097
+ }
1098
+