~george-edison55/less/trunk

« back to all changes in this revision

Viewing changes to dist/less-rhino-1.1.5.js

  • Committer: Nathan Osman
  • Date: 2013-04-16 22:43:51 UTC
  • Revision ID: admin@quickmediasolutions.com-20130416224351-5juqujuu4itkwpat
Initial commit.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//
 
2
// Stub out `require` in rhino
 
3
//
 
4
function require(arg) {
 
5
    return less[arg.split('/')[1]];
 
6
};
 
7
 
 
8
 
 
9
// ecma-5.js
 
10
//
 
11
// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License
 
12
// -- tlrobinson Tom Robinson
 
13
// dantman Daniel Friesen
 
14
 
 
15
//
 
16
// Array
 
17
//
 
18
if (!Array.isArray) {
 
19
    Array.isArray = function(obj) {
 
20
        return Object.prototype.toString.call(obj) === "[object Array]" ||
 
21
               (obj instanceof Array);
 
22
    };
 
23
}
 
24
if (!Array.prototype.forEach) {
 
25
    Array.prototype.forEach =  function(block, thisObject) {
 
26
        var len = this.length >>> 0;
 
27
        for (var i = 0; i < len; i++) {
 
28
            if (i in this) {
 
29
                block.call(thisObject, this[i], i, this);
 
30
            }
 
31
        }
 
32
    };
 
33
}
 
34
if (!Array.prototype.map) {
 
35
    Array.prototype.map = function(fun /*, thisp*/) {
 
36
        var len = this.length >>> 0;
 
37
        var res = new Array(len);
 
38
        var thisp = arguments[1];
 
39
 
 
40
        for (var i = 0; i < len; i++) {
 
41
            if (i in this) {
 
42
                res[i] = fun.call(thisp, this[i], i, this);
 
43
            }
 
44
        }
 
45
        return res;
 
46
    };
 
47
}
 
48
if (!Array.prototype.filter) {
 
49
    Array.prototype.filter = function (block /*, thisp */) {
 
50
        var values = [];
 
51
        var thisp = arguments[1];
 
52
        for (var i = 0; i < this.length; i++) {
 
53
            if (block.call(thisp, this[i])) {
 
54
                values.push(this[i]);
 
55
            }
 
56
        }
 
57
        return values;
 
58
    };
 
59
}
 
60
if (!Array.prototype.reduce) {
 
61
    Array.prototype.reduce = function(fun /*, initial*/) {
 
62
        var len = this.length >>> 0;
 
63
        var i = 0;
 
64
 
 
65
        // no value to return if no initial value and an empty array
 
66
        if (len === 0 && arguments.length === 1) throw new TypeError();
 
67
 
 
68
        if (arguments.length >= 2) {
 
69
            var rv = arguments[1];
 
70
        } else {
 
71
            do {
 
72
                if (i in this) {
 
73
                    rv = this[i++];
 
74
                    break;
 
75
                }
 
76
                // if array contains no values, no initial value to return
 
77
                if (++i >= len) throw new TypeError();
 
78
            } while (true);
 
79
        }
 
80
        for (; i < len; i++) {
 
81
            if (i in this) {
 
82
                rv = fun.call(null, rv, this[i], i, this);
 
83
            }
 
84
        }
 
85
        return rv;
 
86
    };
 
87
}
 
88
if (!Array.prototype.indexOf) {
 
89
    Array.prototype.indexOf = function (value /*, fromIndex */ ) {
 
90
        var length = this.length;
 
91
        var i = arguments[1] || 0;
 
92
 
 
93
        if (!length)     return -1;
 
94
        if (i >= length) return -1;
 
95
        if (i < 0)       i += length;
 
96
 
 
97
        for (; i < length; i++) {
 
98
            if (!Object.prototype.hasOwnProperty.call(this, i)) { continue }
 
99
            if (value === this[i]) return i;
 
100
        }
 
101
        return -1;
 
102
    };
 
103
}
 
104
 
 
105
//
 
106
// Object
 
107
//
 
108
if (!Object.keys) {
 
109
    Object.keys = function (object) {
 
110
        var keys = [];
 
111
        for (var name in object) {
 
112
            if (Object.prototype.hasOwnProperty.call(object, name)) {
 
113
                keys.push(name);
 
114
            }
 
115
        }
 
116
        return keys;
 
117
    };
 
118
}
 
119
 
 
120
//
 
121
// String
 
122
//
 
123
if (!String.prototype.trim) {
 
124
    String.prototype.trim = function () {
 
125
        return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, '');
 
126
    };
 
127
}
 
128
var less, tree;
 
129
 
 
130
if (typeof environment === "object" && ({}).toString.call(environment) === "[object Environment]") {
 
131
    // Rhino
 
132
    // Details on how to detect Rhino: https://github.com/ringo/ringojs/issues/88
 
133
    less = {};
 
134
    tree = less.tree = {};
 
135
    less.mode = 'rhino';
 
136
} else if (typeof(window) === 'undefined') {
 
137
    // Node.js
 
138
    less = exports,
 
139
    tree = require('./tree');
 
140
    less.mode = 'rhino';
 
141
} else {
 
142
    // Browser
 
143
    if (typeof(window.less) === 'undefined') { window.less = {} }
 
144
    less = window.less,
 
145
    tree = window.less.tree = {};
 
146
    less.mode = 'browser';
 
147
}
 
148
//
 
149
// less.js - parser
 
150
//
 
151
//    A relatively straight-forward predictive parser.
 
152
//    There is no tokenization/lexing stage, the input is parsed
 
153
//    in one sweep.
 
154
//
 
155
//    To make the parser fast enough to run in the browser, several
 
156
//    optimization had to be made:
 
157
//
 
158
//    - Matching and slicing on a huge input is often cause of slowdowns.
 
159
//      The solution is to chunkify the input into smaller strings.
 
160
//      The chunks are stored in the `chunks` var,
 
161
//      `j` holds the current chunk index, and `current` holds
 
162
//      the index of the current chunk in relation to `input`.
 
163
//      This gives us an almost 4x speed-up.
 
164
//
 
165
//    - In many cases, we don't need to match individual tokens;
 
166
//      for example, if a value doesn't hold any variables, operations
 
167
//      or dynamic references, the parser can effectively 'skip' it,
 
168
//      treating it as a literal.
 
169
//      An example would be '1px solid #000' - which evaluates to itself,
 
170
//      we don't need to know what the individual components are.
 
171
//      The drawback, of course is that you don't get the benefits of
 
172
//      syntax-checking on the CSS. This gives us a 50% speed-up in the parser,
 
173
//      and a smaller speed-up in the code-gen.
 
174
//
 
175
//
 
176
//    Token matching is done with the `$` function, which either takes
 
177
//    a terminal string or regexp, or a non-terminal function to call.
 
178
//    It also takes care of moving all the indices forwards.
 
179
//
 
180
//
 
181
less.Parser = function Parser(env) {
 
182
    var input,       // LeSS input string
 
183
        i,           // current index in `input`
 
184
        j,           // current chunk
 
185
        temp,        // temporarily holds a chunk's state, for backtracking
 
186
        memo,        // temporarily holds `i`, when backtracking
 
187
        furthest,    // furthest index the parser has gone to
 
188
        chunks,      // chunkified input
 
189
        current,     // index of current chunk, in `input`
 
190
        parser;
 
191
 
 
192
    var that = this;
 
193
 
 
194
    // This function is called after all files
 
195
    // have been imported through `@import`.
 
196
    var finish = function () {};
 
197
 
 
198
    var imports = this.imports = {
 
199
        paths: env && env.paths || [],  // Search paths, when importing
 
200
        queue: [],                      // Files which haven't been imported yet
 
201
        files: {},                      // Holds the imported parse trees
 
202
        mime:  env && env.mime,         // MIME type of .less files
 
203
        push: function (path, callback) {
 
204
            var that = this;
 
205
            this.queue.push(path);
 
206
 
 
207
            //
 
208
            // Import a file asynchronously
 
209
            //
 
210
            less.Parser.importer(path, this.paths, function (root) {
 
211
                that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue
 
212
                that.files[path] = root;                        // Store the root
 
213
 
 
214
                callback(root);
 
215
 
 
216
                if (that.queue.length === 0) { finish() }       // Call `finish` if we're done importing
 
217
            }, env);
 
218
        }
 
219
    };
 
220
 
 
221
    function save()    { temp = chunks[j], memo = i, current = i }
 
222
    function restore() { chunks[j] = temp, i = memo, current = i }
 
223
 
 
224
    function sync() {
 
225
        if (i > current) {
 
226
            chunks[j] = chunks[j].slice(i - current);
 
227
            current = i;
 
228
        }
 
229
    }
 
230
    //
 
231
    // Parse from a token, regexp or string, and move forward if match
 
232
    //
 
233
    function $(tok) {
 
234
        var match, args, length, c, index, endIndex, k, mem;
 
235
 
 
236
        //
 
237
        // Non-terminal
 
238
        //
 
239
        if (tok instanceof Function) {
 
240
            return tok.call(parser.parsers);
 
241
        //
 
242
        // Terminal
 
243
        //
 
244
        //     Either match a single character in the input,
 
245
        //     or match a regexp in the current chunk (chunk[j]).
 
246
        //
 
247
        } else if (typeof(tok) === 'string') {
 
248
            match = input.charAt(i) === tok ? tok : null;
 
249
            length = 1;
 
250
            sync ();
 
251
        } else {
 
252
            sync ();
 
253
 
 
254
            if (match = tok.exec(chunks[j])) {
 
255
                length = match[0].length;
 
256
            } else {
 
257
                return null;
 
258
            }
 
259
        }
 
260
 
 
261
        // The match is confirmed, add the match length to `i`,
 
262
        // and consume any extra white-space characters (' ' || '\n')
 
263
        // which come after that. The reason for this is that LeSS's
 
264
        // grammar is mostly white-space insensitive.
 
265
        //
 
266
        if (match) {
 
267
            mem = i += length;
 
268
            endIndex = i + chunks[j].length - length;
 
269
 
 
270
            while (i < endIndex) {
 
271
                c = input.charCodeAt(i);
 
272
                if (! (c === 32 || c === 10 || c === 9)) { break }
 
273
                i++;
 
274
            }
 
275
            chunks[j] = chunks[j].slice(length + (i - mem));
 
276
            current = i;
 
277
 
 
278
            if (chunks[j].length === 0 && j < chunks.length - 1) { j++ }
 
279
 
 
280
            if(typeof(match) === 'string') {
 
281
                return match;
 
282
            } else {
 
283
                return match.length === 1 ? match[0] : match;
 
284
            }
 
285
        }
 
286
    }
 
287
 
 
288
    // Same as $(), but don't change the state of the parser,
 
289
    // just return the match.
 
290
    function peek(tok) {
 
291
        if (typeof(tok) === 'string') {
 
292
            return input.charAt(i) === tok;
 
293
        } else {
 
294
            if (tok.test(chunks[j])) {
 
295
                return true;
 
296
            } else {
 
297
                return false;
 
298
            }
 
299
        }
 
300
    }
 
301
 
 
302
    this.env = env = env || {};
 
303
 
 
304
    // The optimization level dictates the thoroughness of the parser,
 
305
    // the lower the number, the less nodes it will create in the tree.
 
306
    // This could matter for debugging, or if you want to access
 
307
    // the individual nodes in the tree.
 
308
    this.optimization = ('optimization' in this.env) ? this.env.optimization : 1;
 
309
 
 
310
    this.env.filename = this.env.filename || null;
 
311
 
 
312
    //
 
313
    // The Parser
 
314
    //
 
315
    return parser = {
 
316
 
 
317
        imports: imports,
 
318
        //
 
319
        // Parse an input string into an abstract syntax tree,
 
320
        // call `callback` when done.
 
321
        //
 
322
        parse: function (str, callback) {
 
323
            var root, start, end, zone, line, lines, buff = [], c, error = null;
 
324
 
 
325
            i = j = current = furthest = 0;
 
326
            chunks = [];
 
327
            input = str.replace(/\r\n/g, '\n');
 
328
 
 
329
            // Split the input into chunks.
 
330
            chunks = (function (chunks) {
 
331
                var j = 0,
 
332
                    skip = /[^"'`\{\}\/\(\)]+/g,
 
333
                    comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,
 
334
                    level = 0,
 
335
                    match,
 
336
                    chunk = chunks[0],
 
337
                    inParam,
 
338
                    inString;
 
339
 
 
340
                for (var i = 0, c, cc; i < input.length; i++) {
 
341
                    skip.lastIndex = i;
 
342
                    if (match = skip.exec(input)) {
 
343
                        if (match.index === i) {
 
344
                            i += match[0].length;
 
345
                            chunk.push(match[0]);
 
346
                        }
 
347
                    }
 
348
                    c = input.charAt(i);
 
349
                    comment.lastIndex = i;
 
350
 
 
351
                    if (!inString && !inParam && c === '/') {
 
352
                        cc = input.charAt(i + 1);
 
353
                        if (cc === '/' || cc === '*') {
 
354
                            if (match = comment.exec(input)) {
 
355
                                if (match.index === i) {
 
356
                                    i += match[0].length;
 
357
                                    chunk.push(match[0]);
 
358
                                    c = input.charAt(i);
 
359
                                }
 
360
                            }
 
361
                        }
 
362
                    }
 
363
 
 
364
                    if        (c === '{' && !inString && !inParam) { level ++;
 
365
                        chunk.push(c);
 
366
                    } else if (c === '}' && !inString && !inParam) { level --;
 
367
                        chunk.push(c);
 
368
                        chunks[++j] = chunk = [];
 
369
                    } else if (c === '(' && !inString && !inParam) {
 
370
                        chunk.push(c);
 
371
                        inParam = true;
 
372
                    } else if (c === ')' && !inString && inParam) {
 
373
                        chunk.push(c);
 
374
                        inParam = false;
 
375
                    } else {
 
376
                        if (c === '"' || c === "'" || c === '`') {
 
377
                            if (! inString) {
 
378
                                inString = c;
 
379
                            } else {
 
380
                                inString = inString === c ? false : inString;
 
381
                            }
 
382
                        }
 
383
                        chunk.push(c);
 
384
                    }
 
385
                }
 
386
                if (level > 0) {
 
387
                    throw {
 
388
                        type: 'Syntax',
 
389
                        message: "Missing closing `}`",
 
390
                        filename: env.filename
 
391
                    };
 
392
                }
 
393
 
 
394
                return chunks.map(function (c) { return c.join('') });;
 
395
            })([[]]);
 
396
 
 
397
            // Start with the primary rule.
 
398
            // The whole syntax tree is held under a Ruleset node,
 
399
            // with the `root` property set to true, so no `{}` are
 
400
            // output. The callback is called when the input is parsed.
 
401
            root = new(tree.Ruleset)([], $(this.parsers.primary));
 
402
            root.root = true;
 
403
 
 
404
            root.toCSS = (function (evaluate) {
 
405
                var line, lines, column;
 
406
 
 
407
                return function (options, variables) {
 
408
                    var frames = [];
 
409
 
 
410
                    options = options || {};
 
411
                    //
 
412
                    // Allows setting variables with a hash, so:
 
413
                    //
 
414
                    //   `{ color: new(tree.Color)('#f01') }` will become:
 
415
                    //
 
416
                    //   new(tree.Rule)('@color',
 
417
                    //     new(tree.Value)([
 
418
                    //       new(tree.Expression)([
 
419
                    //         new(tree.Color)('#f01')
 
420
                    //       ])
 
421
                    //     ])
 
422
                    //   )
 
423
                    //
 
424
                    if (typeof(variables) === 'object' && !Array.isArray(variables)) {
 
425
                        variables = Object.keys(variables).map(function (k) {
 
426
                            var value = variables[k];
 
427
 
 
428
                            if (! (value instanceof tree.Value)) {
 
429
                                if (! (value instanceof tree.Expression)) {
 
430
                                    value = new(tree.Expression)([value]);
 
431
                                }
 
432
                                value = new(tree.Value)([value]);
 
433
                            }
 
434
                            return new(tree.Rule)('@' + k, value, false, 0);
 
435
                        });
 
436
                        frames = [new(tree.Ruleset)(null, variables)];
 
437
                    }
 
438
 
 
439
                    try {
 
440
                        var css = evaluate.call(this, { frames: frames })
 
441
                                          .toCSS([], { compress: options.compress || false });
 
442
                    } catch (e) {
 
443
                        lines = input.split('\n');
 
444
                        line = getLine(e.index);
 
445
 
 
446
                        for (var n = e.index, column = -1;
 
447
                                 n >= 0 && input.charAt(n) !== '\n';
 
448
                                 n--) { column++ }
 
449
 
 
450
                        throw {
 
451
                            type: e.type,
 
452
                            message: e.message,
 
453
                            filename: env.filename,
 
454
                            index: e.index,
 
455
                            line: typeof(line) === 'number' ? line + 1 : null,
 
456
                            callLine: e.call && (getLine(e.call) + 1),
 
457
                            callExtract: lines[getLine(e.call)],
 
458
                            stack: e.stack,
 
459
                            column: column,
 
460
                            extract: [
 
461
                                lines[line - 1],
 
462
                                lines[line],
 
463
                                lines[line + 1]
 
464
                            ]
 
465
                        };
 
466
                    }
 
467
                    if (options.compress) {
 
468
                        return css.replace(/(\s)+/g, "$1");
 
469
                    } else {
 
470
                        return css;
 
471
                    }
 
472
 
 
473
                    function getLine(index) {
 
474
                        return index ? (input.slice(0, index).match(/\n/g) || "").length : null;
 
475
                    }
 
476
                };
 
477
            })(root.eval);
 
478
 
 
479
            // If `i` is smaller than the `input.length - 1`,
 
480
            // it means the parser wasn't able to parse the whole
 
481
            // string, so we've got a parsing error.
 
482
            //
 
483
            // We try to extract a \n delimited string,
 
484
            // showing the line where the parse error occured.
 
485
            // We split it up into two parts (the part which parsed,
 
486
            // and the part which didn't), so we can color them differently.
 
487
            if (i < input.length - 1) {
 
488
                i = furthest;
 
489
                lines = input.split('\n');
 
490
                line = (input.slice(0, i).match(/\n/g) || "").length + 1;
 
491
 
 
492
                for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ }
 
493
 
 
494
                error = {
 
495
                    name: "ParseError",
 
496
                    message: "Syntax Error on line " + line,
 
497
                    index: i,
 
498
                    filename: env.filename,
 
499
                    line: line,
 
500
                    column: column,
 
501
                    extract: [
 
502
                        lines[line - 2],
 
503
                        lines[line - 1],
 
504
                        lines[line]
 
505
                    ]
 
506
                };
 
507
            }
 
508
 
 
509
            if (this.imports.queue.length > 0) {
 
510
                finish = function () { callback(error, root) };
 
511
            } else {
 
512
                callback(error, root);
 
513
            }
 
514
        },
 
515
 
 
516
        //
 
517
        // Here in, the parsing rules/functions
 
518
        //
 
519
        // The basic structure of the syntax tree generated is as follows:
 
520
        //
 
521
        //   Ruleset ->  Rule -> Value -> Expression -> Entity
 
522
        //
 
523
        // Here's some LESS code:
 
524
        //
 
525
        //    .class {
 
526
        //      color: #fff;
 
527
        //      border: 1px solid #000;
 
528
        //      width: @w + 4px;
 
529
        //      > .child {...}
 
530
        //    }
 
531
        //
 
532
        // And here's what the parse tree might look like:
 
533
        //
 
534
        //     Ruleset (Selector '.class', [
 
535
        //         Rule ("color",  Value ([Expression [Color #fff]]))
 
536
        //         Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
 
537
        //         Rule ("width",  Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]]))
 
538
        //         Ruleset (Selector [Element '>', '.child'], [...])
 
539
        //     ])
 
540
        //
 
541
        //  In general, most rules will try to parse a token with the `$()` function, and if the return
 
542
        //  value is truly, will return a new node, of the relevant type. Sometimes, we need to check
 
543
        //  first, before parsing, that's when we use `peek()`.
 
544
        //
 
545
        parsers: {
 
546
            //
 
547
            // The `primary` rule is the *entry* and *exit* point of the parser.
 
548
            // The rules here can appear at any level of the parse tree.
 
549
            //
 
550
            // The recursive nature of the grammar is an interplay between the `block`
 
551
            // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
 
552
            // as represented by this simplified grammar:
 
553
            //
 
554
            //     primary  →  (ruleset | rule)+
 
555
            //     ruleset  →  selector+ block
 
556
            //     block    →  '{' primary '}'
 
557
            //
 
558
            // Only at one point is the primary rule not called from the
 
559
            // block rule: at the root level.
 
560
            //
 
561
            primary: function () {
 
562
                var node, root = [];
 
563
 
 
564
                while ((node = $(this.mixin.definition) || $(this.rule)    ||  $(this.ruleset) ||
 
565
                               $(this.mixin.call)       || $(this.comment) ||  $(this.directive))
 
566
                               || $(/^[\s\n]+/)) {
 
567
                    node && root.push(node);
 
568
                }
 
569
                return root;
 
570
            },
 
571
 
 
572
            // We create a Comment node for CSS comments `/* */`,
 
573
            // but keep the LeSS comments `//` silent, by just skipping
 
574
            // over them.
 
575
            comment: function () {
 
576
                var comment;
 
577
 
 
578
                if (input.charAt(i) !== '/') return;
 
579
 
 
580
                if (input.charAt(i + 1) === '/') {
 
581
                    return new(tree.Comment)($(/^\/\/.*/), true);
 
582
                } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) {
 
583
                    return new(tree.Comment)(comment);
 
584
                }
 
585
            },
 
586
 
 
587
            //
 
588
            // Entities are tokens which can be found inside an Expression
 
589
            //
 
590
            entities: {
 
591
                //
 
592
                // A string, which supports escaping " and '
 
593
                //
 
594
                //     "milky way" 'he\'s the one!'
 
595
                //
 
596
                quoted: function () {
 
597
                    var str, j = i, e;
 
598
 
 
599
                    if (input.charAt(j) === '~') { j++, e = true } // Escaped strings
 
600
                    if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return;
 
601
 
 
602
                    e && $('~');
 
603
 
 
604
                    if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) {
 
605
                        return new(tree.Quoted)(str[0], str[1] || str[2], e);
 
606
                    }
 
607
                },
 
608
 
 
609
                //
 
610
                // A catch-all word, such as:
 
611
                //
 
612
                //     black border-collapse
 
613
                //
 
614
                keyword: function () {
 
615
                    var k;
 
616
                    if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) { return new(tree.Keyword)(k) }
 
617
                },
 
618
 
 
619
                //
 
620
                // A function call
 
621
                //
 
622
                //     rgb(255, 0, 255)
 
623
                //
 
624
                // We also try to catch IE's `alpha()`, but let the `alpha` parser
 
625
                // deal with the details.
 
626
                //
 
627
                // The arguments are parsed with the `entities.arguments` parser.
 
628
                //
 
629
                call: function () {
 
630
                    var name, args, index = i;
 
631
 
 
632
                    if (! (name = /^([\w-]+|%)\(/.exec(chunks[j]))) return;
 
633
 
 
634
                    name = name[1].toLowerCase();
 
635
 
 
636
                    if (name === 'url') { return null }
 
637
                    else                { i += name.length }
 
638
 
 
639
                    if (name === 'alpha') { return $(this.alpha) }
 
640
 
 
641
                    $('('); // Parse the '(' and consume whitespace.
 
642
 
 
643
                    args = $(this.entities.arguments);
 
644
 
 
645
                    if (! $(')')) return;
 
646
 
 
647
                    if (name) { return new(tree.Call)(name, args, index) }
 
648
                },
 
649
                arguments: function () {
 
650
                    var args = [], arg;
 
651
 
 
652
                    while (arg = $(this.expression)) {
 
653
                        args.push(arg);
 
654
                        if (! $(',')) { break }
 
655
                    }
 
656
                    return args;
 
657
                },
 
658
                literal: function () {
 
659
                    return $(this.entities.dimension) ||
 
660
                           $(this.entities.color) ||
 
661
                           $(this.entities.quoted);
 
662
                },
 
663
 
 
664
                //
 
665
                // Parse url() tokens
 
666
                //
 
667
                // We use a specific rule for urls, because they don't really behave like
 
668
                // standard function calls. The difference is that the argument doesn't have
 
669
                // to be enclosed within a string, so it can't be parsed as an Expression.
 
670
                //
 
671
                url: function () {
 
672
                    var value;
 
673
 
 
674
                    if (input.charAt(i) !== 'u' || !$(/^url\(/)) return;
 
675
                    value = $(this.entities.quoted)  || $(this.entities.variable) ||
 
676
                            $(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || "";
 
677
                    if (! $(')')) throw new(Error)("missing closing ) for url()");
 
678
 
 
679
                    return new(tree.URL)((value.value || value.data || value instanceof tree.Variable)
 
680
                                        ? value : new(tree.Anonymous)(value), imports.paths);
 
681
                },
 
682
 
 
683
                dataURI: function () {
 
684
                    var obj;
 
685
 
 
686
                    if ($(/^data:/)) {
 
687
                        obj         = {};
 
688
                        obj.mime    = $(/^[^\/]+\/[^,;)]+/)     || '';
 
689
                        obj.charset = $(/^;\s*charset=[^,;)]+/) || '';
 
690
                        obj.base64  = $(/^;\s*base64/)          || '';
 
691
                        obj.data    = $(/^,\s*[^)]+/);
 
692
 
 
693
                        if (obj.data) { return obj }
 
694
                    }
 
695
                },
 
696
 
 
697
                //
 
698
                // A Variable entity, such as `@fink`, in
 
699
                //
 
700
                //     width: @fink + 2px
 
701
                //
 
702
                // We use a different parser for variable definitions,
 
703
                // see `parsers.variable`.
 
704
                //
 
705
                variable: function () {
 
706
                    var name, index = i;
 
707
 
 
708
                    if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) {
 
709
                        return new(tree.Variable)(name, index);
 
710
                    }
 
711
                },
 
712
 
 
713
                //
 
714
                // A Hexadecimal color
 
715
                //
 
716
                //     #4F3C2F
 
717
                //
 
718
                // `rgb` and `hsl` colors are parsed through the `entities.call` parser.
 
719
                //
 
720
                color: function () {
 
721
                    var rgb;
 
722
 
 
723
                    if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) {
 
724
                        return new(tree.Color)(rgb[1]);
 
725
                    }
 
726
                },
 
727
 
 
728
                //
 
729
                // A Dimension, that is, a number and a unit
 
730
                //
 
731
                //     0.5em 95%
 
732
                //
 
733
                dimension: function () {
 
734
                    var value, c = input.charCodeAt(i);
 
735
                    if ((c > 57 || c < 45) || c === 47) return;
 
736
 
 
737
                    if (value = $(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/)) {
 
738
                        return new(tree.Dimension)(value[1], value[2]);
 
739
                    }
 
740
                },
 
741
 
 
742
                //
 
743
                // JavaScript code to be evaluated
 
744
                //
 
745
                //     `window.location.href`
 
746
                //
 
747
                javascript: function () {
 
748
                    var str, j = i, e;
 
749
 
 
750
                    if (input.charAt(j) === '~') { j++, e = true } // Escaped strings
 
751
                    if (input.charAt(j) !== '`') { return }
 
752
 
 
753
                    e && $('~');
 
754
 
 
755
                    if (str = $(/^`([^`]*)`/)) {
 
756
                        return new(tree.JavaScript)(str[1], i, e);
 
757
                    }
 
758
                }
 
759
            },
 
760
 
 
761
            //
 
762
            // The variable part of a variable definition. Used in the `rule` parser
 
763
            //
 
764
            //     @fink:
 
765
            //
 
766
            variable: function () {
 
767
                var name;
 
768
 
 
769
                if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] }
 
770
            },
 
771
 
 
772
            //
 
773
            // A font size/line-height shorthand
 
774
            //
 
775
            //     small/12px
 
776
            //
 
777
            // We need to peek first, or we'll match on keywords and dimensions
 
778
            //
 
779
            shorthand: function () {
 
780
                var a, b;
 
781
 
 
782
                if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return;
 
783
 
 
784
                if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) {
 
785
                    return new(tree.Shorthand)(a, b);
 
786
                }
 
787
            },
 
788
 
 
789
            //
 
790
            // Mixins
 
791
            //
 
792
            mixin: {
 
793
                //
 
794
                // A Mixin call, with an optional argument list
 
795
                //
 
796
                //     #mixins > .square(#fff);
 
797
                //     .rounded(4px, black);
 
798
                //     .button;
 
799
                //
 
800
                // The `while` loop is there because mixins can be
 
801
                // namespaced, but we only support the child and descendant
 
802
                // selector for now.
 
803
                //
 
804
                call: function () {
 
805
                    var elements = [], e, c, args, index = i, s = input.charAt(i);
 
806
 
 
807
                    if (s !== '.' && s !== '#') { return }
 
808
 
 
809
                    while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) {
 
810
                        elements.push(new(tree.Element)(c, e, i));
 
811
                        c = $('>');
 
812
                    }
 
813
                    $('(') && (args = $(this.entities.arguments)) && $(')');
 
814
 
 
815
                    if (elements.length > 0 && ($(';') || peek('}'))) {
 
816
                        return new(tree.mixin.Call)(elements, args, index);
 
817
                    }
 
818
                },
 
819
 
 
820
                //
 
821
                // A Mixin definition, with a list of parameters
 
822
                //
 
823
                //     .rounded (@radius: 2px, @color) {
 
824
                //        ...
 
825
                //     }
 
826
                //
 
827
                // Until we have a finer grained state-machine, we have to
 
828
                // do a look-ahead, to make sure we don't have a mixin call.
 
829
                // See the `rule` function for more information.
 
830
                //
 
831
                // We start by matching `.rounded (`, and then proceed on to
 
832
                // the argument list, which has optional default values.
 
833
                // We store the parameters in `params`, with a `value` key,
 
834
                // if there is a value, such as in the case of `@radius`.
 
835
                //
 
836
                // Once we've got our params list, and a closing `)`, we parse
 
837
                // the `{...}` block.
 
838
                //
 
839
                definition: function () {
 
840
                    var name, params = [], match, ruleset, param, value;
 
841
 
 
842
                    if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') ||
 
843
                        peek(/^[^{]*(;|})/)) return;
 
844
 
 
845
                    if (match = $(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)) {
 
846
                        name = match[1];
 
847
 
 
848
                        while (param = $(this.entities.variable) || $(this.entities.literal)
 
849
                                                                 || $(this.entities.keyword)) {
 
850
                            // Variable
 
851
                            if (param instanceof tree.Variable) {
 
852
                                if ($(':')) {
 
853
                                    if (value = $(this.expression)) {
 
854
                                        params.push({ name: param.name, value: value });
 
855
                                    } else {
 
856
                                        throw new(Error)("Expected value");
 
857
                                    }
 
858
                                } else {
 
859
                                    params.push({ name: param.name });
 
860
                                }
 
861
                            } else {
 
862
                                params.push({ value: param });
 
863
                            }
 
864
                            if (! $(',')) { break }
 
865
                        }
 
866
                        if (! $(')')) throw new(Error)("Expected )");
 
867
 
 
868
                        ruleset = $(this.block);
 
869
 
 
870
                        if (ruleset) {
 
871
                            return new(tree.mixin.Definition)(name, params, ruleset);
 
872
                        }
 
873
                    }
 
874
                }
 
875
            },
 
876
 
 
877
            //
 
878
            // Entities are the smallest recognized token,
 
879
            // and can be found inside a rule's value.
 
880
            //
 
881
            entity: function () {
 
882
                return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) ||
 
883
                       $(this.entities.call)    || $(this.entities.keyword)  || $(this.entities.javascript) ||
 
884
                       $(this.comment);
 
885
            },
 
886
 
 
887
            //
 
888
            // A Rule terminator. Note that we use `peek()` to check for '}',
 
889
            // because the `block` rule will be expecting it, but we still need to make sure
 
890
            // it's there, if ';' was ommitted.
 
891
            //
 
892
            end: function () {
 
893
                return $(';') || peek('}');
 
894
            },
 
895
 
 
896
            //
 
897
            // IE's alpha function
 
898
            //
 
899
            //     alpha(opacity=88)
 
900
            //
 
901
            alpha: function () {
 
902
                var value;
 
903
 
 
904
                if (! $(/^\(opacity=/i)) return;
 
905
                if (value = $(/^\d+/) || $(this.entities.variable)) {
 
906
                    if (! $(')')) throw new(Error)("missing closing ) for alpha()");
 
907
                    return new(tree.Alpha)(value);
 
908
                }
 
909
            },
 
910
 
 
911
            //
 
912
            // A Selector Element
 
913
            //
 
914
            //     div
 
915
            //     + h1
 
916
            //     #socks
 
917
            //     input[type="text"]
 
918
            //
 
919
            // Elements are the building blocks for Selectors,
 
920
            // they are made out of a `Combinator` (see combinator rule),
 
921
            // and an element name, such as a tag a class, or `*`.
 
922
            //
 
923
            element: function () {
 
924
                var e, t, c;
 
925
 
 
926
                c = $(this.combinator);
 
927
                e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) ||
 
928
                    $('*') || $(this.attribute) || $(/^\([^)@]+\)/);
 
929
 
 
930
                if (e) { return new(tree.Element)(c, e, i) }
 
931
 
 
932
                if (c.value && c.value.charAt(0) === '&') {
 
933
                    return new(tree.Element)(c, null, i);
 
934
                }
 
935
            },
 
936
 
 
937
            //
 
938
            // Combinators combine elements together, in a Selector.
 
939
            //
 
940
            // Because our parser isn't white-space sensitive, special care
 
941
            // has to be taken, when parsing the descendant combinator, ` `,
 
942
            // as it's an empty space. We have to check the previous character
 
943
            // in the input, to see if it's a ` ` character. More info on how
 
944
            // we deal with this in *combinator.js*.
 
945
            //
 
946
            combinator: function () {
 
947
                var match, c = input.charAt(i);
 
948
 
 
949
                if (c === '>' || c === '+' || c === '~') {
 
950
                    i++;
 
951
                    while (input.charAt(i) === ' ') { i++ }
 
952
                    return new(tree.Combinator)(c);
 
953
                } else if (c === '&') {
 
954
                    match = '&';
 
955
                    i++;
 
956
                    if(input.charAt(i) === ' ') {
 
957
                        match = '& ';
 
958
                    }
 
959
                    while (input.charAt(i) === ' ') { i++ }
 
960
                    return new(tree.Combinator)(match);
 
961
                } else if (c === ':' && input.charAt(i + 1) === ':') {
 
962
                    i += 2;
 
963
                    while (input.charAt(i) === ' ') { i++ }
 
964
                    return new(tree.Combinator)('::');
 
965
                } else if (input.charAt(i - 1) === ' ') {
 
966
                    return new(tree.Combinator)(" ");
 
967
                } else {
 
968
                    return new(tree.Combinator)(null);
 
969
                }
 
970
            },
 
971
 
 
972
            //
 
973
            // A CSS Selector
 
974
            //
 
975
            //     .class > div + h1
 
976
            //     li a:hover
 
977
            //
 
978
            // Selectors are made out of one or more Elements, see above.
 
979
            //
 
980
            selector: function () {
 
981
                var sel, e, elements = [], c, match;
 
982
 
 
983
                while (e = $(this.element)) {
 
984
                    c = input.charAt(i);
 
985
                    elements.push(e)
 
986
                    if (c === '{' || c === '}' || c === ';' || c === ',') { break }
 
987
                }
 
988
 
 
989
                if (elements.length > 0) { return new(tree.Selector)(elements) }
 
990
            },
 
991
            tag: function () {
 
992
                return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*');
 
993
            },
 
994
            attribute: function () {
 
995
                var attr = '', key, val, op;
 
996
 
 
997
                if (! $('[')) return;
 
998
 
 
999
                if (key = $(/^[a-zA-Z-]+/) || $(this.entities.quoted)) {
 
1000
                    if ((op = $(/^[|~*$^]?=/)) &&
 
1001
                        (val = $(this.entities.quoted) || $(/^[\w-]+/))) {
 
1002
                        attr = [key, op, val.toCSS ? val.toCSS() : val].join('');
 
1003
                    } else { attr = key }
 
1004
                }
 
1005
 
 
1006
                if (! $(']')) return;
 
1007
 
 
1008
                if (attr) { return "[" + attr + "]" }
 
1009
            },
 
1010
 
 
1011
            //
 
1012
            // The `block` rule is used by `ruleset` and `mixin.definition`.
 
1013
            // It's a wrapper around the `primary` rule, with added `{}`.
 
1014
            //
 
1015
            block: function () {
 
1016
                var content;
 
1017
 
 
1018
                if ($('{') && (content = $(this.primary)) && $('}')) {
 
1019
                    return content;
 
1020
                }
 
1021
            },
 
1022
 
 
1023
            //
 
1024
            // div, .class, body > p {...}
 
1025
            //
 
1026
            ruleset: function () {
 
1027
                var selectors = [], s, rules, match;
 
1028
                save();
 
1029
 
 
1030
                while (s = $(this.selector)) {
 
1031
                    selectors.push(s);
 
1032
                    $(this.comment);
 
1033
                    if (! $(',')) { break }
 
1034
                    $(this.comment);
 
1035
                }
 
1036
 
 
1037
                if (selectors.length > 0 && (rules = $(this.block))) {
 
1038
                    return new(tree.Ruleset)(selectors, rules);
 
1039
                } else {
 
1040
                    // Backtrack
 
1041
                    furthest = i;
 
1042
                    restore();
 
1043
                }
 
1044
            },
 
1045
            rule: function () {
 
1046
                var name, value, c = input.charAt(i), important, match;
 
1047
                save();
 
1048
 
 
1049
                if (c === '.' || c === '#' || c === '&') { return }
 
1050
 
 
1051
                if (name = $(this.variable) || $(this.property)) {
 
1052
                    if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) {
 
1053
                        i += match[0].length - 1;
 
1054
                        value = new(tree.Anonymous)(match[1]);
 
1055
                    } else if (name === "font") {
 
1056
                        value = $(this.font);
 
1057
                    } else {
 
1058
                        value = $(this.value);
 
1059
                    }
 
1060
                    important = $(this.important);
 
1061
 
 
1062
                    if (value && $(this.end)) {
 
1063
                        return new(tree.Rule)(name, value, important, memo);
 
1064
                    } else {
 
1065
                        furthest = i;
 
1066
                        restore();
 
1067
                    }
 
1068
                }
 
1069
            },
 
1070
 
 
1071
            //
 
1072
            // An @import directive
 
1073
            //
 
1074
            //     @import "lib";
 
1075
            //
 
1076
            // Depending on our environemnt, importing is done differently:
 
1077
            // In the browser, it's an XHR request, in Node, it would be a
 
1078
            // file-system operation. The function used for importing is
 
1079
            // stored in `import`, which we pass to the Import constructor.
 
1080
            //
 
1081
            "import": function () {
 
1082
                var path;
 
1083
                if ($(/^@import\s+/) &&
 
1084
                    (path = $(this.entities.quoted) || $(this.entities.url)) &&
 
1085
                    $(';')) {
 
1086
                    return new(tree.Import)(path, imports);
 
1087
                }
 
1088
            },
 
1089
 
 
1090
            //
 
1091
            // A CSS Directive
 
1092
            //
 
1093
            //     @charset "utf-8";
 
1094
            //
 
1095
            directive: function () {
 
1096
                var name, value, rules, types;
 
1097
 
 
1098
                if (input.charAt(i) !== '@') return;
 
1099
 
 
1100
                if (value = $(this['import'])) {
 
1101
                    return value;
 
1102
                } else if (name = $(/^@media|@page/) || $(/^@(?:-webkit-|-moz-)?keyframes/)) {
 
1103
                    types = ($(/^[^{]+/) || '').trim();
 
1104
                    if (rules = $(this.block)) {
 
1105
                        return new(tree.Directive)(name + " " + types, rules);
 
1106
                    }
 
1107
                } else if (name = $(/^@[-a-z]+/)) {
 
1108
                    if (name === '@font-face') {
 
1109
                        if (rules = $(this.block)) {
 
1110
                            return new(tree.Directive)(name, rules);
 
1111
                        }
 
1112
                    } else if ((value = $(this.entity)) && $(';')) {
 
1113
                        return new(tree.Directive)(name, value);
 
1114
                    }
 
1115
                }
 
1116
            },
 
1117
            font: function () {
 
1118
                var value = [], expression = [], weight, shorthand, font, e;
 
1119
 
 
1120
                while (e = $(this.shorthand) || $(this.entity)) {
 
1121
                    expression.push(e);
 
1122
                }
 
1123
                value.push(new(tree.Expression)(expression));
 
1124
 
 
1125
                if ($(',')) {
 
1126
                    while (e = $(this.expression)) {
 
1127
                        value.push(e);
 
1128
                        if (! $(',')) { break }
 
1129
                    }
 
1130
                }
 
1131
                return new(tree.Value)(value);
 
1132
            },
 
1133
 
 
1134
            //
 
1135
            // A Value is a comma-delimited list of Expressions
 
1136
            //
 
1137
            //     font-family: Baskerville, Georgia, serif;
 
1138
            //
 
1139
            // In a Rule, a Value represents everything after the `:`,
 
1140
            // and before the `;`.
 
1141
            //
 
1142
            value: function () {
 
1143
                var e, expressions = [], important;
 
1144
 
 
1145
                while (e = $(this.expression)) {
 
1146
                    expressions.push(e);
 
1147
                    if (! $(',')) { break }
 
1148
                }
 
1149
 
 
1150
                if (expressions.length > 0) {
 
1151
                    return new(tree.Value)(expressions);
 
1152
                }
 
1153
            },
 
1154
            important: function () {
 
1155
                if (input.charAt(i) === '!') {
 
1156
                    return $(/^! *important/);
 
1157
                }
 
1158
            },
 
1159
            sub: function () {
 
1160
                var e;
 
1161
 
 
1162
                if ($('(') && (e = $(this.expression)) && $(')')) {
 
1163
                    return e;
 
1164
                }
 
1165
            },
 
1166
            multiplication: function () {
 
1167
                var m, a, op, operation;
 
1168
                if (m = $(this.operand)) {
 
1169
                    while ((op = ($('/') || $('*'))) && (a = $(this.operand))) {
 
1170
                        operation = new(tree.Operation)(op, [operation || m, a]);
 
1171
                    }
 
1172
                    return operation || m;
 
1173
                }
 
1174
            },
 
1175
            addition: function () {
 
1176
                var m, a, op, operation;
 
1177
                if (m = $(this.multiplication)) {
 
1178
                    while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) &&
 
1179
                           (a = $(this.multiplication))) {
 
1180
                        operation = new(tree.Operation)(op, [operation || m, a]);
 
1181
                    }
 
1182
                    return operation || m;
 
1183
                }
 
1184
            },
 
1185
 
 
1186
            //
 
1187
            // An operand is anything that can be part of an operation,
 
1188
            // such as a Color, or a Variable
 
1189
            //
 
1190
            operand: function () {
 
1191
                var negate, p = input.charAt(i + 1);
 
1192
 
 
1193
                if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') }
 
1194
                var o = $(this.sub) || $(this.entities.dimension) ||
 
1195
                        $(this.entities.color) || $(this.entities.variable) ||
 
1196
                        $(this.entities.call);
 
1197
                return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o])
 
1198
                              : o;
 
1199
            },
 
1200
 
 
1201
            //
 
1202
            // Expressions either represent mathematical operations,
 
1203
            // or white-space delimited Entities.
 
1204
            //
 
1205
            //     1px solid black
 
1206
            //     @var * 2
 
1207
            //
 
1208
            expression: function () {
 
1209
                var e, delim, entities = [], d;
 
1210
 
 
1211
                while (e = $(this.addition) || $(this.entity)) {
 
1212
                    entities.push(e);
 
1213
                }
 
1214
                if (entities.length > 0) {
 
1215
                    return new(tree.Expression)(entities);
 
1216
                }
 
1217
            },
 
1218
            property: function () {
 
1219
                var name;
 
1220
 
 
1221
                if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) {
 
1222
                    return name[1];
 
1223
                }
 
1224
            }
 
1225
        }
 
1226
    };
 
1227
};
 
1228
 
 
1229
if (less.mode === 'browser' || less.mode === 'rhino') {
 
1230
    //
 
1231
    // Used by `@import` directives
 
1232
    //
 
1233
    less.Parser.importer = function (path, paths, callback, env) {
 
1234
        if (path.charAt(0) !== '/' && paths.length > 0) {
 
1235
            path = paths[0] + path;
 
1236
        }
 
1237
        // We pass `true` as 3rd argument, to force the reload of the import.
 
1238
        // This is so we can get the syntax tree as opposed to just the CSS output,
 
1239
        // as we need this to evaluate the current stylesheet.
 
1240
        loadStyleSheet({ href: path, title: path, type: env.mime }, callback, true);
 
1241
    };
 
1242
}
 
1243
 
 
1244
(function (tree) {
 
1245
 
 
1246
tree.functions = {
 
1247
    rgb: function (r, g, b) {
 
1248
        return this.rgba(r, g, b, 1.0);
 
1249
    },
 
1250
    rgba: function (r, g, b, a) {
 
1251
        var rgb = [r, g, b].map(function (c) { return number(c) }),
 
1252
            a = number(a);
 
1253
        return new(tree.Color)(rgb, a);
 
1254
    },
 
1255
    hsl: function (h, s, l) {
 
1256
        return this.hsla(h, s, l, 1.0);
 
1257
    },
 
1258
    hsla: function (h, s, l, a) {
 
1259
        h = (number(h) % 360) / 360;
 
1260
        s = number(s); l = number(l); a = number(a);
 
1261
 
 
1262
        var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
 
1263
        var m1 = l * 2 - m2;
 
1264
 
 
1265
        return this.rgba(hue(h + 1/3) * 255,
 
1266
                         hue(h)       * 255,
 
1267
                         hue(h - 1/3) * 255,
 
1268
                         a);
 
1269
 
 
1270
        function hue(h) {
 
1271
            h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
 
1272
            if      (h * 6 < 1) return m1 + (m2 - m1) * h * 6;
 
1273
            else if (h * 2 < 1) return m2;
 
1274
            else if (h * 3 < 2) return m1 + (m2 - m1) * (2/3 - h) * 6;
 
1275
            else                return m1;
 
1276
        }
 
1277
    },
 
1278
    hue: function (color) {
 
1279
        return new(tree.Dimension)(Math.round(color.toHSL().h));
 
1280
    },
 
1281
    saturation: function (color) {
 
1282
        return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%');
 
1283
    },
 
1284
    lightness: function (color) {
 
1285
        return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%');
 
1286
    },
 
1287
    alpha: function (color) {
 
1288
        return new(tree.Dimension)(color.toHSL().a);
 
1289
    },
 
1290
    saturate: function (color, amount) {
 
1291
        var hsl = color.toHSL();
 
1292
 
 
1293
        hsl.s += amount.value / 100;
 
1294
        hsl.s = clamp(hsl.s);
 
1295
        return hsla(hsl);
 
1296
    },
 
1297
    desaturate: function (color, amount) {
 
1298
        var hsl = color.toHSL();
 
1299
 
 
1300
        hsl.s -= amount.value / 100;
 
1301
        hsl.s = clamp(hsl.s);
 
1302
        return hsla(hsl);
 
1303
    },
 
1304
    lighten: function (color, amount) {
 
1305
        var hsl = color.toHSL();
 
1306
 
 
1307
        hsl.l += amount.value / 100;
 
1308
        hsl.l = clamp(hsl.l);
 
1309
        return hsla(hsl);
 
1310
    },
 
1311
    darken: function (color, amount) {
 
1312
        var hsl = color.toHSL();
 
1313
 
 
1314
        hsl.l -= amount.value / 100;
 
1315
        hsl.l = clamp(hsl.l);
 
1316
        return hsla(hsl);
 
1317
    },
 
1318
    fadein: function (color, amount) {
 
1319
        var hsl = color.toHSL();
 
1320
 
 
1321
        hsl.a += amount.value / 100;
 
1322
        hsl.a = clamp(hsl.a);
 
1323
        return hsla(hsl);
 
1324
    },
 
1325
    fadeout: function (color, amount) {
 
1326
        var hsl = color.toHSL();
 
1327
 
 
1328
        hsl.a -= amount.value / 100;
 
1329
        hsl.a = clamp(hsl.a);
 
1330
        return hsla(hsl);
 
1331
    },
 
1332
    fade: function (color, amount) {
 
1333
        var hsl = color.toHSL();
 
1334
 
 
1335
        hsl.a = amount.value / 100;
 
1336
        hsl.a = clamp(hsl.a);
 
1337
        return hsla(hsl);
 
1338
    },
 
1339
    spin: function (color, amount) {
 
1340
        var hsl = color.toHSL();
 
1341
        var hue = (hsl.h + amount.value) % 360;
 
1342
 
 
1343
        hsl.h = hue < 0 ? 360 + hue : hue;
 
1344
 
 
1345
        return hsla(hsl);
 
1346
    },
 
1347
    //
 
1348
    // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
 
1349
    // http://sass-lang.com
 
1350
    //
 
1351
    mix: function (color1, color2, weight) {
 
1352
        var p = weight.value / 100.0;
 
1353
        var w = p * 2 - 1;
 
1354
        var a = color1.toHSL().a - color2.toHSL().a;
 
1355
 
 
1356
        var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
 
1357
        var w2 = 1 - w1;
 
1358
 
 
1359
        var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2,
 
1360
                   color1.rgb[1] * w1 + color2.rgb[1] * w2,
 
1361
                   color1.rgb[2] * w1 + color2.rgb[2] * w2];
 
1362
 
 
1363
        var alpha = color1.alpha * p + color2.alpha * (1 - p);
 
1364
 
 
1365
        return new(tree.Color)(rgb, alpha);
 
1366
    },
 
1367
    greyscale: function (color) {
 
1368
        return this.desaturate(color, new(tree.Dimension)(100));
 
1369
    },
 
1370
    e: function (str) {
 
1371
        return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str);
 
1372
    },
 
1373
    escape: function (str) {
 
1374
        return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29"));
 
1375
    },
 
1376
    '%': function (quoted /* arg, arg, ...*/) {
 
1377
        var args = Array.prototype.slice.call(arguments, 1),
 
1378
            str = quoted.value;
 
1379
 
 
1380
        for (var i = 0; i < args.length; i++) {
 
1381
            str = str.replace(/%[sda]/i, function(token) {
 
1382
                var value = token.match(/s/i) ? args[i].value : args[i].toCSS();
 
1383
                return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value;
 
1384
            });
 
1385
        }
 
1386
        str = str.replace(/%%/g, '%');
 
1387
        return new(tree.Quoted)('"' + str + '"', str);
 
1388
    },
 
1389
    round: function (n) {
 
1390
        if (n instanceof tree.Dimension) {
 
1391
            return new(tree.Dimension)(Math.round(number(n)), n.unit);
 
1392
        } else if (typeof(n) === 'number') {
 
1393
            return Math.round(n);
 
1394
        } else {
 
1395
            throw {
 
1396
                error: "RuntimeError",
 
1397
                message: "math functions take numbers as parameters"
 
1398
            };
 
1399
        }
 
1400
    },
 
1401
    argb: function (color) {
 
1402
        return new(tree.Anonymous)(color.toARGB());
 
1403
 
 
1404
    }
 
1405
};
 
1406
 
 
1407
function hsla(hsla) {
 
1408
    return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a);
 
1409
}
 
1410
 
 
1411
function number(n) {
 
1412
    if (n instanceof tree.Dimension) {
 
1413
        return parseFloat(n.unit == '%' ? n.value / 100 : n.value);
 
1414
    } else if (typeof(n) === 'number') {
 
1415
        return n;
 
1416
    } else {
 
1417
        throw {
 
1418
            error: "RuntimeError",
 
1419
            message: "color functions take numbers as parameters"
 
1420
        };
 
1421
    }
 
1422
}
 
1423
 
 
1424
function clamp(val) {
 
1425
    return Math.min(1, Math.max(0, val));
 
1426
}
 
1427
 
 
1428
})(require('./tree'));
 
1429
(function (tree) {
 
1430
 
 
1431
tree.Alpha = function (val) {
 
1432
    this.value = val;
 
1433
};
 
1434
tree.Alpha.prototype = {
 
1435
    toCSS: function () {
 
1436
        return "alpha(opacity=" +
 
1437
               (this.value.toCSS ? this.value.toCSS() : this.value) + ")";
 
1438
    },
 
1439
    eval: function (env) {
 
1440
        if (this.value.eval) { this.value = this.value.eval(env) }
 
1441
        return this;
 
1442
    }
 
1443
};
 
1444
 
 
1445
})(require('../tree'));
 
1446
(function (tree) {
 
1447
 
 
1448
tree.Anonymous = function (string) {
 
1449
    this.value = string.value || string;
 
1450
};
 
1451
tree.Anonymous.prototype = {
 
1452
    toCSS: function () {
 
1453
        return this.value;
 
1454
    },
 
1455
    eval: function () { return this }
 
1456
};
 
1457
 
 
1458
})(require('../tree'));
 
1459
(function (tree) {
 
1460
 
 
1461
//
 
1462
// A function call node.
 
1463
//
 
1464
tree.Call = function (name, args, index) {
 
1465
    this.name = name;
 
1466
    this.args = args;
 
1467
    this.index = index;
 
1468
};
 
1469
tree.Call.prototype = {
 
1470
    //
 
1471
    // When evaluating a function call,
 
1472
    // we either find the function in `tree.functions` [1],
 
1473
    // in which case we call it, passing the  evaluated arguments,
 
1474
    // or we simply print it out as it appeared originally [2].
 
1475
    //
 
1476
    // The *functions.js* file contains the built-in functions.
 
1477
    //
 
1478
    // The reason why we evaluate the arguments, is in the case where
 
1479
    // we try to pass a variable to a function, like: `saturate(@color)`.
 
1480
    // The function should receive the value, not the variable.
 
1481
    //
 
1482
    eval: function (env) {
 
1483
        var args = this.args.map(function (a) { return a.eval(env) });
 
1484
 
 
1485
        if (this.name in tree.functions) { // 1.
 
1486
            try {
 
1487
                return tree.functions[this.name].apply(tree.functions, args);
 
1488
            } catch (e) {
 
1489
                throw { message: "error evaluating function `" + this.name + "`",
 
1490
                        index: this.index };
 
1491
            }
 
1492
        } else { // 2.
 
1493
            return new(tree.Anonymous)(this.name +
 
1494
                   "(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")");
 
1495
        }
 
1496
    },
 
1497
 
 
1498
    toCSS: function (env) {
 
1499
        return this.eval(env).toCSS();
 
1500
    }
 
1501
};
 
1502
 
 
1503
})(require('../tree'));
 
1504
(function (tree) {
 
1505
//
 
1506
// RGB Colors - #ff0014, #eee
 
1507
//
 
1508
tree.Color = function (rgb, a) {
 
1509
    //
 
1510
    // The end goal here, is to parse the arguments
 
1511
    // into an integer triplet, such as `128, 255, 0`
 
1512
    //
 
1513
    // This facilitates operations and conversions.
 
1514
    //
 
1515
    if (Array.isArray(rgb)) {
 
1516
        this.rgb = rgb;
 
1517
    } else if (rgb.length == 6) {
 
1518
        this.rgb = rgb.match(/.{2}/g).map(function (c) {
 
1519
            return parseInt(c, 16);
 
1520
        });
 
1521
    } else {
 
1522
        this.rgb = rgb.split('').map(function (c) {
 
1523
            return parseInt(c + c, 16);
 
1524
        });
 
1525
    }
 
1526
    this.alpha = typeof(a) === 'number' ? a : 1;
 
1527
};
 
1528
tree.Color.prototype = {
 
1529
    eval: function () { return this },
 
1530
 
 
1531
    //
 
1532
    // If we have some transparency, the only way to represent it
 
1533
    // is via `rgba`. Otherwise, we use the hex representation,
 
1534
    // which has better compatibility with older browsers.
 
1535
    // Values are capped between `0` and `255`, rounded and zero-padded.
 
1536
    //
 
1537
    toCSS: function () {
 
1538
        if (this.alpha < 1.0) {
 
1539
            return "rgba(" + this.rgb.map(function (c) {
 
1540
                return Math.round(c);
 
1541
            }).concat(this.alpha).join(', ') + ")";
 
1542
        } else {
 
1543
            return '#' + this.rgb.map(function (i) {
 
1544
                i = Math.round(i);
 
1545
                i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16);
 
1546
                return i.length === 1 ? '0' + i : i;
 
1547
            }).join('');
 
1548
        }
 
1549
    },
 
1550
 
 
1551
    //
 
1552
    // Operations have to be done per-channel, if not,
 
1553
    // channels will spill onto each other. Once we have
 
1554
    // our result, in the form of an integer triplet,
 
1555
    // we create a new Color node to hold the result.
 
1556
    //
 
1557
    operate: function (op, other) {
 
1558
        var result = [];
 
1559
 
 
1560
        if (! (other instanceof tree.Color)) {
 
1561
            other = other.toColor();
 
1562
        }
 
1563
 
 
1564
        for (var c = 0; c < 3; c++) {
 
1565
            result[c] = tree.operate(op, this.rgb[c], other.rgb[c]);
 
1566
        }
 
1567
        return new(tree.Color)(result, this.alpha + other.alpha);
 
1568
    },
 
1569
 
 
1570
    toHSL: function () {
 
1571
        var r = this.rgb[0] / 255,
 
1572
            g = this.rgb[1] / 255,
 
1573
            b = this.rgb[2] / 255,
 
1574
            a = this.alpha;
 
1575
 
 
1576
        var max = Math.max(r, g, b), min = Math.min(r, g, b);
 
1577
        var h, s, l = (max + min) / 2, d = max - min;
 
1578
 
 
1579
        if (max === min) {
 
1580
            h = s = 0;
 
1581
        } else {
 
1582
            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
 
1583
 
 
1584
            switch (max) {
 
1585
                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
 
1586
                case g: h = (b - r) / d + 2;               break;
 
1587
                case b: h = (r - g) / d + 4;               break;
 
1588
            }
 
1589
            h /= 6;
 
1590
        }
 
1591
        return { h: h * 360, s: s, l: l, a: a };
 
1592
    },
 
1593
    toARGB: function () {
 
1594
        var argb = [Math.round(this.alpha * 255)].concat(this.rgb);
 
1595
        return '#' + argb.map(function (i) {
 
1596
            i = Math.round(i);
 
1597
            i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16);
 
1598
            return i.length === 1 ? '0' + i : i;
 
1599
        }).join('');
 
1600
    }
 
1601
};
 
1602
 
 
1603
 
 
1604
})(require('../tree'));
 
1605
(function (tree) {
 
1606
 
 
1607
tree.Comment = function (value, silent) {
 
1608
    this.value = value;
 
1609
    this.silent = !!silent;
 
1610
};
 
1611
tree.Comment.prototype = {
 
1612
    toCSS: function (env) {
 
1613
        return env.compress ? '' : this.value;
 
1614
    },
 
1615
    eval: function () { return this }
 
1616
};
 
1617
 
 
1618
})(require('../tree'));
 
1619
(function (tree) {
 
1620
 
 
1621
//
 
1622
// A number with a unit
 
1623
//
 
1624
tree.Dimension = function (value, unit) {
 
1625
    this.value = parseFloat(value);
 
1626
    this.unit = unit || null;
 
1627
};
 
1628
 
 
1629
tree.Dimension.prototype = {
 
1630
    eval: function () { return this },
 
1631
    toColor: function () {
 
1632
        return new(tree.Color)([this.value, this.value, this.value]);
 
1633
    },
 
1634
    toCSS: function () {
 
1635
        var css = this.value + this.unit;
 
1636
        return css;
 
1637
    },
 
1638
 
 
1639
    // In an operation between two Dimensions,
 
1640
    // we default to the first Dimension's unit,
 
1641
    // so `1px + 2em` will yield `3px`.
 
1642
    // In the future, we could implement some unit
 
1643
    // conversions such that `100cm + 10mm` would yield
 
1644
    // `101cm`.
 
1645
    operate: function (op, other) {
 
1646
        return new(tree.Dimension)
 
1647
                  (tree.operate(op, this.value, other.value),
 
1648
                  this.unit || other.unit);
 
1649
    }
 
1650
};
 
1651
 
 
1652
})(require('../tree'));
 
1653
(function (tree) {
 
1654
 
 
1655
tree.Directive = function (name, value) {
 
1656
    this.name = name;
 
1657
    if (Array.isArray(value)) {
 
1658
        this.ruleset = new(tree.Ruleset)([], value);
 
1659
    } else {
 
1660
        this.value = value;
 
1661
    }
 
1662
};
 
1663
tree.Directive.prototype = {
 
1664
    toCSS: function (ctx, env) {
 
1665
        if (this.ruleset) {
 
1666
            this.ruleset.root = true;
 
1667
            return this.name + (env.compress ? '{' : ' {\n  ') +
 
1668
                   this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n  ') +
 
1669
                               (env.compress ? '}': '\n}\n');
 
1670
        } else {
 
1671
            return this.name + ' ' + this.value.toCSS() + ';\n';
 
1672
        }
 
1673
    },
 
1674
    eval: function (env) {
 
1675
        env.frames.unshift(this);
 
1676
        this.ruleset = this.ruleset && this.ruleset.eval(env);
 
1677
        env.frames.shift();
 
1678
        return this;
 
1679
    },
 
1680
    variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) },
 
1681
    find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) },
 
1682
    rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }
 
1683
};
 
1684
 
 
1685
})(require('../tree'));
 
1686
(function (tree) {
 
1687
 
 
1688
tree.Element = function (combinator, value, index) {
 
1689
    this.combinator = combinator instanceof tree.Combinator ?
 
1690
                      combinator : new(tree.Combinator)(combinator);
 
1691
    this.value = value ? value.trim() : "";
 
1692
    this.index = index;
 
1693
};
 
1694
tree.Element.prototype.toCSS = function (env) {
 
1695
    return this.combinator.toCSS(env || {}) + this.value;
 
1696
};
 
1697
 
 
1698
tree.Combinator = function (value) {
 
1699
    if (value === ' ') {
 
1700
        this.value = ' ';
 
1701
    } else if (value === '& ') {
 
1702
        this.value = '& ';
 
1703
    } else {
 
1704
        this.value = value ? value.trim() : "";
 
1705
    }
 
1706
};
 
1707
tree.Combinator.prototype.toCSS = function (env) {
 
1708
    return {
 
1709
        ''  : '',
 
1710
        ' ' : ' ',
 
1711
        '&' : '',
 
1712
        '& ' : ' ',
 
1713
        ':' : ' :',
 
1714
        '::': '::',
 
1715
        '+' : env.compress ? '+' : ' + ',
 
1716
        '~' : env.compress ? '~' : ' ~ ',
 
1717
        '>' : env.compress ? '>' : ' > '
 
1718
    }[this.value];
 
1719
};
 
1720
 
 
1721
})(require('../tree'));
 
1722
(function (tree) {
 
1723
 
 
1724
tree.Expression = function (value) { this.value = value };
 
1725
tree.Expression.prototype = {
 
1726
    eval: function (env) {
 
1727
        if (this.value.length > 1) {
 
1728
            return new(tree.Expression)(this.value.map(function (e) {
 
1729
                return e.eval(env);
 
1730
            }));
 
1731
        } else if (this.value.length === 1) {
 
1732
            return this.value[0].eval(env);
 
1733
        } else {
 
1734
            return this;
 
1735
        }
 
1736
    },
 
1737
    toCSS: function (env) {
 
1738
        return this.value.map(function (e) {
 
1739
            return e.toCSS(env);
 
1740
        }).join(' ');
 
1741
    }
 
1742
};
 
1743
 
 
1744
})(require('../tree'));
 
1745
(function (tree) {
 
1746
//
 
1747
// CSS @import node
 
1748
//
 
1749
// The general strategy here is that we don't want to wait
 
1750
// for the parsing to be completed, before we start importing
 
1751
// the file. That's because in the context of a browser,
 
1752
// most of the time will be spent waiting for the server to respond.
 
1753
//
 
1754
// On creation, we push the import path to our import queue, though
 
1755
// `import,push`, we also pass it a callback, which it'll call once
 
1756
// the file has been fetched, and parsed.
 
1757
//
 
1758
tree.Import = function (path, imports) {
 
1759
    var that = this;
 
1760
 
 
1761
    this._path = path;
 
1762
 
 
1763
    // The '.less' extension is optional
 
1764
    if (path instanceof tree.Quoted) {
 
1765
        this.path = /\.(le?|c)ss(\?.*)?$/.test(path.value) ? path.value : path.value + '.less';
 
1766
    } else {
 
1767
        this.path = path.value.value || path.value;
 
1768
    }
 
1769
 
 
1770
    this.css = /css(\?.*)?$/.test(this.path);
 
1771
 
 
1772
    // Only pre-compile .less files
 
1773
    if (! this.css) {
 
1774
        imports.push(this.path, function (root) {
 
1775
            if (! root) {
 
1776
                throw new(Error)("Error parsing " + that.path);
 
1777
            }
 
1778
            that.root = root;
 
1779
        });
 
1780
    }
 
1781
};
 
1782
 
 
1783
//
 
1784
// The actual import node doesn't return anything, when converted to CSS.
 
1785
// The reason is that it's used at the evaluation stage, so that the rules
 
1786
// it imports can be treated like any other rules.
 
1787
//
 
1788
// In `eval`, we make sure all Import nodes get evaluated, recursively, so
 
1789
// we end up with a flat structure, which can easily be imported in the parent
 
1790
// ruleset.
 
1791
//
 
1792
tree.Import.prototype = {
 
1793
    toCSS: function () {
 
1794
        if (this.css) {
 
1795
            return "@import " + this._path.toCSS() + ';\n';
 
1796
        } else {
 
1797
            return "";
 
1798
        }
 
1799
    },
 
1800
    eval: function (env) {
 
1801
        var ruleset;
 
1802
 
 
1803
        if (this.css) {
 
1804
            return this;
 
1805
        } else {
 
1806
            ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0));
 
1807
 
 
1808
            for (var i = 0; i < ruleset.rules.length; i++) {
 
1809
                if (ruleset.rules[i] instanceof tree.Import) {
 
1810
                    Array.prototype
 
1811
                         .splice
 
1812
                         .apply(ruleset.rules,
 
1813
                                [i, 1].concat(ruleset.rules[i].eval(env)));
 
1814
                }
 
1815
            }
 
1816
            return ruleset.rules;
 
1817
        }
 
1818
    }
 
1819
};
 
1820
 
 
1821
})(require('../tree'));
 
1822
(function (tree) {
 
1823
 
 
1824
tree.JavaScript = function (string, index, escaped) {
 
1825
    this.escaped = escaped;
 
1826
    this.expression = string;
 
1827
    this.index = index;
 
1828
};
 
1829
tree.JavaScript.prototype = {
 
1830
    eval: function (env) {
 
1831
        var result,
 
1832
            that = this,
 
1833
            context = {};
 
1834
 
 
1835
        var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) {
 
1836
            return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env));
 
1837
        });
 
1838
 
 
1839
        try {
 
1840
            expression = new(Function)('return (' + expression + ')');
 
1841
        } catch (e) {
 
1842
            throw { message: "JavaScript evaluation error: `" + expression + "`" ,
 
1843
                    index: this.index };
 
1844
        }
 
1845
 
 
1846
        for (var k in env.frames[0].variables()) {
 
1847
            context[k.slice(1)] = {
 
1848
                value: env.frames[0].variables()[k].value,
 
1849
                toJS: function () {
 
1850
                    return this.value.eval(env).toCSS();
 
1851
                }
 
1852
            };
 
1853
        }
 
1854
 
 
1855
        try {
 
1856
            result = expression.call(context);
 
1857
        } catch (e) {
 
1858
            throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" ,
 
1859
                    index: this.index };
 
1860
        }
 
1861
        if (typeof(result) === 'string') {
 
1862
            return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index);
 
1863
        } else if (Array.isArray(result)) {
 
1864
            return new(tree.Anonymous)(result.join(', '));
 
1865
        } else {
 
1866
            return new(tree.Anonymous)(result);
 
1867
        }
 
1868
    }
 
1869
};
 
1870
 
 
1871
})(require('../tree'));
 
1872
 
 
1873
(function (tree) {
 
1874
 
 
1875
tree.Keyword = function (value) { this.value = value };
 
1876
tree.Keyword.prototype = {
 
1877
    eval: function () { return this },
 
1878
    toCSS: function () { return this.value }
 
1879
};
 
1880
 
 
1881
})(require('../tree'));
 
1882
(function (tree) {
 
1883
 
 
1884
tree.mixin = {};
 
1885
tree.mixin.Call = function (elements, args, index) {
 
1886
    this.selector = new(tree.Selector)(elements);
 
1887
    this.arguments = args;
 
1888
    this.index = index;
 
1889
};
 
1890
tree.mixin.Call.prototype = {
 
1891
    eval: function (env) {
 
1892
        var mixins, args, rules = [], match = false;
 
1893
 
 
1894
        for (var i = 0; i < env.frames.length; i++) {
 
1895
            if ((mixins = env.frames[i].find(this.selector)).length > 0) {
 
1896
                args = this.arguments && this.arguments.map(function (a) { return a.eval(env) });
 
1897
                for (var m = 0; m < mixins.length; m++) {
 
1898
                    if (mixins[m].match(args, env)) {
 
1899
                        try {
 
1900
                            Array.prototype.push.apply(
 
1901
                                  rules, mixins[m].eval(env, this.arguments).rules);
 
1902
                            match = true;
 
1903
                        } catch (e) {
 
1904
                            throw { message: e.message, index: e.index, stack: e.stack, call: this.index };
 
1905
                        }
 
1906
                    }
 
1907
                }
 
1908
                if (match) {
 
1909
                    return rules;
 
1910
                } else {
 
1911
                    throw { message: 'No matching definition was found for `' +
 
1912
                                      this.selector.toCSS().trim() + '('      +
 
1913
                                      this.arguments.map(function (a) {
 
1914
                                          return a.toCSS();
 
1915
                                      }).join(', ') + ")`",
 
1916
                            index:   this.index };
 
1917
                }
 
1918
            }
 
1919
        }
 
1920
        throw { message: this.selector.toCSS().trim() + " is undefined",
 
1921
                index: this.index };
 
1922
    }
 
1923
};
 
1924
 
 
1925
tree.mixin.Definition = function (name, params, rules) {
 
1926
    this.name = name;
 
1927
    this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])];
 
1928
    this.params = params;
 
1929
    this.arity = params.length;
 
1930
    this.rules = rules;
 
1931
    this._lookups = {};
 
1932
    this.required = params.reduce(function (count, p) {
 
1933
        if (!p.name || (p.name && !p.value)) { return count + 1 }
 
1934
        else                                 { return count }
 
1935
    }, 0);
 
1936
    this.parent = tree.Ruleset.prototype;
 
1937
    this.frames = [];
 
1938
};
 
1939
tree.mixin.Definition.prototype = {
 
1940
    toCSS:     function ()     { return "" },
 
1941
    variable:  function (name) { return this.parent.variable.call(this, name) },
 
1942
    variables: function ()     { return this.parent.variables.call(this) },
 
1943
    find:      function ()     { return this.parent.find.apply(this, arguments) },
 
1944
    rulesets:  function ()     { return this.parent.rulesets.apply(this) },
 
1945
 
 
1946
    eval: function (env, args) {
 
1947
        var frame = new(tree.Ruleset)(null, []), context, _arguments = [];
 
1948
 
 
1949
        for (var i = 0, val; i < this.params.length; i++) {
 
1950
            if (this.params[i].name) {
 
1951
                if (val = (args && args[i]) || this.params[i].value) {
 
1952
                    frame.rules.unshift(new(tree.Rule)(this.params[i].name, val.eval(env)));
 
1953
                } else {
 
1954
                    throw { message: "wrong number of arguments for " + this.name +
 
1955
                            ' (' + args.length + ' for ' + this.arity + ')' };
 
1956
                }
 
1957
            }
 
1958
        }
 
1959
        for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) {
 
1960
            _arguments.push(args[i] || this.params[i].value);
 
1961
        }
 
1962
        frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env)));
 
1963
 
 
1964
        return new(tree.Ruleset)(null, this.rules.slice(0)).eval({
 
1965
            frames: [this, frame].concat(this.frames, env.frames)
 
1966
        });
 
1967
    },
 
1968
    match: function (args, env) {
 
1969
        var argsLength = (args && args.length) || 0, len;
 
1970
 
 
1971
        if (argsLength < this.required)                               { return false }
 
1972
        if ((this.required > 0) && (argsLength > this.params.length)) { return false }
 
1973
 
 
1974
        len = Math.min(argsLength, this.arity);
 
1975
 
 
1976
        for (var i = 0; i < len; i++) {
 
1977
            if (!this.params[i].name) {
 
1978
                if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) {
 
1979
                    return false;
 
1980
                }
 
1981
            }
 
1982
        }
 
1983
        return true;
 
1984
    }
 
1985
};
 
1986
 
 
1987
})(require('../tree'));
 
1988
(function (tree) {
 
1989
 
 
1990
tree.Operation = function (op, operands) {
 
1991
    this.op = op.trim();
 
1992
    this.operands = operands;
 
1993
};
 
1994
tree.Operation.prototype.eval = function (env) {
 
1995
    var a = this.operands[0].eval(env),
 
1996
        b = this.operands[1].eval(env),
 
1997
        temp;
 
1998
 
 
1999
    if (a instanceof tree.Dimension && b instanceof tree.Color) {
 
2000
        if (this.op === '*' || this.op === '+') {
 
2001
            temp = b, b = a, a = temp;
 
2002
        } else {
 
2003
            throw { name: "OperationError",
 
2004
                    message: "Can't substract or divide a color from a number" };
 
2005
        }
 
2006
    }
 
2007
    return a.operate(this.op, b);
 
2008
};
 
2009
 
 
2010
tree.operate = function (op, a, b) {
 
2011
    switch (op) {
 
2012
        case '+': return a + b;
 
2013
        case '-': return a - b;
 
2014
        case '*': return a * b;
 
2015
        case '/': return a / b;
 
2016
    }
 
2017
};
 
2018
 
 
2019
})(require('../tree'));
 
2020
(function (tree) {
 
2021
 
 
2022
tree.Quoted = function (str, content, escaped, i) {
 
2023
    this.escaped = escaped;
 
2024
    this.value = content || '';
 
2025
    this.quote = str.charAt(0);
 
2026
    this.index = i;
 
2027
};
 
2028
tree.Quoted.prototype = {
 
2029
    toCSS: function () {
 
2030
        if (this.escaped) {
 
2031
            return this.value;
 
2032
        } else {
 
2033
            return this.quote + this.value + this.quote;
 
2034
        }
 
2035
    },
 
2036
    eval: function (env) {
 
2037
        var that = this;
 
2038
        var value = this.value.replace(/`([^`]+)`/g, function (_, exp) {
 
2039
            return new(tree.JavaScript)(exp, that.index, true).eval(env).value;
 
2040
        }).replace(/@\{([\w-]+)\}/g, function (_, name) {
 
2041
            var v = new(tree.Variable)('@' + name, that.index).eval(env);
 
2042
            return v.value || v.toCSS();
 
2043
        });
 
2044
        return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index);
 
2045
    }
 
2046
};
 
2047
 
 
2048
})(require('../tree'));
 
2049
(function (tree) {
 
2050
 
 
2051
tree.Rule = function (name, value, important, index) {
 
2052
    this.name = name;
 
2053
    this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]);
 
2054
    this.important = important ? ' ' + important.trim() : '';
 
2055
    this.index = index;
 
2056
 
 
2057
    if (name.charAt(0) === '@') {
 
2058
        this.variable = true;
 
2059
    } else { this.variable = false }
 
2060
};
 
2061
tree.Rule.prototype.toCSS = function (env) {
 
2062
    if (this.variable) { return "" }
 
2063
    else {
 
2064
        return this.name + (env.compress ? ':' : ': ') +
 
2065
               this.value.toCSS(env) +
 
2066
               this.important + ";";
 
2067
    }
 
2068
};
 
2069
 
 
2070
tree.Rule.prototype.eval = function (context) {
 
2071
    return new(tree.Rule)(this.name, this.value.eval(context), this.important, this.index);
 
2072
};
 
2073
 
 
2074
tree.Shorthand = function (a, b) {
 
2075
    this.a = a;
 
2076
    this.b = b;
 
2077
};
 
2078
 
 
2079
tree.Shorthand.prototype = {
 
2080
    toCSS: function (env) {
 
2081
        return this.a.toCSS(env) + "/" + this.b.toCSS(env);
 
2082
    },
 
2083
    eval: function () { return this }
 
2084
};
 
2085
 
 
2086
})(require('../tree'));
 
2087
(function (tree) {
 
2088
 
 
2089
tree.Ruleset = function (selectors, rules) {
 
2090
    this.selectors = selectors;
 
2091
    this.rules = rules;
 
2092
    this._lookups = {};
 
2093
};
 
2094
tree.Ruleset.prototype = {
 
2095
    eval: function (env) {
 
2096
        var ruleset = new(tree.Ruleset)(this.selectors, this.rules.slice(0));
 
2097
 
 
2098
        ruleset.root = this.root;
 
2099
 
 
2100
        // push the current ruleset to the frames stack
 
2101
        env.frames.unshift(ruleset);
 
2102
 
 
2103
        // Evaluate imports
 
2104
        if (ruleset.root) {
 
2105
            for (var i = 0; i < ruleset.rules.length; i++) {
 
2106
                if (ruleset.rules[i] instanceof tree.Import) {
 
2107
                    Array.prototype.splice
 
2108
                         .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env)));
 
2109
                }
 
2110
            }
 
2111
        }
 
2112
 
 
2113
        // Store the frames around mixin definitions,
 
2114
        // so they can be evaluated like closures when the time comes.
 
2115
        for (var i = 0; i < ruleset.rules.length; i++) {
 
2116
            if (ruleset.rules[i] instanceof tree.mixin.Definition) {
 
2117
                ruleset.rules[i].frames = env.frames.slice(0);
 
2118
            }
 
2119
        }
 
2120
 
 
2121
        // Evaluate mixin calls.
 
2122
        for (var i = 0; i < ruleset.rules.length; i++) {
 
2123
            if (ruleset.rules[i] instanceof tree.mixin.Call) {
 
2124
                Array.prototype.splice
 
2125
                     .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env)));
 
2126
            }
 
2127
        }
 
2128
 
 
2129
        // Evaluate everything else
 
2130
        for (var i = 0, rule; i < ruleset.rules.length; i++) {
 
2131
            rule = ruleset.rules[i];
 
2132
 
 
2133
            if (! (rule instanceof tree.mixin.Definition)) {
 
2134
                ruleset.rules[i] = rule.eval ? rule.eval(env) : rule;
 
2135
            }
 
2136
        }
 
2137
 
 
2138
        // Pop the stack
 
2139
        env.frames.shift();
 
2140
 
 
2141
        return ruleset;
 
2142
    },
 
2143
    match: function (args) {
 
2144
        return !args || args.length === 0;
 
2145
    },
 
2146
    variables: function () {
 
2147
        if (this._variables) { return this._variables }
 
2148
        else {
 
2149
            return this._variables = this.rules.reduce(function (hash, r) {
 
2150
                if (r instanceof tree.Rule && r.variable === true) {
 
2151
                    hash[r.name] = r;
 
2152
                }
 
2153
                return hash;
 
2154
            }, {});
 
2155
        }
 
2156
    },
 
2157
    variable: function (name) {
 
2158
        return this.variables()[name];
 
2159
    },
 
2160
    rulesets: function () {
 
2161
        if (this._rulesets) { return this._rulesets }
 
2162
        else {
 
2163
            return this._rulesets = this.rules.filter(function (r) {
 
2164
                return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition);
 
2165
            });
 
2166
        }
 
2167
    },
 
2168
    find: function (selector, self) {
 
2169
        self = self || this;
 
2170
        var rules = [], rule, match,
 
2171
            key = selector.toCSS();
 
2172
 
 
2173
        if (key in this._lookups) { return this._lookups[key] }
 
2174
 
 
2175
        this.rulesets().forEach(function (rule) {
 
2176
            if (rule !== self) {
 
2177
                for (var j = 0; j < rule.selectors.length; j++) {
 
2178
                    if (match = selector.match(rule.selectors[j])) {
 
2179
                        if (selector.elements.length > rule.selectors[j].elements.length) {
 
2180
                            Array.prototype.push.apply(rules, rule.find(
 
2181
                                new(tree.Selector)(selector.elements.slice(1)), self));
 
2182
                        } else {
 
2183
                            rules.push(rule);
 
2184
                        }
 
2185
                        break;
 
2186
                    }
 
2187
                }
 
2188
            }
 
2189
        });
 
2190
        return this._lookups[key] = rules;
 
2191
    },
 
2192
    //
 
2193
    // Entry point for code generation
 
2194
    //
 
2195
    //     `context` holds an array of arrays.
 
2196
    //
 
2197
    toCSS: function (context, env) {
 
2198
        var css = [],      // The CSS output
 
2199
            rules = [],    // node.Rule instances
 
2200
            rulesets = [], // node.Ruleset instances
 
2201
            paths = [],    // Current selectors
 
2202
            selector,      // The fully rendered selector
 
2203
            rule;
 
2204
 
 
2205
        if (! this.root) {
 
2206
            if (context.length === 0) {
 
2207
                paths = this.selectors.map(function (s) { return [s] });
 
2208
            } else {
 
2209
                this.joinSelectors( paths, context, this.selectors );
 
2210
            }
 
2211
        }
 
2212
 
 
2213
        // Compile rules and rulesets
 
2214
        for (var i = 0; i < this.rules.length; i++) {
 
2215
            rule = this.rules[i];
 
2216
 
 
2217
            if (rule.rules || (rule instanceof tree.Directive)) {
 
2218
                rulesets.push(rule.toCSS(paths, env));
 
2219
            } else if (rule instanceof tree.Comment) {
 
2220
                if (!rule.silent) {
 
2221
                    if (this.root) {
 
2222
                        rulesets.push(rule.toCSS(env));
 
2223
                    } else {
 
2224
                        rules.push(rule.toCSS(env));
 
2225
                    }
 
2226
                }
 
2227
            } else {
 
2228
                if (rule.toCSS && !rule.variable) {
 
2229
                    rules.push(rule.toCSS(env));
 
2230
                } else if (rule.value && !rule.variable) {
 
2231
                    rules.push(rule.value.toString());
 
2232
                }
 
2233
            }
 
2234
        } 
 
2235
 
 
2236
        rulesets = rulesets.join('');
 
2237
 
 
2238
        // If this is the root node, we don't render
 
2239
        // a selector, or {}.
 
2240
        // Otherwise, only output if this ruleset has rules.
 
2241
        if (this.root) {
 
2242
            css.push(rules.join(env.compress ? '' : '\n'));
 
2243
        } else {
 
2244
            if (rules.length > 0) {
 
2245
                selector = paths.map(function (p) {
 
2246
                    return p.map(function (s) {
 
2247
                        return s.toCSS(env);
 
2248
                    }).join('').trim();
 
2249
                }).join(env.compress ? ',' : (paths.length > 3 ? ',\n' : ', '));
 
2250
                css.push(selector,
 
2251
                        (env.compress ? '{' : ' {\n  ') +
 
2252
                        rules.join(env.compress ? '' : '\n  ') +
 
2253
                        (env.compress ? '}' : '\n}\n'));
 
2254
            }
 
2255
        }
 
2256
        css.push(rulesets);
 
2257
 
 
2258
        return css.join('') + (env.compress ? '\n' : '');
 
2259
    },
 
2260
 
 
2261
    joinSelectors: function (paths, context, selectors) {
 
2262
        for (var s = 0; s < selectors.length; s++) {
 
2263
            this.joinSelector(paths, context, selectors[s]);
 
2264
        }
 
2265
    },
 
2266
 
 
2267
    joinSelector: function (paths, context, selector) {
 
2268
        var before = [], after = [], beforeElements = [],
 
2269
            afterElements = [], hasParentSelector = false, el;
 
2270
 
 
2271
        for (var i = 0; i < selector.elements.length; i++) {
 
2272
            el = selector.elements[i];
 
2273
            if (el.combinator.value.charAt(0) === '&') {
 
2274
                hasParentSelector = true;
 
2275
            }
 
2276
            if (hasParentSelector) afterElements.push(el);
 
2277
            else                   beforeElements.push(el);
 
2278
        }
 
2279
 
 
2280
        if (! hasParentSelector) {
 
2281
            afterElements = beforeElements;
 
2282
            beforeElements = [];
 
2283
        }
 
2284
 
 
2285
        if (beforeElements.length > 0) {
 
2286
            before.push(new(tree.Selector)(beforeElements));
 
2287
        }
 
2288
 
 
2289
        if (afterElements.length > 0) {
 
2290
            after.push(new(tree.Selector)(afterElements));
 
2291
        }
 
2292
 
 
2293
        for (var c = 0; c < context.length; c++) {
 
2294
            paths.push(before.concat(context[c]).concat(after));
 
2295
        }
 
2296
    }
 
2297
};
 
2298
})(require('../tree'));
 
2299
(function (tree) {
 
2300
 
 
2301
tree.Selector = function (elements) {
 
2302
    this.elements = elements;
 
2303
    if (this.elements[0].combinator.value === "") {
 
2304
        this.elements[0].combinator.value = ' ';
 
2305
    }
 
2306
};
 
2307
tree.Selector.prototype.match = function (other) {
 
2308
    var len  = this.elements.length,
 
2309
        olen = other.elements.length,
 
2310
        max  = Math.min(len, olen);
 
2311
 
 
2312
    if (len < olen) {
 
2313
        return false;
 
2314
    } else {
 
2315
        for (var i = 0; i < max; i++) {
 
2316
            if (this.elements[i].value !== other.elements[i].value) {
 
2317
                return false;
 
2318
            }
 
2319
        }
 
2320
    }
 
2321
    return true;
 
2322
};
 
2323
tree.Selector.prototype.toCSS = function (env) {
 
2324
    if (this._css) { return this._css }
 
2325
 
 
2326
    return this._css = this.elements.map(function (e) {
 
2327
        if (typeof(e) === 'string') {
 
2328
            return ' ' + e.trim();
 
2329
        } else {
 
2330
            return e.toCSS(env);
 
2331
        }
 
2332
    }).join('');
 
2333
};
 
2334
 
 
2335
})(require('../tree'));
 
2336
(function (tree) {
 
2337
 
 
2338
tree.URL = function (val, paths) {
 
2339
    if (val.data) {
 
2340
        this.attrs = val;
 
2341
    } else {
 
2342
        // Add the base path if the URL is relative and we are in the browser
 
2343
        if (!/^(?:https?:\/\/|file:\/\/|data:)?/.test(val.value) && paths.length > 0 && typeof(window) !== 'undefined') {
 
2344
            val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value);
 
2345
        }
 
2346
        this.value = val;
 
2347
        this.paths = paths;
 
2348
    }
 
2349
};
 
2350
tree.URL.prototype = {
 
2351
    toCSS: function () {
 
2352
        return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data
 
2353
                                    : this.value.toCSS()) + ")";
 
2354
    },
 
2355
    eval: function (ctx) {
 
2356
        return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths);
 
2357
    }
 
2358
};
 
2359
 
 
2360
})(require('../tree'));
 
2361
(function (tree) {
 
2362
 
 
2363
tree.Value = function (value) {
 
2364
    this.value = value;
 
2365
    this.is = 'value';
 
2366
};
 
2367
tree.Value.prototype = {
 
2368
    eval: function (env) {
 
2369
        if (this.value.length === 1) {
 
2370
            return this.value[0].eval(env);
 
2371
        } else {
 
2372
            return new(tree.Value)(this.value.map(function (v) {
 
2373
                return v.eval(env);
 
2374
            }));
 
2375
        }
 
2376
    },
 
2377
    toCSS: function (env) {
 
2378
        return this.value.map(function (e) {
 
2379
            return e.toCSS(env);
 
2380
        }).join(env.compress ? ',' : ', ');
 
2381
    }
 
2382
};
 
2383
 
 
2384
})(require('../tree'));
 
2385
(function (tree) {
 
2386
 
 
2387
tree.Variable = function (name, index) { this.name = name, this.index = index };
 
2388
tree.Variable.prototype = {
 
2389
    eval: function (env) {
 
2390
        var variable, v, name = this.name;
 
2391
 
 
2392
        if (name.indexOf('@@') == 0) {
 
2393
            name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value;
 
2394
        }
 
2395
 
 
2396
        if (variable = tree.find(env.frames, function (frame) {
 
2397
            if (v = frame.variable(name)) {
 
2398
                return v.value.eval(env);
 
2399
            }
 
2400
        })) { return variable }
 
2401
        else {
 
2402
            throw { message: "variable " + name + " is undefined",
 
2403
                    index: this.index };
 
2404
        }
 
2405
    }
 
2406
};
 
2407
 
 
2408
})(require('../tree'));
 
2409
require('./tree').find = function (obj, fun) {
 
2410
    for (var i = 0, r; i < obj.length; i++) {
 
2411
        if (r = fun.call(obj, obj[i])) { return r }
 
2412
    }
 
2413
    return null;
 
2414
};
 
2415
require('./tree').jsify = function (obj) {
 
2416
    if (Array.isArray(obj.value) && (obj.value.length > 1)) {
 
2417
        return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']';
 
2418
    } else {
 
2419
        return obj.toCSS(false);
 
2420
    }
 
2421
};
 
2422
var name;
 
2423
 
 
2424
function loadStyleSheet(sheet, callback, reload, remaining) {
 
2425
    var sheetName = name.slice(0, name.lastIndexOf('/') + 1) + sheet.href;
 
2426
    var input = readFile(sheetName);
 
2427
    var parser = new less.Parser();
 
2428
    parser.parse(input, function (e, root) {
 
2429
        if (e) {
 
2430
            print("Error: " + e);
 
2431
            quit(1);
 
2432
        }
 
2433
        callback(root, sheet, { local: false, lastModified: 0, remaining: remaining });
 
2434
    });
 
2435
 
 
2436
    // callback({}, sheet, { local: true, remaining: remaining });
 
2437
}
 
2438
 
 
2439
function writeFile(filename, content) {
 
2440
    var fstream = new java.io.FileWriter(filename);
 
2441
    var out = new java.io.BufferedWriter(fstream);
 
2442
    out.write(content);
 
2443
    out.close();
 
2444
}
 
2445
 
 
2446
// Command line integration via Rhino
 
2447
(function (args) {
 
2448
    name = args[0];
 
2449
    var output = args[1];
 
2450
 
 
2451
    if (!name) {
 
2452
        print('No files present in the fileset; Check your pattern match in build.xml');
 
2453
        quit(1);
 
2454
    }
 
2455
    path = name.split("/");path.pop();path=path.join("/")
 
2456
 
 
2457
    var input = readFile(name);
 
2458
 
 
2459
    if (!input) {
 
2460
        print('lesscss: couldn\'t open file ' + name);
 
2461
        quit(1);
 
2462
    }
 
2463
 
 
2464
    var result;
 
2465
    var parser = new less.Parser();
 
2466
    parser.parse(input, function (e, root) {
 
2467
        if (e) {
 
2468
            quit(1);
 
2469
        } else {
 
2470
            result = root.toCSS();
 
2471
            if (output) {
 
2472
                writeFile(output, result);
 
2473
                print("Written to " + output);
 
2474
            } else {
 
2475
                print(result);
 
2476
            }
 
2477
            quit(0);
 
2478
        }
 
2479
    });
 
2480
    print("done");
 
2481
}(arguments));