source: Dev/trunk/src/client/util/less/dist/less-rhino-1.1.5.js @ 483

Last change on this file since 483 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 81.3 KB
Line 
1//
2// Stub out `require` in rhino
3//
4function 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//
18if (!Array.isArray) {
19    Array.isArray = function(obj) {
20        return Object.prototype.toString.call(obj) === "[object Array]" ||
21               (obj instanceof Array);
22    };
23}
24if (!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}
34if (!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}
48if (!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}
60if (!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}
88if (!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//
108if (!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//
123if (!String.prototype.trim) {
124    String.prototype.trim = function () {
125        return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, '');
126    };
127}
128var less, tree;
129
130if (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//
181less.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
1229if (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
1246tree.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
1407function hsla(hsla) {
1408    return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a);
1409}
1410
1411function 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
1424function clamp(val) {
1425    return Math.min(1, Math.max(0, val));
1426}
1427
1428})(require('./tree'));
1429(function (tree) {
1430
1431tree.Alpha = function (val) {
1432    this.value = val;
1433};
1434tree.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
1448tree.Anonymous = function (string) {
1449    this.value = string.value || string;
1450};
1451tree.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//
1464tree.Call = function (name, args, index) {
1465    this.name = name;
1466    this.args = args;
1467    this.index = index;
1468};
1469tree.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//
1508tree.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};
1528tree.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
1607tree.Comment = function (value, silent) {
1608    this.value = value;
1609    this.silent = !!silent;
1610};
1611tree.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//
1624tree.Dimension = function (value, unit) {
1625    this.value = parseFloat(value);
1626    this.unit = unit || null;
1627};
1628
1629tree.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
1655tree.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};
1663tree.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
1688tree.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};
1694tree.Element.prototype.toCSS = function (env) {
1695    return this.combinator.toCSS(env || {}) + this.value;
1696};
1697
1698tree.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};
1707tree.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
1724tree.Expression = function (value) { this.value = value };
1725tree.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//
1758tree.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//
1792tree.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
1824tree.JavaScript = function (string, index, escaped) {
1825    this.escaped = escaped;
1826    this.expression = string;
1827    this.index = index;
1828};
1829tree.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
1875tree.Keyword = function (value) { this.value = value };
1876tree.Keyword.prototype = {
1877    eval: function () { return this },
1878    toCSS: function () { return this.value }
1879};
1880
1881})(require('../tree'));
1882(function (tree) {
1883
1884tree.mixin = {};
1885tree.mixin.Call = function (elements, args, index) {
1886    this.selector = new(tree.Selector)(elements);
1887    this.arguments = args;
1888    this.index = index;
1889};
1890tree.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
1925tree.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};
1939tree.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
1990tree.Operation = function (op, operands) {
1991    this.op = op.trim();
1992    this.operands = operands;
1993};
1994tree.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
2010tree.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
2022tree.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};
2028tree.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
2051tree.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};
2061tree.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
2070tree.Rule.prototype.eval = function (context) {
2071    return new(tree.Rule)(this.name, this.value.eval(context), this.important, this.index);
2072};
2073
2074tree.Shorthand = function (a, b) {
2075    this.a = a;
2076    this.b = b;
2077};
2078
2079tree.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
2089tree.Ruleset = function (selectors, rules) {
2090    this.selectors = selectors;
2091    this.rules = rules;
2092    this._lookups = {};
2093};
2094tree.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
2301tree.Selector = function (elements) {
2302    this.elements = elements;
2303    if (this.elements[0].combinator.value === "") {
2304        this.elements[0].combinator.value = ' ';
2305    }
2306};
2307tree.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};
2323tree.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
2338tree.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};
2350tree.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
2363tree.Value = function (value) {
2364    this.value = value;
2365    this.is = 'value';
2366};
2367tree.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
2387tree.Variable = function (name, index) { this.name = name, this.index = index };
2388tree.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'));
2409require('./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};
2415require('./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};
2422var name;
2423
2424function 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
2439function 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));
Note: See TracBrowser for help on using the repository browser.