source: Dev/trunk/src/client/util/less/dist/less-rhino-1.3.1.js @ 529

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

Added Dojo 1.9.3 release.

File size: 124.0 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    if (typeof(window) === 'undefined') { less = {} }
134    else                                { less = window.less = {} }
135    tree = less.tree = {};
136    less.mode = 'rhino';
137} else if (typeof(window) === 'undefined') {
138    // Node.js
139    less = exports,
140    tree = require('./tree');
141    less.mode = 'node';
142} else {
143    // Browser
144    if (typeof(window.less) === 'undefined') { window.less = {} }
145    less = window.less,
146    tree = window.less.tree = {};
147    less.mode = 'browser';
148}
149//
150// less.js - parser
151//
152//    A relatively straight-forward predictive parser.
153//    There is no tokenization/lexing stage, the input is parsed
154//    in one sweep.
155//
156//    To make the parser fast enough to run in the browser, several
157//    optimization had to be made:
158//
159//    - Matching and slicing on a huge input is often cause of slowdowns.
160//      The solution is to chunkify the input into smaller strings.
161//      The chunks are stored in the `chunks` var,
162//      `j` holds the current chunk index, and `current` holds
163//      the index of the current chunk in relation to `input`.
164//      This gives us an almost 4x speed-up.
165//
166//    - In many cases, we don't need to match individual tokens;
167//      for example, if a value doesn't hold any variables, operations
168//      or dynamic references, the parser can effectively 'skip' it,
169//      treating it as a literal.
170//      An example would be '1px solid #000' - which evaluates to itself,
171//      we don't need to know what the individual components are.
172//      The drawback, of course is that you don't get the benefits of
173//      syntax-checking on the CSS. This gives us a 50% speed-up in the parser,
174//      and a smaller speed-up in the code-gen.
175//
176//
177//    Token matching is done with the `$` function, which either takes
178//    a terminal string or regexp, or a non-terminal function to call.
179//    It also takes care of moving all the indices forwards.
180//
181//
182less.Parser = function Parser(env) {
183    var input,       // LeSS input string
184        i,           // current index in `input`
185        j,           // current chunk
186        temp,        // temporarily holds a chunk's state, for backtracking
187        memo,        // temporarily holds `i`, when backtracking
188        furthest,    // furthest index the parser has gone to
189        chunks,      // chunkified input
190        current,     // index of current chunk, in `input`
191        parser;
192
193    var that = this;
194
195    // Top parser on an import tree must be sure there is one "env"
196    // which will then be passed arround by reference.
197    var env = env || { };
198    if (!env.contents) { env.contents={}; }  // env.contents must be passed arround with top env
199
200    // This function is called after all files
201    // have been imported through `@import`.
202    var finish = function () {};
203
204    var imports = this.imports = {
205        paths: env && env.paths || [],  // Search paths, when importing
206        queue: [],                      // Files which haven't been imported yet
207        files: {},                      // Holds the imported parse trees
208        contents: env.contents,         // Holds the imported file contents
209        mime:  env && env.mime,         // MIME type of .less files
210        error: null,                    // Error in parsing/evaluating an import
211        push: function (path, callback) {
212            var that = this;
213            this.queue.push(path);
214
215            //
216            // Import a file asynchronously
217            //
218            less.Parser.importer(path, this.paths, function (e, root) {
219                that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue
220
221                var imported = path in that.files;
222
223                that.files[path] = root;                        // Store the root
224
225                if (e && !that.error) { that.error = e }
226
227                callback(e, root, imported);
228
229                if (that.queue.length === 0) { finish(e) }       // Call `finish` if we're done importing
230            }, env);
231        }
232    };
233
234    function save()    { temp = chunks[j], memo = i, current = i }
235    function restore() { chunks[j] = temp, i = memo, current = i }
236
237    function sync() {
238        if (i > current) {
239            chunks[j] = chunks[j].slice(i - current);
240            current = i;
241        }
242    }
243    function isWhitespace(c) {
244        // Could change to \s?
245        var code = c.charCodeAt(0);
246        return code === 32 || code === 10 || code === 9;
247    }
248    //
249    // Parse from a token, regexp or string, and move forward if match
250    //
251    function $(tok) {
252        var match, args, length, index, k;
253
254        //
255        // Non-terminal
256        //
257        if (tok instanceof Function) {
258            return tok.call(parser.parsers);
259        //
260        // Terminal
261        //
262        //     Either match a single character in the input,
263        //     or match a regexp in the current chunk (chunk[j]).
264        //
265        } else if (typeof(tok) === 'string') {
266            match = input.charAt(i) === tok ? tok : null;
267            length = 1;
268            sync ();
269        } else {
270            sync ();
271
272            if (match = tok.exec(chunks[j])) {
273                length = match[0].length;
274            } else {
275                return null;
276            }
277        }
278
279        // The match is confirmed, add the match length to `i`,
280        // and consume any extra white-space characters (' ' || '\n')
281        // which come after that. The reason for this is that LeSS's
282        // grammar is mostly white-space insensitive.
283        //
284        if (match) {
285            skipWhitespace(length);
286
287            if(typeof(match) === 'string') {
288                return match;
289            } else {
290                return match.length === 1 ? match[0] : match;
291            }
292        }
293    }
294
295    function skipWhitespace(length) {
296        var oldi = i, oldj = j,
297            endIndex = i + chunks[j].length,
298            mem = i += length;
299
300        while (i < endIndex) {
301            if (! isWhitespace(input.charAt(i))) { break }
302            i++;
303        }
304        chunks[j] = chunks[j].slice(length + (i - mem));
305        current = i;
306
307        if (chunks[j].length === 0 && j < chunks.length - 1) { j++ }
308
309        return oldi !== i || oldj !== j;
310    }
311
312    function expect(arg, msg) {
313        var result = $(arg);
314        if (! result) {
315            error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'"
316                                                   : "unexpected token"));
317        } else {
318            return result;
319        }
320    }
321
322    function error(msg, type) {
323        throw { index: i, type: type || 'Syntax', message: msg };
324    }
325
326    // Same as $(), but don't change the state of the parser,
327    // just return the match.
328    function peek(tok) {
329        if (typeof(tok) === 'string') {
330            return input.charAt(i) === tok;
331        } else {
332            if (tok.test(chunks[j])) {
333                return true;
334            } else {
335                return false;
336            }
337        }
338    }
339
340    function getInput(e, env) {
341        if (e.filename && env.filename && (e.filename !== env.filename)) {
342            return parser.imports.contents[e.filename];
343        } else {
344            return input;
345        }
346    }
347
348    function getLocation(index, input) {
349        for (var n = index, column = -1;
350                 n >= 0 && input.charAt(n) !== '\n';
351                 n--) { column++ }
352
353        return { line:   typeof(index) === 'number' ? (input.slice(0, index).match(/\n/g) || "").length : null,
354                 column: column };
355    }
356
357    function getFileName(e) {
358        if(less.mode === 'browser' || less.mode === 'rhino')
359            return e.filename;
360        else
361            return require('path').resolve(e.filename);
362    }
363
364    function getDebugInfo(index, inputStream, e) {
365        return {
366            lineNumber: getLocation(index, inputStream).line + 1,
367            fileName: getFileName(e)
368        };
369    }
370
371    function LessError(e, env) {
372        var input = getInput(e, env),
373            loc = getLocation(e.index, input),
374            line = loc.line,
375            col  = loc.column,
376            lines = input.split('\n');
377
378        this.type = e.type || 'Syntax';
379        this.message = e.message;
380        this.filename = e.filename || env.filename;
381        this.index = e.index;
382        this.line = typeof(line) === 'number' ? line + 1 : null;
383        this.callLine = e.call && (getLocation(e.call, input).line + 1);
384        this.callExtract = lines[getLocation(e.call, input).line];
385        this.stack = e.stack;
386        this.column = col;
387        this.extract = [
388            lines[line - 1],
389            lines[line],
390            lines[line + 1]
391        ];
392    }
393
394    this.env = env = env || {};
395
396    // The optimization level dictates the thoroughness of the parser,
397    // the lower the number, the less nodes it will create in the tree.
398    // This could matter for debugging, or if you want to access
399    // the individual nodes in the tree.
400    this.optimization = ('optimization' in this.env) ? this.env.optimization : 1;
401
402    this.env.filename = this.env.filename || null;
403
404    //
405    // The Parser
406    //
407    return parser = {
408
409        imports: imports,
410        //
411        // Parse an input string into an abstract syntax tree,
412        // call `callback` when done.
413        //
414        parse: function (str, callback) {
415            var root, start, end, zone, line, lines, buff = [], c, error = null;
416
417            i = j = current = furthest = 0;
418            input = str.replace(/\r\n/g, '\n');
419
420            // Remove potential UTF Byte Order Mark
421            input = input.replace(/^\uFEFF/, '');
422
423            // Split the input into chunks.
424            chunks = (function (chunks) {
425                var j = 0,
426                    skip = /(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g,
427                    comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,
428                    string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g,
429                    level = 0,
430                    match,
431                    chunk = chunks[0],
432                    inParam;
433
434                for (var i = 0, c, cc; i < input.length; i++) {
435                    skip.lastIndex = i;
436                    if (match = skip.exec(input)) {
437                        if (match.index === i) {
438                            i += match[0].length;
439                            chunk.push(match[0]);
440                        }
441                    }
442                    c = input.charAt(i);
443                    comment.lastIndex = string.lastIndex = i;
444
445                    if (match = string.exec(input)) {
446                        if (match.index === i) {
447                            i += match[0].length;
448                            chunk.push(match[0]);
449                            c = input.charAt(i);
450                        }
451                    }
452
453                    if (!inParam && c === '/') {
454                        cc = input.charAt(i + 1);
455                        if (cc === '/' || cc === '*') {
456                            if (match = comment.exec(input)) {
457                                if (match.index === i) {
458                                    i += match[0].length;
459                                    chunk.push(match[0]);
460                                    c = input.charAt(i);
461                                }
462                            }
463                        }
464                    }
465                   
466                    switch (c) {
467                        case '{': if (! inParam) { level ++;        chunk.push(c);                           break }
468                        case '}': if (! inParam) { level --;        chunk.push(c); chunks[++j] = chunk = []; break }
469                        case '(': if (! inParam) { inParam = true;  chunk.push(c);                           break }
470                        case ')': if (  inParam) { inParam = false; chunk.push(c);                           break }
471                        default:                                    chunk.push(c);
472                    }
473                }
474                if (level > 0) {
475                    error = new(LessError)({
476                        index: i,
477                        type: 'Parse',
478                        message: "missing closing `}`",
479                        filename: env.filename
480                    }, env);
481                }
482
483                return chunks.map(function (c) { return c.join('') });;
484            })([[]]);
485
486            if (error) {
487                return callback(error);
488            }
489
490            // Start with the primary rule.
491            // The whole syntax tree is held under a Ruleset node,
492            // with the `root` property set to true, so no `{}` are
493            // output. The callback is called when the input is parsed.
494            try {
495                root = new(tree.Ruleset)([], $(this.parsers.primary));
496                root.root = true;
497            } catch (e) {
498                return callback(new(LessError)(e, env));
499            }
500
501            root.toCSS = (function (evaluate) {
502                var line, lines, column;
503
504                return function (options, variables) {
505                    var frames = [], importError;
506
507                    options = options || {};
508                    //
509                    // Allows setting variables with a hash, so:
510                    //
511                    //   `{ color: new(tree.Color)('#f01') }` will become:
512                    //
513                    //   new(tree.Rule)('@color',
514                    //     new(tree.Value)([
515                    //       new(tree.Expression)([
516                    //         new(tree.Color)('#f01')
517                    //       ])
518                    //     ])
519                    //   )
520                    //
521                    if (typeof(variables) === 'object' && !Array.isArray(variables)) {
522                        variables = Object.keys(variables).map(function (k) {
523                            var value = variables[k];
524
525                            if (! (value instanceof tree.Value)) {
526                                if (! (value instanceof tree.Expression)) {
527                                    value = new(tree.Expression)([value]);
528                                }
529                                value = new(tree.Value)([value]);
530                            }
531                            return new(tree.Rule)('@' + k, value, false, 0);
532                        });
533                        frames = [new(tree.Ruleset)(null, variables)];
534                    }
535
536                    try {
537                        var css = evaluate.call(this, { frames: frames })
538                                          .toCSS([], { compress: options.compress || false, dumpLineNumbers: env.dumpLineNumbers });
539                    } catch (e) {
540                        throw new(LessError)(e, env);
541                    }
542
543                    if ((importError = parser.imports.error)) { // Check if there was an error during importing
544                        if (importError instanceof LessError) throw importError;
545                        else                                  throw new(LessError)(importError, env);
546                    }
547
548                    if (options.yuicompress && less.mode === 'node') {
549                        return require('./cssmin').compressor.cssmin(css);
550                    } else if (options.compress) {
551                        return css.replace(/(\s)+/g, "$1");
552                    } else {
553                        return css;
554                    }
555                };
556            })(root.eval);
557
558            // If `i` is smaller than the `input.length - 1`,
559            // it means the parser wasn't able to parse the whole
560            // string, so we've got a parsing error.
561            //
562            // We try to extract a \n delimited string,
563            // showing the line where the parse error occured.
564            // We split it up into two parts (the part which parsed,
565            // and the part which didn't), so we can color them differently.
566            if (i < input.length - 1) {
567                i = furthest;
568                lines = input.split('\n');
569                line = (input.slice(0, i).match(/\n/g) || "").length + 1;
570
571                for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ }
572
573                error = {
574                    type: "Parse",
575                    message: "Syntax Error on line " + line,
576                    index: i,
577                    filename: env.filename,
578                    line: line,
579                    column: column,
580                    extract: [
581                        lines[line - 2],
582                        lines[line - 1],
583                        lines[line]
584                    ]
585                };
586            }
587
588            if (this.imports.queue.length > 0) {
589                finish = function (e) {
590                    if (e) callback(e);
591                    else callback(null, root);
592                };
593            } else {
594                callback(error, root);
595            }
596        },
597
598        //
599        // Here in, the parsing rules/functions
600        //
601        // The basic structure of the syntax tree generated is as follows:
602        //
603        //   Ruleset ->  Rule -> Value -> Expression -> Entity
604        //
605        // Here's some LESS code:
606        //
607        //    .class {
608        //      color: #fff;
609        //      border: 1px solid #000;
610        //      width: @w + 4px;
611        //      > .child {...}
612        //    }
613        //
614        // And here's what the parse tree might look like:
615        //
616        //     Ruleset (Selector '.class', [
617        //         Rule ("color",  Value ([Expression [Color #fff]]))
618        //         Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
619        //         Rule ("width",  Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]]))
620        //         Ruleset (Selector [Element '>', '.child'], [...])
621        //     ])
622        //
623        //  In general, most rules will try to parse a token with the `$()` function, and if the return
624        //  value is truly, will return a new node, of the relevant type. Sometimes, we need to check
625        //  first, before parsing, that's when we use `peek()`.
626        //
627        parsers: {
628            //
629            // The `primary` rule is the *entry* and *exit* point of the parser.
630            // The rules here can appear at any level of the parse tree.
631            //
632            // The recursive nature of the grammar is an interplay between the `block`
633            // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
634            // as represented by this simplified grammar:
635            //
636            //     primary  →  (ruleset | rule)+
637            //     ruleset  →  selector+ block
638            //     block    →  '{' primary '}'
639            //
640            // Only at one point is the primary rule not called from the
641            // block rule: at the root level.
642            //
643            primary: function () {
644                var node, root = [];
645
646                while ((node = $(this.mixin.definition) || $(this.rule)    ||  $(this.ruleset) ||
647                               $(this.mixin.call)       || $(this.comment) ||  $(this.directive))
648                               || $(/^[\s\n]+/)) {
649                    node && root.push(node);
650                }
651                return root;
652            },
653
654            // We create a Comment node for CSS comments `/* */`,
655            // but keep the LeSS comments `//` silent, by just skipping
656            // over them.
657            comment: function () {
658                var comment;
659
660                if (input.charAt(i) !== '/') return;
661
662                if (input.charAt(i + 1) === '/') {
663                    return new(tree.Comment)($(/^\/\/.*/), true);
664                } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) {
665                    return new(tree.Comment)(comment);
666                }
667            },
668
669            //
670            // Entities are tokens which can be found inside an Expression
671            //
672            entities: {
673                //
674                // A string, which supports escaping " and '
675                //
676                //     "milky way" 'he\'s the one!'
677                //
678                quoted: function () {
679                    var str, j = i, e;
680
681                    if (input.charAt(j) === '~') { j++, e = true } // Escaped strings
682                    if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return;
683
684                    e && $('~');
685
686                    if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) {
687                        return new(tree.Quoted)(str[0], str[1] || str[2], e);
688                    }
689                },
690
691                //
692                // A catch-all word, such as:
693                //
694                //     black border-collapse
695                //
696                keyword: function () {
697                    var k;
698
699                    if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) {
700                        if (tree.colors.hasOwnProperty(k)) {
701                            // detect named color
702                            return new(tree.Color)(tree.colors[k].slice(1));
703                        } else {
704                            return new(tree.Keyword)(k);
705                        }
706                    }
707                },
708
709                //
710                // A function call
711                //
712                //     rgb(255, 0, 255)
713                //
714                // We also try to catch IE's `alpha()`, but let the `alpha` parser
715                // deal with the details.
716                //
717                // The arguments are parsed with the `entities.arguments` parser.
718                //
719                call: function () {
720                    var name, nameLC, args, alpha_ret, index = i;
721
722                    if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return;
723
724                    name = name[1];
725                    nameLC = name.toLowerCase();
726
727                    if (nameLC === 'url') { return null }
728                    else                { i += name.length }
729
730                    if (nameLC === 'alpha') {
731                        alpha_ret = $(this.alpha);
732                        if(typeof alpha_ret !== 'undefined') {
733                            return alpha_ret;
734                        }
735                    }
736
737                    $('('); // Parse the '(' and consume whitespace.
738
739                    args = $(this.entities.arguments);
740
741                    if (! $(')')) return;
742
743                    if (name) { return new(tree.Call)(name, args, index, env.filename) }
744                },
745                arguments: function () {
746                    var args = [], arg;
747
748                    while (arg = $(this.entities.assignment) || $(this.expression)) {
749                        args.push(arg);
750                        if (! $(',')) { break }
751                    }
752                    return args;
753                },
754                literal: function () {
755                    return $(this.entities.ratio) ||
756                           $(this.entities.dimension) ||
757                           $(this.entities.color) ||
758                           $(this.entities.quoted);
759                },
760
761                // Assignments are argument entities for calls.
762                // They are present in ie filter properties as shown below.
763                //
764                //     filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
765                //
766
767                assignment: function () {
768                    var key, value;
769                    if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) {
770                        return new(tree.Assignment)(key, value);
771                    }
772                },
773
774                //
775                // Parse url() tokens
776                //
777                // We use a specific rule for urls, because they don't really behave like
778                // standard function calls. The difference is that the argument doesn't have
779                // to be enclosed within a string, so it can't be parsed as an Expression.
780                //
781                url: function () {
782                    var value;
783
784                    if (input.charAt(i) !== 'u' || !$(/^url\(/)) return;
785                    value = $(this.entities.quoted)  || $(this.entities.variable) ||
786                            $(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || "";
787
788                    expect(')');
789
790                    return new(tree.URL)((value.value != null || value instanceof tree.Variable)
791                                        ? value : new(tree.Anonymous)(value), imports.paths);
792                },
793
794                //
795                // A Variable entity, such as `@fink`, in
796                //
797                //     width: @fink + 2px
798                //
799                // We use a different parser for variable definitions,
800                // see `parsers.variable`.
801                //
802                variable: function () {
803                    var name, index = i;
804
805                    if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) {
806                        return new(tree.Variable)(name, index, env.filename);
807                    }
808                },
809
810                // A variable entity useing the protective {} e.g. @{var}
811                variableCurly: function () {
812                    var name, curly, index = i;
813
814                    if (input.charAt(i) === '@' && (curly = $(/^@\{([\w-]+)\}/))) {
815                        return new(tree.Variable)("@" + curly[1], index, env.filename);
816                    }
817                },
818
819                //
820                // A Hexadecimal color
821                //
822                //     #4F3C2F
823                //
824                // `rgb` and `hsl` colors are parsed through the `entities.call` parser.
825                //
826                color: function () {
827                    var rgb;
828
829                    if (input.charAt(i) === '#' && (rgb = $(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) {
830                        return new(tree.Color)(rgb[1]);
831                    }
832                },
833
834                //
835                // A Dimension, that is, a number and a unit
836                //
837                //     0.5em 95%
838                //
839                dimension: function () {
840                    var value, c = input.charCodeAt(i);
841                    if ((c > 57 || c < 45) || c === 47) return;
842
843                    if (value = $(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn|dpi|dpcm|dppx|rem|vw|vh|vmin|vm|ch)?/)) {
844                        return new(tree.Dimension)(value[1], value[2]);
845                    }
846                },
847
848                //
849                // A Ratio
850                //
851                //    16/9
852                //
853                ratio: function () {
854                  var value, c = input.charCodeAt(i);
855                  if (c > 57 || c < 48) return;
856
857                  if (value = $(/^(\d+\/\d+)/)) {
858                    return new(tree.Ratio)(value[1]);
859                  }
860                },
861
862                //
863                // JavaScript code to be evaluated
864                //
865                //     `window.location.href`
866                //
867                javascript: function () {
868                    var str, j = i, e;
869
870                    if (input.charAt(j) === '~') { j++, e = true } // Escaped strings
871                    if (input.charAt(j) !== '`') { return }
872
873                    e && $('~');
874
875                    if (str = $(/^`([^`]*)`/)) {
876                        return new(tree.JavaScript)(str[1], i, e);
877                    }
878                }
879            },
880
881            //
882            // The variable part of a variable definition. Used in the `rule` parser
883            //
884            //     @fink:
885            //
886            variable: function () {
887                var name;
888
889                if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] }
890            },
891
892            //
893            // A font size/line-height shorthand
894            //
895            //     small/12px
896            //
897            // We need to peek first, or we'll match on keywords and dimensions
898            //
899            shorthand: function () {
900                var a, b;
901
902                if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return;
903
904                save();
905
906                if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) {
907                    return new(tree.Shorthand)(a, b);
908                }
909
910                restore();
911            },
912
913            //
914            // Mixins
915            //
916            mixin: {
917                //
918                // A Mixin call, with an optional argument list
919                //
920                //     #mixins > .square(#fff);
921                //     .rounded(4px, black);
922                //     .button;
923                //
924                // The `while` loop is there because mixins can be
925                // namespaced, but we only support the child and descendant
926                // selector for now.
927                //
928                call: function () {
929                    var elements = [], e, c, args = [], arg, index = i, s = input.charAt(i), name, value, important = false;
930
931                    if (s !== '.' && s !== '#') { return }
932                   
933                    save(); // stop us absorbing part of an invalid selector
934
935                    while (e = $(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/)) {
936                        elements.push(new(tree.Element)(c, e, i));
937                        c = $('>');
938                    }
939                    if ($('(')) {
940                        while (arg = $(this.expression)) {
941                            value = arg;
942                            name = null;
943
944                            // Variable
945                            if (arg.value.length == 1) {
946                                var val = arg.value[0];
947                                if (val instanceof tree.Variable) {
948                                    if ($(':')) {
949                                        if (value = $(this.expression)) {
950                                            name = val.name;
951                                        } else {
952                                            throw new(Error)("Expected value");
953                                        }
954                                    }
955                                }
956                            }
957
958                            args.push({ name: name, value: value });
959
960                            if (! $(',')) { break }
961                        }
962                        if (! $(')')) throw new(Error)("Expected )");
963                    }
964
965                    if ($(this.important)) {
966                        important = true;
967                    }
968
969                    if (elements.length > 0 && ($(';') || peek('}'))) {
970                        return new(tree.mixin.Call)(elements, args, index, env.filename, important);
971                    }
972                   
973                    restore();
974                },
975
976                //
977                // A Mixin definition, with a list of parameters
978                //
979                //     .rounded (@radius: 2px, @color) {
980                //        ...
981                //     }
982                //
983                // Until we have a finer grained state-machine, we have to
984                // do a look-ahead, to make sure we don't have a mixin call.
985                // See the `rule` function for more information.
986                //
987                // We start by matching `.rounded (`, and then proceed on to
988                // the argument list, which has optional default values.
989                // We store the parameters in `params`, with a `value` key,
990                // if there is a value, such as in the case of `@radius`.
991                //
992                // Once we've got our params list, and a closing `)`, we parse
993                // the `{...}` block.
994                //
995                definition: function () {
996                    var name, params = [], match, ruleset, param, value, cond, variadic = false;
997                    if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') ||
998                        peek(/^[^{]*(;|})/)) return;
999
1000                    save();
1001
1002                    if (match = $(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/)) {
1003                        name = match[1];
1004
1005                        do {
1006                            if (input.charAt(i) === '.' && $(/^\.{3}/)) {
1007                                variadic = true;
1008                                break;
1009                            } else if (param = $(this.entities.variable) || $(this.entities.literal)
1010                                                                         || $(this.entities.keyword)) {
1011                                // Variable
1012                                if (param instanceof tree.Variable) {
1013                                    if ($(':')) {
1014                                        value = expect(this.expression, 'expected expression');
1015                                        params.push({ name: param.name, value: value });
1016                                    } else if ($(/^\.{3}/)) {
1017                                        params.push({ name: param.name, variadic: true });
1018                                        variadic = true;
1019                                        break;
1020                                    } else {
1021                                        params.push({ name: param.name });
1022                                    }
1023                                } else {
1024                                    params.push({ value: param });
1025                                }
1026                            } else {
1027                                break;
1028                            }
1029                        } while ($(','))
1030
1031                        // .mixincall("@{a}");
1032                        // looks a bit like a mixin definition.. so we have to be nice and restore
1033                        if (!$(')')) {
1034                            furthest = i;
1035                            restore();
1036                        }
1037
1038                        if ($(/^when/)) { // Guard
1039                            cond = expect(this.conditions, 'expected condition');
1040                        }
1041
1042                        ruleset = $(this.block);
1043
1044                        if (ruleset) {
1045                            return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic);
1046                        } else {
1047                            restore();
1048                        }
1049                    }
1050                }
1051            },
1052
1053            //
1054            // Entities are the smallest recognized token,
1055            // and can be found inside a rule's value.
1056            //
1057            entity: function () {
1058                return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) ||
1059                       $(this.entities.call)    || $(this.entities.keyword)  || $(this.entities.javascript) ||
1060                       $(this.comment);
1061            },
1062
1063            //
1064            // A Rule terminator. Note that we use `peek()` to check for '}',
1065            // because the `block` rule will be expecting it, but we still need to make sure
1066            // it's there, if ';' was ommitted.
1067            //
1068            end: function () {
1069                return $(';') || peek('}');
1070            },
1071
1072            //
1073            // IE's alpha function
1074            //
1075            //     alpha(opacity=88)
1076            //
1077            alpha: function () {
1078                var value;
1079
1080                if (! $(/^\(opacity=/i)) return;
1081                if (value = $(/^\d+/) || $(this.entities.variable)) {
1082                    expect(')');
1083                    return new(tree.Alpha)(value);
1084                }
1085            },
1086
1087            //
1088            // A Selector Element
1089            //
1090            //     div
1091            //     + h1
1092            //     #socks
1093            //     input[type="text"]
1094            //
1095            // Elements are the building blocks for Selectors,
1096            // they are made out of a `Combinator` (see combinator rule),
1097            // and an element name, such as a tag a class, or `*`.
1098            //
1099            element: function () {
1100                var e, t, c, v;
1101
1102                c = $(this.combinator);
1103
1104                e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) ||
1105                    $('*') || $('&') || $(this.attribute) || $(/^\([^)@]+\)/) || $(/^[\.#](?=@)/) || $(this.entities.variableCurly);
1106
1107                if (! e) {
1108                    if ($('(') && (v = ($(this.entities.variableCurly) || $(this.entities.variable))) && $(')')) {
1109                        e = new(tree.Paren)(v);
1110                    }
1111                }
1112
1113                if (e) { return new(tree.Element)(c, e, i) }
1114            },
1115
1116            //
1117            // Combinators combine elements together, in a Selector.
1118            //
1119            // Because our parser isn't white-space sensitive, special care
1120            // has to be taken, when parsing the descendant combinator, ` `,
1121            // as it's an empty space. We have to check the previous character
1122            // in the input, to see if it's a ` ` character. More info on how
1123            // we deal with this in *combinator.js*.
1124            //
1125            combinator: function () {
1126                var match, c = input.charAt(i);
1127
1128                if (c === '>' || c === '+' || c === '~') {
1129                    i++;
1130                    while (input.charAt(i).match(/\s/)) { i++ }
1131                    return new(tree.Combinator)(c);
1132                } else if (input.charAt(i - 1).match(/\s/)) {
1133                    return new(tree.Combinator)(" ");
1134                } else {
1135                    return new(tree.Combinator)(null);
1136                }
1137            },
1138
1139            //
1140            // A CSS Selector
1141            //
1142            //     .class > div + h1
1143            //     li a:hover
1144            //
1145            // Selectors are made out of one or more Elements, see above.
1146            //
1147            selector: function () {
1148                var sel, e, elements = [], c, match;
1149
1150                // depreciated, will be removed soon
1151                if ($('(')) {
1152                    sel = $(this.entity);
1153                    expect(')');
1154                    return new(tree.Selector)([new(tree.Element)('', sel, i)]);
1155                }
1156
1157                while (e = $(this.element)) {
1158                    c = input.charAt(i);
1159                    elements.push(e)
1160                    if (c === '{' || c === '}' || c === ';' || c === ',') { break }
1161                }
1162
1163                if (elements.length > 0) { return new(tree.Selector)(elements) }
1164            },
1165            tag: function () {
1166                return $(/^[A-Za-z][A-Za-z-]*[0-9]?/) || $('*');
1167            },
1168            attribute: function () {
1169                var attr = '', key, val, op;
1170
1171                if (! $('[')) return;
1172
1173                if (key = $(/^(?:[_A-Za-z0-9-]|\\.)+/) || $(this.entities.quoted)) {
1174                    if ((op = $(/^[|~*$^]?=/)) &&
1175                        (val = $(this.entities.quoted) || $(/^[\w-]+/))) {
1176                        attr = [key, op, val.toCSS ? val.toCSS() : val].join('');
1177                    } else { attr = key }
1178                }
1179
1180                if (! $(']')) return;
1181
1182                if (attr) { return "[" + attr + "]" }
1183            },
1184
1185            //
1186            // The `block` rule is used by `ruleset` and `mixin.definition`.
1187            // It's a wrapper around the `primary` rule, with added `{}`.
1188            //
1189            block: function () {
1190                var content;
1191                if ($('{') && (content = $(this.primary)) && $('}')) {
1192                    return content;
1193                }
1194            },
1195
1196            //
1197            // div, .class, body > p {...}
1198            //
1199            ruleset: function () {
1200                var selectors = [], s, rules, match, debugInfo;
1201                save();
1202
1203                if (env.dumpLineNumbers)
1204                    debugInfo = getDebugInfo(i, input, env);
1205
1206                while (s = $(this.selector)) {
1207                    selectors.push(s);
1208                    $(this.comment);
1209                    if (! $(',')) { break }
1210                    $(this.comment);
1211                }
1212
1213                if (selectors.length > 0 && (rules = $(this.block))) {
1214                    var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports);
1215                    if (env.dumpLineNumbers)
1216                        ruleset.debugInfo = debugInfo;
1217                    return ruleset;
1218                } else {
1219                    // Backtrack
1220                    furthest = i;
1221                    restore();
1222                }
1223            },
1224            rule: function () {
1225                var name, value, c = input.charAt(i), important, match;
1226                save();
1227
1228                if (c === '.' || c === '#' || c === '&') { return }
1229
1230                if (name = $(this.variable) || $(this.property)) {
1231                    if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) {
1232                        i += match[0].length - 1;
1233                        value = new(tree.Anonymous)(match[1]);
1234                    } else if (name === "font") {
1235                        value = $(this.font);
1236                    } else {
1237                        value = $(this.value);
1238                    }
1239                    important = $(this.important);
1240
1241                    if (value && $(this.end)) {
1242                        return new(tree.Rule)(name, value, important, memo);
1243                    } else {
1244                        furthest = i;
1245                        restore();
1246                    }
1247                }
1248            },
1249
1250            //
1251            // An @import directive
1252            //
1253            //     @import "lib";
1254            //
1255            // Depending on our environemnt, importing is done differently:
1256            // In the browser, it's an XHR request, in Node, it would be a
1257            // file-system operation. The function used for importing is
1258            // stored in `import`, which we pass to the Import constructor.
1259            //
1260            "import": function () {
1261                var path, features, index = i;
1262               
1263                save();
1264               
1265                var dir = $(/^@import(?:-(once))?\s+/);
1266
1267                if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) {
1268                    features = $(this.mediaFeatures);
1269                    if ($(';')) {
1270                        return new(tree.Import)(path, imports, features, (dir[1] === 'once'), index);
1271                    }
1272                }
1273               
1274                restore();
1275            },
1276
1277            mediaFeature: function () {
1278                var e, p, nodes = [];
1279
1280                do {
1281                    if (e = $(this.entities.keyword)) {
1282                        nodes.push(e);
1283                    } else if ($('(')) {
1284                        p = $(this.property);
1285                        e = $(this.entity);
1286                        if ($(')')) {
1287                            if (p && e) {
1288                                nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, true)));
1289                            } else if (e) {
1290                                nodes.push(new(tree.Paren)(e));
1291                            } else {
1292                                return null;
1293                            }
1294                        } else { return null }
1295                    }
1296                } while (e);
1297
1298                if (nodes.length > 0) {
1299                    return new(tree.Expression)(nodes);
1300                }
1301            },
1302
1303            mediaFeatures: function () {
1304                var e, features = [];
1305
1306                do {
1307                  if (e = $(this.mediaFeature)) {
1308                      features.push(e);
1309                      if (! $(',')) { break }
1310                  } else if (e = $(this.entities.variable)) {
1311                      features.push(e);
1312                      if (! $(',')) { break }
1313                  }
1314                } while (e);
1315
1316                return features.length > 0 ? features : null;
1317            },
1318
1319            media: function () {
1320                var features, rules, media, debugInfo;
1321
1322                if (env.dumpLineNumbers)
1323                    debugInfo = getDebugInfo(i, input, env);
1324
1325                if ($(/^@media/)) {
1326                    features = $(this.mediaFeatures);
1327
1328                    if (rules = $(this.block)) {
1329                        media = new(tree.Media)(rules, features);
1330                        if(env.dumpLineNumbers)
1331                            media.debugInfo = debugInfo;
1332                        return media;
1333                    }
1334                }
1335            },
1336
1337            //
1338            // A CSS Directive
1339            //
1340            //     @charset "utf-8";
1341            //
1342            directive: function () {
1343                var name, value, rules, identifier, e, nodes, nonVendorSpecificName,
1344                    hasBlock, hasIdentifier;
1345
1346                if (input.charAt(i) !== '@') return;
1347
1348                if (value = $(this['import']) || $(this.media)) {
1349                    return value;
1350                }
1351               
1352                save();
1353
1354                name = $(/^@[a-z-]+/);
1355
1356                nonVendorSpecificName = name;
1357                if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) {
1358                    nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1);
1359                }
1360
1361                switch(nonVendorSpecificName) {
1362                    case "@font-face":
1363                        hasBlock = true;
1364                        break;
1365                    case "@viewport":
1366                    case "@top-left":
1367                    case "@top-left-corner":
1368                    case "@top-center":
1369                    case "@top-right":
1370                    case "@top-right-corner":
1371                    case "@bottom-left":
1372                    case "@bottom-left-corner":
1373                    case "@bottom-center":
1374                    case "@bottom-right":
1375                    case "@bottom-right-corner":
1376                    case "@left-top":
1377                    case "@left-middle":
1378                    case "@left-bottom":
1379                    case "@right-top":
1380                    case "@right-middle":
1381                    case "@right-bottom":
1382                        hasBlock = true;
1383                        break;
1384                    case "@page":
1385                    case "@document":
1386                    case "@supports":
1387                    case "@keyframes":
1388                        hasBlock = true;
1389                        hasIdentifier = true;
1390                        break;
1391                }
1392
1393                if (hasIdentifier) {
1394                    name += " " + ($(/^[^{]+/) || '').trim();
1395                }
1396
1397                if (hasBlock)
1398                {
1399                    if (rules = $(this.block)) {
1400                        return new(tree.Directive)(name, rules);
1401                    }
1402                } else {
1403                    if ((value = $(this.entity)) && $(';')) {
1404                        return new(tree.Directive)(name, value);
1405                    }
1406                }
1407               
1408                restore();
1409            },
1410            font: function () {
1411                var value = [], expression = [], weight, shorthand, font, e;
1412
1413                while (e = $(this.shorthand) || $(this.entity)) {
1414                    expression.push(e);
1415                }
1416                value.push(new(tree.Expression)(expression));
1417
1418                if ($(',')) {
1419                    while (e = $(this.expression)) {
1420                        value.push(e);
1421                        if (! $(',')) { break }
1422                    }
1423                }
1424                return new(tree.Value)(value);
1425            },
1426
1427            //
1428            // A Value is a comma-delimited list of Expressions
1429            //
1430            //     font-family: Baskerville, Georgia, serif;
1431            //
1432            // In a Rule, a Value represents everything after the `:`,
1433            // and before the `;`.
1434            //
1435            value: function () {
1436                var e, expressions = [], important;
1437
1438                while (e = $(this.expression)) {
1439                    expressions.push(e);
1440                    if (! $(',')) { break }
1441                }
1442
1443                if (expressions.length > 0) {
1444                    return new(tree.Value)(expressions);
1445                }
1446            },
1447            important: function () {
1448                if (input.charAt(i) === '!') {
1449                    return $(/^! *important/);
1450                }
1451            },
1452            sub: function () {
1453                var e;
1454
1455                if ($('(') && (e = $(this.expression)) && $(')')) {
1456                    return e;
1457                }
1458            },
1459            multiplication: function () {
1460                var m, a, op, operation;
1461                if (m = $(this.operand)) {
1462                    while (!peek(/^\/\*/) && (op = ($('/') || $('*'))) && (a = $(this.operand))) {
1463                        operation = new(tree.Operation)(op, [operation || m, a]);
1464                    }
1465                    return operation || m;
1466                }
1467            },
1468            addition: function () {
1469                var m, a, op, operation;
1470                if (m = $(this.multiplication)) {
1471                    while ((op = $(/^[-+]\s+/) || (!isWhitespace(input.charAt(i - 1)) && ($('+') || $('-')))) &&
1472                           (a = $(this.multiplication))) {
1473                        operation = new(tree.Operation)(op, [operation || m, a]);
1474                    }
1475                    return operation || m;
1476                }
1477            },
1478            conditions: function () {
1479                var a, b, index = i, condition;
1480
1481                if (a = $(this.condition)) {
1482                    while ($(',') && (b = $(this.condition))) {
1483                        condition = new(tree.Condition)('or', condition || a, b, index);
1484                    }
1485                    return condition || a;
1486                }
1487            },
1488            condition: function () {
1489                var a, b, c, op, index = i, negate = false;
1490
1491                if ($(/^not/)) { negate = true }
1492                expect('(');
1493                if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) {
1494                    if (op = $(/^(?:>=|=<|[<=>])/)) {
1495                        if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) {
1496                            c = new(tree.Condition)(op, a, b, index, negate);
1497                        } else {
1498                            error('expected expression');
1499                        }
1500                    } else {
1501                        c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate);
1502                    }
1503                    expect(')');
1504                    return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c;
1505                }
1506            },
1507
1508            //
1509            // An operand is anything that can be part of an operation,
1510            // such as a Color, or a Variable
1511            //
1512            operand: function () {
1513                var negate, p = input.charAt(i + 1);
1514
1515                if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') }
1516                var o = $(this.sub) || $(this.entities.dimension) ||
1517                        $(this.entities.color) || $(this.entities.variable) ||
1518                        $(this.entities.call);
1519                return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o])
1520                              : o;
1521            },
1522
1523            //
1524            // Expressions either represent mathematical operations,
1525            // or white-space delimited Entities.
1526            //
1527            //     1px solid black
1528            //     @var * 2
1529            //
1530            expression: function () {
1531                var e, delim, entities = [], d;
1532
1533                while (e = $(this.addition) || $(this.entity)) {
1534                    entities.push(e);
1535                }
1536                if (entities.length > 0) {
1537                    return new(tree.Expression)(entities);
1538                }
1539            },
1540            property: function () {
1541                var name;
1542
1543                if (name = $(/^(\*?-?[_a-z0-9-]+)\s*:/)) {
1544                    return name[1];
1545                }
1546            }
1547        }
1548    };
1549};
1550
1551if (less.mode === 'browser' || less.mode === 'rhino') {
1552    //
1553    // Used by `@import` directives
1554    //
1555    less.Parser.importer = function (path, paths, callback, env) {
1556        if (!/^([a-z-]+:)?\//.test(path) && paths.length > 0) {
1557            path = paths[0] + path;
1558        }
1559        // We pass `true` as 3rd argument, to force the reload of the import.
1560        // This is so we can get the syntax tree as opposed to just the CSS output,
1561        // as we need this to evaluate the current stylesheet.
1562        // __ Now using the hack of passing a ref to top parser's content cache in the 1st arg. __
1563        loadStyleSheet({ href: path, title: path, type: env.mime, contents: env.contents }, function (e) {
1564            if (e && typeof(env.errback) === "function") {
1565                env.errback.call(null, path, paths, callback, env);
1566            } else {
1567                callback.apply(null, arguments);
1568            }
1569        }, true);
1570    };
1571}
1572
1573(function (tree) {
1574
1575tree.functions = {
1576    rgb: function (r, g, b) {
1577        return this.rgba(r, g, b, 1.0);
1578    },
1579    rgba: function (r, g, b, a) {
1580        var rgb = [r, g, b].map(function (c) { return number(c) }),
1581            a = number(a);
1582        return new(tree.Color)(rgb, a);
1583    },
1584    hsl: function (h, s, l) {
1585        return this.hsla(h, s, l, 1.0);
1586    },
1587    hsla: function (h, s, l, a) {
1588        h = (number(h) % 360) / 360;
1589        s = number(s); l = number(l); a = number(a);
1590
1591        var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
1592        var m1 = l * 2 - m2;
1593
1594        return this.rgba(hue(h + 1/3) * 255,
1595                         hue(h)       * 255,
1596                         hue(h - 1/3) * 255,
1597                         a);
1598
1599        function hue(h) {
1600            h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
1601            if      (h * 6 < 1) return m1 + (m2 - m1) * h * 6;
1602            else if (h * 2 < 1) return m2;
1603            else if (h * 3 < 2) return m1 + (m2 - m1) * (2/3 - h) * 6;
1604            else                return m1;
1605        }
1606    },
1607    hue: function (color) {
1608        return new(tree.Dimension)(Math.round(color.toHSL().h));
1609    },
1610    saturation: function (color) {
1611        return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%');
1612    },
1613    lightness: function (color) {
1614        return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%');
1615    },
1616    red: function (color) {
1617        return new(tree.Dimension)(color.rgb[0]);
1618    },
1619    green: function (color) {
1620        return new(tree.Dimension)(color.rgb[1]);
1621    },
1622    blue: function (color) {
1623        return new(tree.Dimension)(color.rgb[2]);
1624    },
1625    alpha: function (color) {
1626        return new(tree.Dimension)(color.toHSL().a);
1627    },
1628    luma: function (color) {
1629        return new(tree.Dimension)(Math.round((0.2126 * (color.rgb[0]/255) +
1630            0.7152 * (color.rgb[1]/255) +
1631            0.0722 * (color.rgb[2]/255))
1632            * color.alpha * 100), '%');
1633    },
1634    saturate: function (color, amount) {
1635        var hsl = color.toHSL();
1636
1637        hsl.s += amount.value / 100;
1638        hsl.s = clamp(hsl.s);
1639        return hsla(hsl);
1640    },
1641    desaturate: function (color, amount) {
1642        var hsl = color.toHSL();
1643
1644        hsl.s -= amount.value / 100;
1645        hsl.s = clamp(hsl.s);
1646        return hsla(hsl);
1647    },
1648    lighten: function (color, amount) {
1649        var hsl = color.toHSL();
1650
1651        hsl.l += amount.value / 100;
1652        hsl.l = clamp(hsl.l);
1653        return hsla(hsl);
1654    },
1655    darken: function (color, amount) {
1656        var hsl = color.toHSL();
1657
1658        hsl.l -= amount.value / 100;
1659        hsl.l = clamp(hsl.l);
1660        return hsla(hsl);
1661    },
1662    fadein: function (color, amount) {
1663        var hsl = color.toHSL();
1664
1665        hsl.a += amount.value / 100;
1666        hsl.a = clamp(hsl.a);
1667        return hsla(hsl);
1668    },
1669    fadeout: function (color, amount) {
1670        var hsl = color.toHSL();
1671
1672        hsl.a -= amount.value / 100;
1673        hsl.a = clamp(hsl.a);
1674        return hsla(hsl);
1675    },
1676    fade: function (color, amount) {
1677        var hsl = color.toHSL();
1678
1679        hsl.a = amount.value / 100;
1680        hsl.a = clamp(hsl.a);
1681        return hsla(hsl);
1682    },
1683    spin: function (color, amount) {
1684        var hsl = color.toHSL();
1685        var hue = (hsl.h + amount.value) % 360;
1686
1687        hsl.h = hue < 0 ? 360 + hue : hue;
1688
1689        return hsla(hsl);
1690    },
1691    //
1692    // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
1693    // http://sass-lang.com
1694    //
1695    mix: function (color1, color2, weight) {
1696        if (!weight) {
1697            weight = new(tree.Dimension)(50);
1698        }
1699        var p = weight.value / 100.0;
1700        var w = p * 2 - 1;
1701        var a = color1.toHSL().a - color2.toHSL().a;
1702
1703        var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
1704        var w2 = 1 - w1;
1705
1706        var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2,
1707                   color1.rgb[1] * w1 + color2.rgb[1] * w2,
1708                   color1.rgb[2] * w1 + color2.rgb[2] * w2];
1709
1710        var alpha = color1.alpha * p + color2.alpha * (1 - p);
1711
1712        return new(tree.Color)(rgb, alpha);
1713    },
1714    greyscale: function (color) {
1715        return this.desaturate(color, new(tree.Dimension)(100));
1716    },
1717    contrast: function (color, dark, light, threshold) {
1718        if (typeof light === 'undefined') {
1719            light = this.rgba(255, 255, 255, 1.0);
1720        }
1721        if (typeof dark === 'undefined') {
1722            dark = this.rgba(0, 0, 0, 1.0);
1723        }
1724        if (typeof threshold === 'undefined') {
1725            threshold = 0.43;
1726        } else {
1727            threshold = threshold.value;
1728        }
1729        if (((0.2126 * (color.rgb[0]/255) + 0.7152 * (color.rgb[1]/255) + 0.0722 * (color.rgb[2]/255)) * color.alpha) < threshold) {
1730            return light;
1731        } else {
1732            return dark;
1733        }
1734    },
1735    e: function (str) {
1736        return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str);
1737    },
1738    escape: function (str) {
1739        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"));
1740    },
1741    '%': function (quoted /* arg, arg, ...*/) {
1742        var args = Array.prototype.slice.call(arguments, 1),
1743            str = quoted.value;
1744
1745        for (var i = 0; i < args.length; i++) {
1746            str = str.replace(/%[sda]/i, function(token) {
1747                var value = token.match(/s/i) ? args[i].value : args[i].toCSS();
1748                return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value;
1749            });
1750        }
1751        str = str.replace(/%%/g, '%');
1752        return new(tree.Quoted)('"' + str + '"', str);
1753    },
1754    round: function (n, f) {
1755        var fraction = typeof(f) === "undefined" ? 0 : f.value;
1756        if (n instanceof tree.Dimension) {
1757            return new(tree.Dimension)(number(n).toFixed(fraction), n.unit);
1758        } else if (typeof(n) === 'number') {
1759            return n.toFixed(fraction);
1760        } else {
1761            throw { type: "Argument", message: "argument must be a number" };
1762        }
1763    },
1764    ceil: function (n) {
1765        return this._math('ceil', n);
1766    },
1767    floor: function (n) {
1768        return this._math('floor', n);
1769    },
1770    _math: function (fn, n) {
1771        if (n instanceof tree.Dimension) {
1772            return new(tree.Dimension)(Math[fn](number(n)), n.unit);
1773        } else if (typeof(n) === 'number') {
1774            return Math[fn](n);
1775        } else {
1776            throw { type: "Argument", message: "argument must be a number" };
1777        }
1778    },
1779    argb: function (color) {
1780        return new(tree.Anonymous)(color.toARGB());
1781
1782    },
1783    percentage: function (n) {
1784        return new(tree.Dimension)(n.value * 100, '%');
1785    },
1786    color: function (n) {
1787        if (n instanceof tree.Quoted) {
1788            return new(tree.Color)(n.value.slice(1));
1789        } else {
1790            throw { type: "Argument", message: "argument must be a string" };
1791        }
1792    },
1793    iscolor: function (n) {
1794        return this._isa(n, tree.Color);
1795    },
1796    isnumber: function (n) {
1797        return this._isa(n, tree.Dimension);
1798    },
1799    isstring: function (n) {
1800        return this._isa(n, tree.Quoted);
1801    },
1802    iskeyword: function (n) {
1803        return this._isa(n, tree.Keyword);
1804    },
1805    isurl: function (n) {
1806        return this._isa(n, tree.URL);
1807    },
1808    ispixel: function (n) {
1809        return (n instanceof tree.Dimension) && n.unit === 'px' ? tree.True : tree.False;
1810    },
1811    ispercentage: function (n) {
1812        return (n instanceof tree.Dimension) && n.unit === '%' ? tree.True : tree.False;
1813    },
1814    isem: function (n) {
1815        return (n instanceof tree.Dimension) && n.unit === 'em' ? tree.True : tree.False;
1816    },
1817    _isa: function (n, Type) {
1818        return (n instanceof Type) ? tree.True : tree.False;
1819    },
1820   
1821    /* Blending modes */
1822   
1823    multiply: function(color1, color2) {
1824        var r = color1.rgb[0] * color2.rgb[0] / 255;
1825        var g = color1.rgb[1] * color2.rgb[1] / 255;
1826        var b = color1.rgb[2] * color2.rgb[2] / 255;
1827        return this.rgb(r, g, b);
1828    },
1829    screen: function(color1, color2) {
1830        var r = 255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255;
1831        var g = 255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255;
1832        var b = 255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255;
1833        return this.rgb(r, g, b);
1834    },
1835    overlay: function(color1, color2) {
1836        var r = color1.rgb[0] < 128 ? 2 * color1.rgb[0] * color2.rgb[0] / 255 : 255 - 2 * (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255;
1837        var g = color1.rgb[1] < 128 ? 2 * color1.rgb[1] * color2.rgb[1] / 255 : 255 - 2 * (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255;
1838        var b = color1.rgb[2] < 128 ? 2 * color1.rgb[2] * color2.rgb[2] / 255 : 255 - 2 * (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255;
1839        return this.rgb(r, g, b);
1840    },
1841    softlight: function(color1, color2) {
1842        var t = color2.rgb[0] * color1.rgb[0] / 255;
1843        var r = t + color1.rgb[0] * (255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255 - t) / 255;
1844        t = color2.rgb[1] * color1.rgb[1] / 255;
1845        var g = t + color1.rgb[1] * (255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255 - t) / 255;
1846        t = color2.rgb[2] * color1.rgb[2] / 255;
1847        var b = t + color1.rgb[2] * (255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255 - t) / 255;
1848        return this.rgb(r, g, b);
1849    },
1850    hardlight: function(color1, color2) {
1851        var r = color2.rgb[0] < 128 ? 2 * color2.rgb[0] * color1.rgb[0] / 255 : 255 - 2 * (255 - color2.rgb[0]) * (255 - color1.rgb[0]) / 255;
1852        var g = color2.rgb[1] < 128 ? 2 * color2.rgb[1] * color1.rgb[1] / 255 : 255 - 2 * (255 - color2.rgb[1]) * (255 - color1.rgb[1]) / 255;
1853        var b = color2.rgb[2] < 128 ? 2 * color2.rgb[2] * color1.rgb[2] / 255 : 255 - 2 * (255 - color2.rgb[2]) * (255 - color1.rgb[2]) / 255;
1854        return this.rgb(r, g, b);
1855    },
1856    difference: function(color1, color2) {
1857        var r = Math.abs(color1.rgb[0] - color2.rgb[0]);
1858        var g = Math.abs(color1.rgb[1] - color2.rgb[1]);
1859        var b = Math.abs(color1.rgb[2] - color2.rgb[2]);
1860        return this.rgb(r, g, b);
1861    },
1862    exclusion: function(color1, color2) {
1863        var r = color1.rgb[0] + color2.rgb[0] * (255 - color1.rgb[0] - color1.rgb[0]) / 255;
1864        var g = color1.rgb[1] + color2.rgb[1] * (255 - color1.rgb[1] - color1.rgb[1]) / 255;
1865        var b = color1.rgb[2] + color2.rgb[2] * (255 - color1.rgb[2] - color1.rgb[2]) / 255;
1866        return this.rgb(r, g, b);
1867    },
1868    average: function(color1, color2) {
1869        var r = (color1.rgb[0] + color2.rgb[0]) / 2;
1870        var g = (color1.rgb[1] + color2.rgb[1]) / 2;
1871        var b = (color1.rgb[2] + color2.rgb[2]) / 2;
1872        return this.rgb(r, g, b);
1873    },
1874    negation: function(color1, color2) {
1875        var r = 255 - Math.abs(255 - color2.rgb[0] - color1.rgb[0]);
1876        var g = 255 - Math.abs(255 - color2.rgb[1] - color1.rgb[1]);
1877        var b = 255 - Math.abs(255 - color2.rgb[2] - color1.rgb[2]);
1878        return this.rgb(r, g, b);
1879    },
1880    tint: function(color, amount) {
1881        return this.mix(this.rgb(255,255,255), color, amount);
1882    },
1883    shade: function(color, amount) {
1884        return this.mix(this.rgb(0, 0, 0), color, amount);
1885    }
1886};
1887
1888function hsla(hsla) {
1889    return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a);
1890}
1891
1892function number(n) {
1893    if (n instanceof tree.Dimension) {
1894        return parseFloat(n.unit == '%' ? n.value / 100 : n.value);
1895    } else if (typeof(n) === 'number') {
1896        return n;
1897    } else {
1898        throw {
1899            error: "RuntimeError",
1900            message: "color functions take numbers as parameters"
1901        };
1902    }
1903}
1904
1905function clamp(val) {
1906    return Math.min(1, Math.max(0, val));
1907}
1908
1909})(require('./tree'));
1910(function (tree) {
1911    tree.colors = {
1912        'aliceblue':'#f0f8ff',
1913        'antiquewhite':'#faebd7',
1914        'aqua':'#00ffff',
1915        'aquamarine':'#7fffd4',
1916        'azure':'#f0ffff',
1917        'beige':'#f5f5dc',
1918        'bisque':'#ffe4c4',
1919        'black':'#000000',
1920        'blanchedalmond':'#ffebcd',
1921        'blue':'#0000ff',
1922        'blueviolet':'#8a2be2',
1923        'brown':'#a52a2a',
1924        'burlywood':'#deb887',
1925        'cadetblue':'#5f9ea0',
1926        'chartreuse':'#7fff00',
1927        'chocolate':'#d2691e',
1928        'coral':'#ff7f50',
1929        'cornflowerblue':'#6495ed',
1930        'cornsilk':'#fff8dc',
1931        'crimson':'#dc143c',
1932        'cyan':'#00ffff',
1933        'darkblue':'#00008b',
1934        'darkcyan':'#008b8b',
1935        'darkgoldenrod':'#b8860b',
1936        'darkgray':'#a9a9a9',
1937        'darkgrey':'#a9a9a9',
1938        'darkgreen':'#006400',
1939        'darkkhaki':'#bdb76b',
1940        'darkmagenta':'#8b008b',
1941        'darkolivegreen':'#556b2f',
1942        'darkorange':'#ff8c00',
1943        'darkorchid':'#9932cc',
1944        'darkred':'#8b0000',
1945        'darksalmon':'#e9967a',
1946        'darkseagreen':'#8fbc8f',
1947        'darkslateblue':'#483d8b',
1948        'darkslategray':'#2f4f4f',
1949        'darkslategrey':'#2f4f4f',
1950        'darkturquoise':'#00ced1',
1951        'darkviolet':'#9400d3',
1952        'deeppink':'#ff1493',
1953        'deepskyblue':'#00bfff',
1954        'dimgray':'#696969',
1955        'dimgrey':'#696969',
1956        'dodgerblue':'#1e90ff',
1957        'firebrick':'#b22222',
1958        'floralwhite':'#fffaf0',
1959        'forestgreen':'#228b22',
1960        'fuchsia':'#ff00ff',
1961        'gainsboro':'#dcdcdc',
1962        'ghostwhite':'#f8f8ff',
1963        'gold':'#ffd700',
1964        'goldenrod':'#daa520',
1965        'gray':'#808080',
1966        'grey':'#808080',
1967        'green':'#008000',
1968        'greenyellow':'#adff2f',
1969        'honeydew':'#f0fff0',
1970        'hotpink':'#ff69b4',
1971        'indianred':'#cd5c5c',
1972        'indigo':'#4b0082',
1973        'ivory':'#fffff0',
1974        'khaki':'#f0e68c',
1975        'lavender':'#e6e6fa',
1976        'lavenderblush':'#fff0f5',
1977        'lawngreen':'#7cfc00',
1978        'lemonchiffon':'#fffacd',
1979        'lightblue':'#add8e6',
1980        'lightcoral':'#f08080',
1981        'lightcyan':'#e0ffff',
1982        'lightgoldenrodyellow':'#fafad2',
1983        'lightgray':'#d3d3d3',
1984        'lightgrey':'#d3d3d3',
1985        'lightgreen':'#90ee90',
1986        'lightpink':'#ffb6c1',
1987        'lightsalmon':'#ffa07a',
1988        'lightseagreen':'#20b2aa',
1989        'lightskyblue':'#87cefa',
1990        'lightslategray':'#778899',
1991        'lightslategrey':'#778899',
1992        'lightsteelblue':'#b0c4de',
1993        'lightyellow':'#ffffe0',
1994        'lime':'#00ff00',
1995        'limegreen':'#32cd32',
1996        'linen':'#faf0e6',
1997        'magenta':'#ff00ff',
1998        'maroon':'#800000',
1999        'mediumaquamarine':'#66cdaa',
2000        'mediumblue':'#0000cd',
2001        'mediumorchid':'#ba55d3',
2002        'mediumpurple':'#9370d8',
2003        'mediumseagreen':'#3cb371',
2004        'mediumslateblue':'#7b68ee',
2005        'mediumspringgreen':'#00fa9a',
2006        'mediumturquoise':'#48d1cc',
2007        'mediumvioletred':'#c71585',
2008        'midnightblue':'#191970',
2009        'mintcream':'#f5fffa',
2010        'mistyrose':'#ffe4e1',
2011        'moccasin':'#ffe4b5',
2012        'navajowhite':'#ffdead',
2013        'navy':'#000080',
2014        'oldlace':'#fdf5e6',
2015        'olive':'#808000',
2016        'olivedrab':'#6b8e23',
2017        'orange':'#ffa500',
2018        'orangered':'#ff4500',
2019        'orchid':'#da70d6',
2020        'palegoldenrod':'#eee8aa',
2021        'palegreen':'#98fb98',
2022        'paleturquoise':'#afeeee',
2023        'palevioletred':'#d87093',
2024        'papayawhip':'#ffefd5',
2025        'peachpuff':'#ffdab9',
2026        'peru':'#cd853f',
2027        'pink':'#ffc0cb',
2028        'plum':'#dda0dd',
2029        'powderblue':'#b0e0e6',
2030        'purple':'#800080',
2031        'red':'#ff0000',
2032        'rosybrown':'#bc8f8f',
2033        'royalblue':'#4169e1',
2034        'saddlebrown':'#8b4513',
2035        'salmon':'#fa8072',
2036        'sandybrown':'#f4a460',
2037        'seagreen':'#2e8b57',
2038        'seashell':'#fff5ee',
2039        'sienna':'#a0522d',
2040        'silver':'#c0c0c0',
2041        'skyblue':'#87ceeb',
2042        'slateblue':'#6a5acd',
2043        'slategray':'#708090',
2044        'slategrey':'#708090',
2045        'snow':'#fffafa',
2046        'springgreen':'#00ff7f',
2047        'steelblue':'#4682b4',
2048        'tan':'#d2b48c',
2049        'teal':'#008080',
2050        'thistle':'#d8bfd8',
2051        'tomato':'#ff6347',
2052        // 'transparent':'rgba(0,0,0,0)',
2053        'turquoise':'#40e0d0',
2054        'violet':'#ee82ee',
2055        'wheat':'#f5deb3',
2056        'white':'#ffffff',
2057        'whitesmoke':'#f5f5f5',
2058        'yellow':'#ffff00',
2059        'yellowgreen':'#9acd32'
2060    };
2061})(require('./tree'));
2062(function (tree) {
2063
2064tree.Alpha = function (val) {
2065    this.value = val;
2066};
2067tree.Alpha.prototype = {
2068    toCSS: function () {
2069        return "alpha(opacity=" +
2070               (this.value.toCSS ? this.value.toCSS() : this.value) + ")";
2071    },
2072    eval: function (env) {
2073        if (this.value.eval) { this.value = this.value.eval(env) }
2074        return this;
2075    }
2076};
2077
2078})(require('../tree'));
2079(function (tree) {
2080
2081tree.Anonymous = function (string) {
2082    this.value = string.value || string;
2083};
2084tree.Anonymous.prototype = {
2085    toCSS: function () {
2086        return this.value;
2087    },
2088    eval: function () { return this },
2089    compare: function (x) {
2090        if (!x.toCSS) {
2091            return -1;
2092        }
2093       
2094        var left = this.toCSS(),
2095            right = x.toCSS();
2096       
2097        if (left === right) {
2098            return 0;
2099        }
2100       
2101        return left < right ? -1 : 1;
2102    }
2103};
2104
2105})(require('../tree'));
2106(function (tree) {
2107
2108tree.Assignment = function (key, val) {
2109    this.key = key;
2110    this.value = val;
2111};
2112tree.Assignment.prototype = {
2113    toCSS: function () {
2114        return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value);
2115    },
2116    eval: function (env) {
2117        if (this.value.eval) {
2118            return new(tree.Assignment)(this.key, this.value.eval(env));
2119        }
2120        return this;
2121    }
2122};
2123
2124})(require('../tree'));(function (tree) {
2125
2126//
2127// A function call node.
2128//
2129tree.Call = function (name, args, index, filename) {
2130    this.name = name;
2131    this.args = args;
2132    this.index = index;
2133    this.filename = filename;
2134};
2135tree.Call.prototype = {
2136    //
2137    // When evaluating a function call,
2138    // we either find the function in `tree.functions` [1],
2139    // in which case we call it, passing the  evaluated arguments,
2140    // or we simply print it out as it appeared originally [2].
2141    //
2142    // The *functions.js* file contains the built-in functions.
2143    //
2144    // The reason why we evaluate the arguments, is in the case where
2145    // we try to pass a variable to a function, like: `saturate(@color)`.
2146    // The function should receive the value, not the variable.
2147    //
2148    eval: function (env) {
2149        var args = this.args.map(function (a) { return a.eval(env) });
2150
2151        if (this.name in tree.functions) { // 1.
2152            try {
2153                return tree.functions[this.name].apply(tree.functions, args);
2154            } catch (e) {
2155                throw { type: e.type || "Runtime",
2156                        message: "error evaluating function `" + this.name + "`" +
2157                                 (e.message ? ': ' + e.message : ''),
2158                        index: this.index, filename: this.filename };
2159            }
2160        } else { // 2.
2161            return new(tree.Anonymous)(this.name +
2162                   "(" + args.map(function (a) { return a.toCSS(env) }).join(', ') + ")");
2163        }
2164    },
2165
2166    toCSS: function (env) {
2167        return this.eval(env).toCSS();
2168    }
2169};
2170
2171})(require('../tree'));
2172(function (tree) {
2173//
2174// RGB Colors - #ff0014, #eee
2175//
2176tree.Color = function (rgb, a) {
2177    //
2178    // The end goal here, is to parse the arguments
2179    // into an integer triplet, such as `128, 255, 0`
2180    //
2181    // This facilitates operations and conversions.
2182    //
2183    if (Array.isArray(rgb)) {
2184        this.rgb = rgb;
2185    } else if (rgb.length == 6) {
2186        this.rgb = rgb.match(/.{2}/g).map(function (c) {
2187            return parseInt(c, 16);
2188        });
2189    } else {
2190        this.rgb = rgb.split('').map(function (c) {
2191            return parseInt(c + c, 16);
2192        });
2193    }
2194    this.alpha = typeof(a) === 'number' ? a : 1;
2195};
2196tree.Color.prototype = {
2197    eval: function () { return this },
2198
2199    //
2200    // If we have some transparency, the only way to represent it
2201    // is via `rgba`. Otherwise, we use the hex representation,
2202    // which has better compatibility with older browsers.
2203    // Values are capped between `0` and `255`, rounded and zero-padded.
2204    //
2205    toCSS: function () {
2206        if (this.alpha < 1.0) {
2207            return "rgba(" + this.rgb.map(function (c) {
2208                return Math.round(c);
2209            }).concat(this.alpha).join(', ') + ")";
2210        } else {
2211            return '#' + this.rgb.map(function (i) {
2212                i = Math.round(i);
2213                i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16);
2214                return i.length === 1 ? '0' + i : i;
2215            }).join('');
2216        }
2217    },
2218
2219    //
2220    // Operations have to be done per-channel, if not,
2221    // channels will spill onto each other. Once we have
2222    // our result, in the form of an integer triplet,
2223    // we create a new Color node to hold the result.
2224    //
2225    operate: function (op, other) {
2226        var result = [];
2227
2228        if (! (other instanceof tree.Color)) {
2229            other = other.toColor();
2230        }
2231
2232        for (var c = 0; c < 3; c++) {
2233            result[c] = tree.operate(op, this.rgb[c], other.rgb[c]);
2234        }
2235        return new(tree.Color)(result, this.alpha + other.alpha);
2236    },
2237
2238    toHSL: function () {
2239        var r = this.rgb[0] / 255,
2240            g = this.rgb[1] / 255,
2241            b = this.rgb[2] / 255,
2242            a = this.alpha;
2243
2244        var max = Math.max(r, g, b), min = Math.min(r, g, b);
2245        var h, s, l = (max + min) / 2, d = max - min;
2246
2247        if (max === min) {
2248            h = s = 0;
2249        } else {
2250            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
2251
2252            switch (max) {
2253                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
2254                case g: h = (b - r) / d + 2;               break;
2255                case b: h = (r - g) / d + 4;               break;
2256            }
2257            h /= 6;
2258        }
2259        return { h: h * 360, s: s, l: l, a: a };
2260    },
2261    toARGB: function () {
2262        var argb = [Math.round(this.alpha * 255)].concat(this.rgb);
2263        return '#' + argb.map(function (i) {
2264            i = Math.round(i);
2265            i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16);
2266            return i.length === 1 ? '0' + i : i;
2267        }).join('');
2268    },
2269    compare: function (x) {
2270        if (!x.rgb) {
2271            return -1;
2272        }
2273       
2274        return (x.rgb[0] === this.rgb[0] &&
2275            x.rgb[1] === this.rgb[1] &&
2276            x.rgb[2] === this.rgb[2] &&
2277            x.alpha === this.alpha) ? 0 : -1;
2278    }
2279};
2280
2281
2282})(require('../tree'));
2283(function (tree) {
2284
2285tree.Comment = function (value, silent) {
2286    this.value = value;
2287    this.silent = !!silent;
2288};
2289tree.Comment.prototype = {
2290    toCSS: function (env) {
2291        return env.compress ? '' : this.value;
2292    },
2293    eval: function () { return this }
2294};
2295
2296})(require('../tree'));
2297(function (tree) {
2298
2299tree.Condition = function (op, l, r, i, negate) {
2300    this.op = op.trim();
2301    this.lvalue = l;
2302    this.rvalue = r;
2303    this.index = i;
2304    this.negate = negate;
2305};
2306tree.Condition.prototype.eval = function (env) {
2307    var a = this.lvalue.eval(env),
2308        b = this.rvalue.eval(env);
2309
2310    var i = this.index, result;
2311
2312    var result = (function (op) {
2313        switch (op) {
2314            case 'and':
2315                return a && b;
2316            case 'or':
2317                return a || b;
2318            default:
2319                if (a.compare) {
2320                    result = a.compare(b);
2321                } else if (b.compare) {
2322                    result = b.compare(a);
2323                } else {
2324                    throw { type: "Type",
2325                            message: "Unable to perform comparison",
2326                            index: i };
2327                }
2328                switch (result) {
2329                    case -1: return op === '<' || op === '=<';
2330                    case  0: return op === '=' || op === '>=' || op === '=<';
2331                    case  1: return op === '>' || op === '>=';
2332                }
2333        }
2334    })(this.op);
2335    return this.negate ? !result : result;
2336};
2337
2338})(require('../tree'));
2339(function (tree) {
2340
2341//
2342// A number with a unit
2343//
2344tree.Dimension = function (value, unit) {
2345    this.value = parseFloat(value);
2346    this.unit = unit || null;
2347};
2348
2349tree.Dimension.prototype = {
2350    eval: function () { return this },
2351    toColor: function () {
2352        return new(tree.Color)([this.value, this.value, this.value]);
2353    },
2354    toCSS: function () {
2355        var css = this.value + this.unit;
2356        return css;
2357    },
2358
2359    // In an operation between two Dimensions,
2360    // we default to the first Dimension's unit,
2361    // so `1px + 2em` will yield `3px`.
2362    // In the future, we could implement some unit
2363    // conversions such that `100cm + 10mm` would yield
2364    // `101cm`.
2365    operate: function (op, other) {
2366        return new(tree.Dimension)
2367                  (tree.operate(op, this.value, other.value),
2368                  this.unit || other.unit);
2369    },
2370
2371    // TODO: Perform unit conversion before comparing
2372    compare: function (other) {
2373        if (other instanceof tree.Dimension) {
2374            if (other.value > this.value) {
2375                return -1;
2376            } else if (other.value < this.value) {
2377                return 1;
2378            } else {
2379                return 0;
2380            }
2381        } else {
2382            return -1;
2383        }
2384    }
2385};
2386
2387})(require('../tree'));
2388(function (tree) {
2389
2390tree.Directive = function (name, value) {
2391    this.name = name;
2392
2393    if (Array.isArray(value)) {
2394        this.ruleset = new(tree.Ruleset)([], value);
2395        this.ruleset.allowImports = true;
2396    } else {
2397        this.value = value;
2398    }
2399};
2400tree.Directive.prototype = {
2401    toCSS: function (ctx, env) {
2402        if (this.ruleset) {
2403            this.ruleset.root = true;
2404            return this.name + (env.compress ? '{' : ' {\n  ') +
2405                   this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n  ') +
2406                               (env.compress ? '}': '\n}\n');
2407        } else {
2408            return this.name + ' ' + this.value.toCSS() + ';\n';
2409        }
2410    },
2411    eval: function (env) {
2412        var evaldDirective = this;
2413        if (this.ruleset) {
2414            env.frames.unshift(this);
2415            evaldDirective = new(tree.Directive)(this.name);
2416            evaldDirective.ruleset = this.ruleset.eval(env);
2417            env.frames.shift();
2418        }
2419        return evaldDirective;
2420    },
2421    variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) },
2422    find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) },
2423    rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }
2424};
2425
2426})(require('../tree'));
2427(function (tree) {
2428
2429tree.Element = function (combinator, value, index) {
2430    this.combinator = combinator instanceof tree.Combinator ?
2431                      combinator : new(tree.Combinator)(combinator);
2432
2433    if (typeof(value) === 'string') {
2434        this.value = value.trim();
2435    } else if (value) {
2436        this.value = value;
2437    } else {
2438        this.value = "";
2439    }
2440    this.index = index;
2441};
2442tree.Element.prototype.eval = function (env) {
2443    return new(tree.Element)(this.combinator,
2444                             this.value.eval ? this.value.eval(env) : this.value,
2445                             this.index);
2446};
2447tree.Element.prototype.toCSS = function (env) {
2448        var value = (this.value.toCSS ? this.value.toCSS(env) : this.value);
2449        if (value == '' && this.combinator.value.charAt(0) == '&') {
2450                return '';
2451        } else {
2452                return this.combinator.toCSS(env || {}) + value;
2453        }
2454};
2455
2456tree.Combinator = function (value) {
2457    if (value === ' ') {
2458        this.value = ' ';
2459    } else {
2460        this.value = value ? value.trim() : "";
2461    }
2462};
2463tree.Combinator.prototype.toCSS = function (env) {
2464    return {
2465        ''  : '',
2466        ' ' : ' ',
2467        ':' : ' :',
2468        '+' : env.compress ? '+' : ' + ',
2469        '~' : env.compress ? '~' : ' ~ ',
2470        '>' : env.compress ? '>' : ' > '
2471    }[this.value];
2472};
2473
2474})(require('../tree'));
2475(function (tree) {
2476
2477tree.Expression = function (value) { this.value = value };
2478tree.Expression.prototype = {
2479    eval: function (env) {
2480        if (this.value.length > 1) {
2481            return new(tree.Expression)(this.value.map(function (e) {
2482                return e.eval(env);
2483            }));
2484        } else if (this.value.length === 1) {
2485            return this.value[0].eval(env);
2486        } else {
2487            return this;
2488        }
2489    },
2490    toCSS: function (env) {
2491        return this.value.map(function (e) {
2492            return e.toCSS ? e.toCSS(env) : '';
2493        }).join(' ');
2494    }
2495};
2496
2497})(require('../tree'));
2498(function (tree) {
2499//
2500// CSS @import node
2501//
2502// The general strategy here is that we don't want to wait
2503// for the parsing to be completed, before we start importing
2504// the file. That's because in the context of a browser,
2505// most of the time will be spent waiting for the server to respond.
2506//
2507// On creation, we push the import path to our import queue, though
2508// `import,push`, we also pass it a callback, which it'll call once
2509// the file has been fetched, and parsed.
2510//
2511tree.Import = function (path, imports, features, once, index) {
2512    var that = this;
2513
2514    this.once = once;
2515    this.index = index;
2516    this._path = path;
2517    this.features = features && new(tree.Value)(features);
2518
2519    // The '.less' extension is optional
2520    if (path instanceof tree.Quoted) {
2521        this.path = /\.(le?|c)ss(\?.*)?$/.test(path.value) ? path.value : path.value + '.less';
2522    } else {
2523        this.path = path.value.value || path.value;
2524    }
2525
2526    this.css = /css(\?.*)?$/.test(this.path);
2527
2528    // Only pre-compile .less files
2529    if (! this.css) {
2530        imports.push(this.path, function (e, root, imported) {
2531            if (e) { e.index = index }
2532            if (imported && that.once) that.skip = imported;
2533            that.root = root || new(tree.Ruleset)([], []);
2534        });
2535    }
2536};
2537
2538//
2539// The actual import node doesn't return anything, when converted to CSS.
2540// The reason is that it's used at the evaluation stage, so that the rules
2541// it imports can be treated like any other rules.
2542//
2543// In `eval`, we make sure all Import nodes get evaluated, recursively, so
2544// we end up with a flat structure, which can easily be imported in the parent
2545// ruleset.
2546//
2547tree.Import.prototype = {
2548    toCSS: function (env) {
2549        var features = this.features ? ' ' + this.features.toCSS(env) : '';
2550
2551        if (this.css) {
2552            return "@import " + this._path.toCSS() + features + ';\n';
2553        } else {
2554            return "";
2555        }
2556    },
2557    eval: function (env) {
2558        var ruleset, features = this.features && this.features.eval(env);
2559
2560        if (this.skip) return [];
2561
2562        if (this.css) {
2563            return this;
2564        } else {
2565            ruleset = new(tree.Ruleset)([], this.root.rules.slice(0));
2566
2567            for (var i = 0; i < ruleset.rules.length; i++) {
2568                if (ruleset.rules[i] instanceof tree.Import) {
2569                    Array.prototype
2570                         .splice
2571                         .apply(ruleset.rules,
2572                                [i, 1].concat(ruleset.rules[i].eval(env)));
2573                }
2574            }
2575            return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules;
2576        }
2577    }
2578};
2579
2580})(require('../tree'));
2581(function (tree) {
2582
2583tree.JavaScript = function (string, index, escaped) {
2584    this.escaped = escaped;
2585    this.expression = string;
2586    this.index = index;
2587};
2588tree.JavaScript.prototype = {
2589    eval: function (env) {
2590        var result,
2591            that = this,
2592            context = {};
2593
2594        var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) {
2595            return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env));
2596        });
2597
2598        try {
2599            expression = new(Function)('return (' + expression + ')');
2600        } catch (e) {
2601            throw { message: "JavaScript evaluation error: `" + expression + "`" ,
2602                    index: this.index };
2603        }
2604
2605        for (var k in env.frames[0].variables()) {
2606            context[k.slice(1)] = {
2607                value: env.frames[0].variables()[k].value,
2608                toJS: function () {
2609                    return this.value.eval(env).toCSS();
2610                }
2611            };
2612        }
2613
2614        try {
2615            result = expression.call(context);
2616        } catch (e) {
2617            throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" ,
2618                    index: this.index };
2619        }
2620        if (typeof(result) === 'string') {
2621            return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index);
2622        } else if (Array.isArray(result)) {
2623            return new(tree.Anonymous)(result.join(', '));
2624        } else {
2625            return new(tree.Anonymous)(result);
2626        }
2627    }
2628};
2629
2630})(require('../tree'));
2631
2632(function (tree) {
2633
2634tree.Keyword = function (value) { this.value = value };
2635tree.Keyword.prototype = {
2636    eval: function () { return this },
2637    toCSS: function () { return this.value },
2638    compare: function (other) {
2639        if (other instanceof tree.Keyword) {
2640            return other.value === this.value ? 0 : 1;
2641        } else {
2642            return -1;
2643        }
2644    }
2645};
2646
2647tree.True = new(tree.Keyword)('true');
2648tree.False = new(tree.Keyword)('false');
2649
2650})(require('../tree'));
2651(function (tree) {
2652
2653tree.Media = function (value, features) {
2654    var selectors = this.emptySelectors();
2655
2656    this.features = new(tree.Value)(features);
2657    this.ruleset = new(tree.Ruleset)(selectors, value);
2658    this.ruleset.allowImports = true;
2659};
2660tree.Media.prototype = {
2661    toCSS: function (ctx, env) {
2662        var features = this.features.toCSS(env);
2663
2664        this.ruleset.root = (ctx.length === 0 || ctx[0].multiMedia);
2665        return '@media ' + features + (env.compress ? '{' : ' {\n  ') +
2666               this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n  ') +
2667                           (env.compress ? '}': '\n}\n');
2668    },
2669    eval: function (env) {
2670        if (!env.mediaBlocks) {
2671            env.mediaBlocks = [];
2672            env.mediaPath = [];
2673        }
2674       
2675        var blockIndex = env.mediaBlocks.length;
2676        env.mediaPath.push(this);
2677        env.mediaBlocks.push(this);
2678
2679        var media = new(tree.Media)([], []);
2680        if(this.debugInfo) {
2681            this.ruleset.debugInfo = this.debugInfo;
2682            media.debugInfo = this.debugInfo;
2683        }
2684        media.features = this.features.eval(env);
2685       
2686        env.frames.unshift(this.ruleset);
2687        media.ruleset = this.ruleset.eval(env);
2688        env.frames.shift();
2689       
2690        env.mediaBlocks[blockIndex] = media;
2691        env.mediaPath.pop();
2692
2693        return env.mediaPath.length === 0 ? media.evalTop(env) :
2694                    media.evalNested(env)
2695    },
2696    variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) },
2697    find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) },
2698    rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) },
2699    emptySelectors: function() {
2700        var el = new(tree.Element)('', '&', 0);
2701        return [new(tree.Selector)([el])];
2702    },
2703
2704    evalTop: function (env) {
2705        var result = this;
2706
2707        // Render all dependent Media blocks.
2708        if (env.mediaBlocks.length > 1) {
2709            var selectors = this.emptySelectors();
2710            result = new(tree.Ruleset)(selectors, env.mediaBlocks);
2711            result.multiMedia = true;
2712        }
2713
2714        delete env.mediaBlocks;
2715        delete env.mediaPath;
2716
2717        return result;
2718    },
2719    evalNested: function (env) {
2720        var i, value,
2721            path = env.mediaPath.concat([this]);
2722
2723        // Extract the media-query conditions separated with `,` (OR).
2724        for (i = 0; i < path.length; i++) {
2725            value = path[i].features instanceof tree.Value ?
2726                        path[i].features.value : path[i].features;
2727            path[i] = Array.isArray(value) ? value : [value];
2728        }
2729
2730        // Trace all permutations to generate the resulting media-query.
2731        //
2732        // (a, b and c) with nested (d, e) ->
2733        //    a and d
2734        //    a and e
2735        //    b and c and d
2736        //    b and c and e
2737        this.features = new(tree.Value)(this.permute(path).map(function (path) {
2738            path = path.map(function (fragment) {
2739                return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment);
2740            });
2741
2742            for(i = path.length - 1; i > 0; i--) {
2743                path.splice(i, 0, new(tree.Anonymous)("and"));
2744            }
2745
2746            return new(tree.Expression)(path);
2747        }));
2748
2749        // Fake a tree-node that doesn't output anything.
2750        return new(tree.Ruleset)([], []);
2751    },
2752    permute: function (arr) {
2753      if (arr.length === 0) {
2754          return [];
2755      } else if (arr.length === 1) {
2756          return arr[0];
2757      } else {
2758          var result = [];
2759          var rest = this.permute(arr.slice(1));
2760          for (var i = 0; i < rest.length; i++) {
2761              for (var j = 0; j < arr[0].length; j++) {
2762                  result.push([arr[0][j]].concat(rest[i]));
2763              }
2764          }
2765          return result;
2766      }
2767    },
2768    bubbleSelectors: function (selectors) {
2769      this.ruleset = new(tree.Ruleset)(selectors.slice(0), [this.ruleset]);
2770    }
2771};
2772
2773})(require('../tree'));
2774(function (tree) {
2775
2776tree.mixin = {};
2777tree.mixin.Call = function (elements, args, index, filename, important) {
2778    this.selector = new(tree.Selector)(elements);
2779    this.arguments = args;
2780    this.index = index;
2781    this.filename = filename;
2782    this.important = important;
2783};
2784tree.mixin.Call.prototype = {
2785    eval: function (env) {
2786        var mixins, args, rules = [], match = false;
2787
2788        for (var i = 0; i < env.frames.length; i++) {
2789            if ((mixins = env.frames[i].find(this.selector)).length > 0) {
2790                args = this.arguments && this.arguments.map(function (a) {
2791                    return { name: a.name, value: a.value.eval(env) };
2792                });
2793                for (var m = 0; m < mixins.length; m++) {
2794                    if (mixins[m].match(args, env)) {
2795                        try {
2796                            Array.prototype.push.apply(
2797                                  rules, mixins[m].eval(env, this.arguments, this.important).rules);
2798                            match = true;
2799                        } catch (e) {
2800                            throw { message: e.message, index: this.index, filename: this.filename, stack: e.stack };
2801                        }
2802                    }
2803                }
2804                if (match) {
2805                    return rules;
2806                } else {
2807                    throw { type:    'Runtime',
2808                            message: 'No matching definition was found for `' +
2809                                      this.selector.toCSS().trim() + '('      +
2810                                      this.arguments.map(function (a) {
2811                                          return a.toCSS();
2812                                      }).join(', ') + ")`",
2813                            index:   this.index, filename: this.filename };
2814                }
2815            }
2816        }
2817        throw { type: 'Name',
2818                message: this.selector.toCSS().trim() + " is undefined",
2819                index: this.index, filename: this.filename };
2820    }
2821};
2822
2823tree.mixin.Definition = function (name, params, rules, condition, variadic) {
2824    this.name = name;
2825    this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])];
2826    this.params = params;
2827    this.condition = condition;
2828    this.variadic = variadic;
2829    this.arity = params.length;
2830    this.rules = rules;
2831    this._lookups = {};
2832    this.required = params.reduce(function (count, p) {
2833        if (!p.name || (p.name && !p.value)) { return count + 1 }
2834        else                                 { return count }
2835    }, 0);
2836    this.parent = tree.Ruleset.prototype;
2837    this.frames = [];
2838};
2839tree.mixin.Definition.prototype = {
2840    toCSS:     function ()     { return "" },
2841    variable:  function (name) { return this.parent.variable.call(this, name) },
2842    variables: function ()     { return this.parent.variables.call(this) },
2843    find:      function ()     { return this.parent.find.apply(this, arguments) },
2844    rulesets:  function ()     { return this.parent.rulesets.apply(this) },
2845
2846    evalParams: function (env, args) {
2847        var frame = new(tree.Ruleset)(null, []), varargs, arg;
2848
2849        for (var i = 0, val, name; i < this.params.length; i++) {
2850            arg = args && args[i]
2851
2852            if (arg && arg.name) {
2853                frame.rules.unshift(new(tree.Rule)(arg.name, arg.value.eval(env)));
2854                args.splice(i, 1);
2855                i--;
2856                continue;
2857            }
2858                       
2859            if (name = this.params[i].name) {
2860                if (this.params[i].variadic && args) {
2861                    varargs = [];
2862                    for (var j = i; j < args.length; j++) {
2863                        varargs.push(args[j].value.eval(env));
2864                    }
2865                    frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env)));
2866                } else if (val = (arg && arg.value) || this.params[i].value) {
2867                    frame.rules.unshift(new(tree.Rule)(name, val.eval(env)));
2868                } else {
2869                    throw { type: 'Runtime', message: "wrong number of arguments for " + this.name +
2870                            ' (' + args.length + ' for ' + this.arity + ')' };
2871                }
2872            }
2873        }
2874        return frame;
2875    },
2876    eval: function (env, args, important) {
2877        var frame = this.evalParams(env, args), context, _arguments = [], rules, start;
2878
2879        for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) {
2880            _arguments.push((args[i] && args[i].value) || this.params[i].value);
2881        }
2882        frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env)));
2883
2884        rules = important ?
2885            this.rules.map(function (r) {
2886                return new(tree.Rule)(r.name, r.value, '!important', r.index);
2887            }) : this.rules.slice(0);
2888
2889        return new(tree.Ruleset)(null, rules).eval({
2890            frames: [this, frame].concat(this.frames, env.frames)
2891        });
2892    },
2893    match: function (args, env) {
2894        var argsLength = (args && args.length) || 0, len, frame;
2895
2896        if (! this.variadic) {
2897            if (argsLength < this.required)                               { return false }
2898            if (argsLength > this.params.length)                          { return false }
2899            if ((this.required > 0) && (argsLength > this.params.length)) { return false }
2900        }
2901
2902        if (this.condition && !this.condition.eval({
2903            frames: [this.evalParams(env, args)].concat(env.frames)
2904        }))                                                           { return false }
2905
2906        len = Math.min(argsLength, this.arity);
2907
2908        for (var i = 0; i < len; i++) {
2909            if (!this.params[i].name) {
2910                if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) {
2911                    return false;
2912                }
2913            }
2914        }
2915        return true;
2916    }
2917};
2918
2919})(require('../tree'));
2920(function (tree) {
2921
2922tree.Operation = function (op, operands) {
2923    this.op = op.trim();
2924    this.operands = operands;
2925};
2926tree.Operation.prototype.eval = function (env) {
2927    var a = this.operands[0].eval(env),
2928        b = this.operands[1].eval(env),
2929        temp;
2930
2931    if (a instanceof tree.Dimension && b instanceof tree.Color) {
2932        if (this.op === '*' || this.op === '+') {
2933            temp = b, b = a, a = temp;
2934        } else {
2935            throw { name: "OperationError",
2936                    message: "Can't substract or divide a color from a number" };
2937        }
2938    }
2939    return a.operate(this.op, b);
2940};
2941
2942tree.operate = function (op, a, b) {
2943    switch (op) {
2944        case '+': return a + b;
2945        case '-': return a - b;
2946        case '*': return a * b;
2947        case '/': return a / b;
2948    }
2949};
2950
2951})(require('../tree'));
2952
2953(function (tree) {
2954
2955tree.Paren = function (node) {
2956    this.value = node;
2957};
2958tree.Paren.prototype = {
2959    toCSS: function (env) {
2960        return '(' + this.value.toCSS(env) + ')';
2961    },
2962    eval: function (env) {
2963        return new(tree.Paren)(this.value.eval(env));
2964    }
2965};
2966
2967})(require('../tree'));
2968(function (tree) {
2969
2970tree.Quoted = function (str, content, escaped, i) {
2971    this.escaped = escaped;
2972    this.value = content || '';
2973    this.quote = str.charAt(0);
2974    this.index = i;
2975};
2976tree.Quoted.prototype = {
2977    toCSS: function () {
2978        if (this.escaped) {
2979            return this.value;
2980        } else {
2981            return this.quote + this.value + this.quote;
2982        }
2983    },
2984    eval: function (env) {
2985        var that = this;
2986        var value = this.value.replace(/`([^`]+)`/g, function (_, exp) {
2987            return new(tree.JavaScript)(exp, that.index, true).eval(env).value;
2988        }).replace(/@\{([\w-]+)\}/g, function (_, name) {
2989            var v = new(tree.Variable)('@' + name, that.index).eval(env);
2990            return ('value' in v) ? v.value : v.toCSS();
2991        });
2992        return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index);
2993    },
2994    compare: function (x) {
2995        if (!x.toCSS) {
2996            return -1;
2997        }
2998       
2999        var left = this.toCSS(),
3000            right = x.toCSS();
3001       
3002        if (left === right) {
3003            return 0;
3004        }
3005       
3006        return left < right ? -1 : 1;
3007    }
3008};
3009
3010})(require('../tree'));
3011(function (tree) {
3012
3013tree.Ratio = function (value) {
3014    this.value = value;
3015};
3016tree.Ratio.prototype = {
3017    toCSS: function (env) {
3018        return this.value;
3019    },
3020    eval: function () { return this }
3021};
3022
3023})(require('../tree'));
3024(function (tree) {
3025
3026tree.Rule = function (name, value, important, index, inline) {
3027    this.name = name;
3028    this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]);
3029    this.important = important ? ' ' + important.trim() : '';
3030    this.index = index;
3031    this.inline = inline || false;
3032
3033    if (name.charAt(0) === '@') {
3034        this.variable = true;
3035    } else { this.variable = false }
3036};
3037tree.Rule.prototype.toCSS = function (env) {
3038    if (this.variable) { return "" }
3039    else {
3040        return this.name + (env.compress ? ':' : ': ') +
3041               this.value.toCSS(env) +
3042               this.important + (this.inline ? "" : ";");
3043    }
3044};
3045
3046tree.Rule.prototype.eval = function (context) {
3047    return new(tree.Rule)(this.name,
3048                          this.value.eval(context),
3049                          this.important,
3050                          this.index, this.inline);
3051};
3052
3053tree.Shorthand = function (a, b) {
3054    this.a = a;
3055    this.b = b;
3056};
3057
3058tree.Shorthand.prototype = {
3059    toCSS: function (env) {
3060        return this.a.toCSS(env) + "/" + this.b.toCSS(env);
3061    },
3062    eval: function () { return this }
3063};
3064
3065})(require('../tree'));
3066(function (tree) {
3067
3068tree.Ruleset = function (selectors, rules, strictImports) {
3069    this.selectors = selectors;
3070    this.rules = rules;
3071    this._lookups = {};
3072    this.strictImports = strictImports;
3073};
3074tree.Ruleset.prototype = {
3075    eval: function (env) {
3076        var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) });
3077        var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports);
3078        var rules = [];
3079       
3080        ruleset.root = this.root;
3081        ruleset.allowImports = this.allowImports;
3082
3083        if(this.debugInfo) {
3084            ruleset.debugInfo = this.debugInfo;
3085        }
3086
3087        // push the current ruleset to the frames stack
3088        env.frames.unshift(ruleset);
3089
3090        // Evaluate imports
3091        if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) {
3092            for (var i = 0; i < ruleset.rules.length; i++) {
3093                if (ruleset.rules[i] instanceof tree.Import) {
3094                    rules = rules.concat(ruleset.rules[i].eval(env));
3095                } else {
3096                    rules.push(ruleset.rules[i]);
3097                }
3098            }
3099            ruleset.rules = rules;
3100            rules = [];
3101        }
3102
3103        // Store the frames around mixin definitions,
3104        // so they can be evaluated like closures when the time comes.
3105        for (var i = 0; i < ruleset.rules.length; i++) {
3106            if (ruleset.rules[i] instanceof tree.mixin.Definition) {
3107                ruleset.rules[i].frames = env.frames.slice(0);
3108            }
3109        }
3110       
3111        var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0;
3112
3113        // Evaluate mixin calls.
3114        for (var i = 0; i < ruleset.rules.length; i++) {
3115            if (ruleset.rules[i] instanceof tree.mixin.Call) {
3116                rules = rules.concat(ruleset.rules[i].eval(env));
3117            } else {
3118                rules.push(ruleset.rules[i]);
3119            }
3120        }
3121        ruleset.rules = rules;
3122
3123        // Evaluate everything else
3124        for (var i = 0, rule; i < ruleset.rules.length; i++) {
3125            rule = ruleset.rules[i];
3126
3127            if (! (rule instanceof tree.mixin.Definition)) {
3128                ruleset.rules[i] = rule.eval ? rule.eval(env) : rule;
3129            }
3130        }
3131
3132        // Pop the stack
3133        env.frames.shift();
3134       
3135        if (env.mediaBlocks) {
3136            for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) {
3137                env.mediaBlocks[i].bubbleSelectors(selectors);
3138            }
3139        }
3140
3141        return ruleset;
3142    },
3143    match: function (args) {
3144        return !args || args.length === 0;
3145    },
3146    variables: function () {
3147        if (this._variables) { return this._variables }
3148        else {
3149            return this._variables = this.rules.reduce(function (hash, r) {
3150                if (r instanceof tree.Rule && r.variable === true) {
3151                    hash[r.name] = r;
3152                }
3153                return hash;
3154            }, {});
3155        }
3156    },
3157    variable: function (name) {
3158        return this.variables()[name];
3159    },
3160    rulesets: function () {
3161        if (this._rulesets) { return this._rulesets }
3162        else {
3163            return this._rulesets = this.rules.filter(function (r) {
3164                return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition);
3165            });
3166        }
3167    },
3168    find: function (selector, self) {
3169        self = self || this;
3170        var rules = [], rule, match,
3171            key = selector.toCSS();
3172
3173        if (key in this._lookups) { return this._lookups[key] }
3174
3175        this.rulesets().forEach(function (rule) {
3176            if (rule !== self) {
3177                for (var j = 0; j < rule.selectors.length; j++) {
3178                    if (match = selector.match(rule.selectors[j])) {
3179                        if (selector.elements.length > rule.selectors[j].elements.length) {
3180                            Array.prototype.push.apply(rules, rule.find(
3181                                new(tree.Selector)(selector.elements.slice(1)), self));
3182                        } else {
3183                            rules.push(rule);
3184                        }
3185                        break;
3186                    }
3187                }
3188            }
3189        });
3190        return this._lookups[key] = rules;
3191    },
3192    //
3193    // Entry point for code generation
3194    //
3195    //     `context` holds an array of arrays.
3196    //
3197    toCSS: function (context, env) {
3198        var css = [],      // The CSS output
3199            rules = [],    // node.Rule instances
3200           _rules = [],    //
3201            rulesets = [], // node.Ruleset instances
3202            paths = [],    // Current selectors
3203            selector,      // The fully rendered selector
3204            debugInfo,     // Line number debugging
3205            rule;
3206
3207        if (! this.root) {
3208            this.joinSelectors(paths, context, this.selectors);
3209        }
3210
3211        // Compile rules and rulesets
3212        for (var i = 0; i < this.rules.length; i++) {
3213            rule = this.rules[i];
3214
3215            if (rule.rules || (rule instanceof tree.Directive) || (rule instanceof tree.Media)) {
3216                rulesets.push(rule.toCSS(paths, env));
3217            } else if (rule instanceof tree.Comment) {
3218                if (!rule.silent) {
3219                    if (this.root) {
3220                        rulesets.push(rule.toCSS(env));
3221                    } else {
3222                        rules.push(rule.toCSS(env));
3223                    }
3224                }
3225            } else {
3226                if (rule.toCSS && !rule.variable) {
3227                    rules.push(rule.toCSS(env));
3228                } else if (rule.value && !rule.variable) {
3229                    rules.push(rule.value.toString());
3230                }
3231            }
3232        }
3233
3234        rulesets = rulesets.join('');
3235
3236        // If this is the root node, we don't render
3237        // a selector, or {}.
3238        // Otherwise, only output if this ruleset has rules.
3239        if (this.root) {
3240            css.push(rules.join(env.compress ? '' : '\n'));
3241        } else {
3242            if (rules.length > 0) {
3243                debugInfo = tree.debugInfo(env, this);
3244                selector = paths.map(function (p) {
3245                    return p.map(function (s) {
3246                        return s.toCSS(env);
3247                    }).join('').trim();
3248                }).join(env.compress ? ',' : ',\n');
3249
3250                // Remove duplicates
3251                for (var i = rules.length - 1; i >= 0; i--) {
3252                    if (_rules.indexOf(rules[i]) === -1) {
3253                        _rules.unshift(rules[i]);
3254                    }
3255                }
3256                rules = _rules;
3257
3258                css.push(debugInfo + selector +
3259                        (env.compress ? '{' : ' {\n  ') +
3260                        rules.join(env.compress ? '' : '\n  ') +
3261                        (env.compress ? '}' : '\n}\n'));
3262            }
3263        }
3264        css.push(rulesets);
3265
3266        return css.join('')  + (env.compress ? '\n' : '');
3267    },
3268
3269    joinSelectors: function (paths, context, selectors) {
3270        for (var s = 0; s < selectors.length; s++) {
3271            this.joinSelector(paths, context, selectors[s]);
3272        }
3273    },
3274
3275    joinSelector: function (paths, context, selector) {
3276
3277        var i, j, k,
3278            hasParentSelector, newSelectors, el, sel, parentSel,
3279            newSelectorPath, afterParentJoin, newJoinedSelector,
3280            newJoinedSelectorEmpty, lastSelector, currentElements,
3281            selectorsMultiplied;
3282   
3283        for (i = 0; i < selector.elements.length; i++) {
3284            el = selector.elements[i];
3285            if (el.value === '&') {
3286                hasParentSelector = true;
3287            }
3288        }
3289   
3290        if (!hasParentSelector) {
3291            if (context.length > 0) {
3292                for(i = 0; i < context.length; i++) {
3293                    paths.push(context[i].concat(selector));
3294                }
3295            }
3296            else {
3297                paths.push([selector]);
3298            }
3299            return;
3300        }
3301
3302        // The paths are [[Selector]]
3303        // The first list is a list of comma seperated selectors
3304        // The inner list is a list of inheritance seperated selectors
3305        // e.g.
3306        // .a, .b {
3307        //   .c {
3308        //   }
3309        // }
3310        // == [[.a] [.c]] [[.b] [.c]]
3311        //
3312
3313        // the elements from the current selector so far
3314        currentElements = [];
3315        // the current list of new selectors to add to the path.
3316        // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
3317        // by the parents
3318        newSelectors = [[]];
3319
3320        for (i = 0; i < selector.elements.length; i++) {
3321            el = selector.elements[i];
3322            // non parent reference elements just get added
3323            if (el.value !== "&") {
3324                currentElements.push(el);
3325            } else {
3326                // the new list of selectors to add
3327                selectorsMultiplied = [];
3328
3329                // merge the current list of non parent selector elements
3330                // on to the current list of selectors to add
3331                if (currentElements.length > 0) {
3332                    this.mergeElementsOnToSelectors(currentElements, newSelectors);
3333                }
3334
3335                // loop through our current selectors
3336                for(j = 0; j < newSelectors.length; j++) {
3337                    sel = newSelectors[j];
3338                    // if we don't have any parent paths, the & might be in a mixin so that it can be used
3339                    // whether there are parents or not
3340                    if (context.length == 0) {
3341                        // the combinator used on el should now be applied to the next element instead so that
3342                        // it is not lost
3343                        if (sel.length > 0) {
3344                            sel[0].elements = sel[0].elements.slice(0);
3345                            sel[0].elements.push(new(tree.Element)(el.combinator, '', 0)); //new Element(el.Combinator,  ""));
3346                        }
3347                        selectorsMultiplied.push(sel);
3348                    }
3349                    else {
3350                        // and the parent selectors
3351                        for(k = 0; k < context.length; k++) {
3352                            parentSel = context[k];
3353                            // We need to put the current selectors
3354                            // then join the last selector's elements on to the parents selectors
3355
3356                            // our new selector path
3357                            newSelectorPath = [];
3358                            // selectors from the parent after the join
3359                            afterParentJoin = [];
3360                            newJoinedSelectorEmpty = true;
3361
3362                            //construct the joined selector - if & is the first thing this will be empty,
3363                            // if not newJoinedSelector will be the last set of elements in the selector
3364                            if (sel.length > 0) {
3365                                newSelectorPath = sel.slice(0);
3366                                lastSelector = newSelectorPath.pop();
3367                                newJoinedSelector = new(tree.Selector)(lastSelector.elements.slice(0));
3368                                newJoinedSelectorEmpty = false;
3369                            }
3370                            else {
3371                                newJoinedSelector = new(tree.Selector)([]);
3372                            }
3373
3374                            //put together the parent selectors after the join
3375                            if (parentSel.length > 1) {
3376                                afterParentJoin = afterParentJoin.concat(parentSel.slice(1));
3377                            }
3378
3379                            if (parentSel.length > 0) {
3380                                newJoinedSelectorEmpty = false;
3381
3382                                // join the elements so far with the first part of the parent
3383                                newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, 0));
3384                                newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1));
3385                            }
3386
3387                            if (!newJoinedSelectorEmpty) {
3388                                // now add the joined selector
3389                                newSelectorPath.push(newJoinedSelector);
3390                            }
3391
3392                            // and the rest of the parent
3393                            newSelectorPath = newSelectorPath.concat(afterParentJoin);
3394
3395                            // add that to our new set of selectors
3396                            selectorsMultiplied.push(newSelectorPath);
3397                        }
3398                    }
3399                }
3400
3401                // our new selectors has been multiplied, so reset the state
3402                newSelectors = selectorsMultiplied;
3403                currentElements = [];
3404            }
3405        }
3406
3407        // if we have any elements left over (e.g. .a& .b == .b)
3408        // add them on to all the current selectors
3409        if (currentElements.length > 0) {
3410            this.mergeElementsOnToSelectors(currentElements, newSelectors);
3411        }
3412
3413        for(i = 0; i < newSelectors.length; i++) {
3414            paths.push(newSelectors[i]);
3415        }
3416    },
3417   
3418    mergeElementsOnToSelectors: function(elements, selectors) {
3419        var i, sel;
3420
3421        if (selectors.length == 0) {
3422            selectors.push([ new(tree.Selector)(elements) ]);
3423            return;
3424        }
3425
3426        for(i = 0; i < selectors.length; i++) {
3427            sel = selectors[i];
3428
3429            // if the previous thing in sel is a parent this needs to join on to it
3430            if (sel.length > 0) {
3431                sel[sel.length - 1] = new(tree.Selector)(sel[sel.length - 1].elements.concat(elements));
3432            }
3433            else {
3434                sel.push(new(tree.Selector)(elements));
3435            }
3436        }
3437    }
3438};
3439})(require('../tree'));
3440(function (tree) {
3441
3442tree.Selector = function (elements) {
3443    this.elements = elements;
3444};
3445tree.Selector.prototype.match = function (other) {
3446    var len  = this.elements.length,
3447        olen = other.elements.length,
3448        max  = Math.min(len, olen);
3449
3450    if (len < olen) {
3451        return false;
3452    } else {
3453        for (var i = 0; i < max; i++) {
3454            if (this.elements[i].value !== other.elements[i].value) {
3455                return false;
3456            }
3457        }
3458    }
3459    return true;
3460};
3461tree.Selector.prototype.eval = function (env) {
3462    return new(tree.Selector)(this.elements.map(function (e) {
3463        return e.eval(env);
3464    }));
3465};
3466tree.Selector.prototype.toCSS = function (env) {
3467    if (this._css) { return this._css }
3468   
3469    if (this.elements[0].combinator.value === "") {
3470        this._css = ' ';
3471    } else {
3472        this._css = '';
3473    }
3474   
3475    this._css += this.elements.map(function (e) {
3476        if (typeof(e) === 'string') {
3477            return ' ' + e.trim();
3478        } else {
3479            return e.toCSS(env);
3480        }
3481    }).join('');
3482   
3483    return this._css;
3484};
3485
3486})(require('../tree'));
3487(function (tree) {
3488
3489tree.URL = function (val, paths) {
3490    this.value = val;
3491    this.paths = paths;
3492};
3493tree.URL.prototype = {
3494    toCSS: function () {
3495        return "url(" + this.value.toCSS() + ")";
3496    },
3497    eval: function (ctx) {
3498        var val = this.value.eval(ctx);
3499
3500        // Add the base path if the URL is relative and we are in the browser
3501        if (typeof window !== 'undefined' && typeof val.value === "string" && !/^(?:[a-z-]+:|\/)/.test(val.value) && this.paths.length > 0) {
3502            val.value = this.paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value);
3503        }
3504
3505        return new(tree.URL)(val, this.paths);
3506    }
3507};
3508
3509})(require('../tree'));
3510(function (tree) {
3511
3512tree.Value = function (value) {
3513    this.value = value;
3514    this.is = 'value';
3515};
3516tree.Value.prototype = {
3517    eval: function (env) {
3518        if (this.value.length === 1) {
3519            return this.value[0].eval(env);
3520        } else {
3521            return new(tree.Value)(this.value.map(function (v) {
3522                return v.eval(env);
3523            }));
3524        }
3525    },
3526    toCSS: function (env) {
3527        return this.value.map(function (e) {
3528            return e.toCSS(env);
3529        }).join(env.compress ? ',' : ', ');
3530    }
3531};
3532
3533})(require('../tree'));
3534(function (tree) {
3535
3536tree.Variable = function (name, index, file) { this.name = name, this.index = index, this.file = file };
3537tree.Variable.prototype = {
3538    eval: function (env) {
3539        var variable, v, name = this.name;
3540
3541        if (name.indexOf('@@') == 0) {
3542            name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value;
3543        }
3544
3545        if (variable = tree.find(env.frames, function (frame) {
3546            if (v = frame.variable(name)) {
3547                return v.value.eval(env);
3548            }
3549        })) { return variable }
3550        else {
3551            throw { type: 'Name',
3552                    message: "variable " + name + " is undefined",
3553                    filename: this.file,
3554                    index: this.index };
3555        }
3556    }
3557};
3558
3559})(require('../tree'));
3560(function (tree) {
3561
3562tree.debugInfo = function(env, ctx) {
3563    var result="";
3564    if (env.dumpLineNumbers && !env.compress) {
3565        switch(env.dumpLineNumbers) {
3566            case 'comments':
3567                result = tree.debugInfo.asComment(ctx);
3568                break;
3569            case 'mediaquery':
3570                result = tree.debugInfo.asMediaQuery(ctx);
3571                break;
3572            case 'all':
3573                result = tree.debugInfo.asComment(ctx)+tree.debugInfo.asMediaQuery(ctx);
3574                break;
3575        }
3576    }
3577    return result;
3578};
3579
3580tree.debugInfo.asComment = function(ctx) {
3581    return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n';
3582};
3583
3584tree.debugInfo.asMediaQuery = function(ctx) {
3585    return '@media -sass-debug-info{filename{font-family:"' + ctx.debugInfo.fileName + '";}line{font-family:"' + ctx.debugInfo.lineNumber + '";}}\n';
3586};
3587
3588tree.find = function (obj, fun) {
3589    for (var i = 0, r; i < obj.length; i++) {
3590        if (r = fun.call(obj, obj[i])) { return r }
3591    }
3592    return null;
3593};
3594tree.jsify = function (obj) {
3595    if (Array.isArray(obj.value) && (obj.value.length > 1)) {
3596        return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']';
3597    } else {
3598        return obj.toCSS(false);
3599    }
3600};
3601
3602})(require('./tree'));
3603var name;
3604
3605function loadStyleSheet(sheet, callback, reload, remaining) {
3606    var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')),
3607        sheetName = name.slice(0, endOfPath + 1) + sheet.href,
3608        contents = sheet.contents || {},
3609        input = readFile(sheetName);
3610       
3611    contents[sheetName] = input;
3612       
3613    var parser = new less.Parser({
3614        paths: [sheet.href.replace(/[\w\.-]+$/, '')],
3615        contents: contents
3616    });
3617    parser.parse(input, function (e, root) {
3618        if (e) {
3619            return error(e, sheetName);
3620        }
3621        try {
3622            callback(e, root, sheet, { local: false, lastModified: 0, remaining: remaining });
3623        } catch(e) {
3624            error(e, sheetName);
3625        }
3626    });
3627}
3628
3629function writeFile(filename, content) {
3630    var fstream = new java.io.FileWriter(filename);
3631    var out = new java.io.BufferedWriter(fstream);
3632    out.write(content);
3633    out.close();
3634}
3635
3636// Command line integration via Rhino
3637(function (args) {
3638    var output,
3639        compress = false,
3640        i;
3641       
3642    for(i = 0; i < args.length; i++) {
3643        switch(args[i]) {
3644            case "-x":
3645                compress = true;
3646                break;
3647            default:
3648                if (!name) {
3649                    name = args[i];
3650                } else if (!output) {
3651                    output = args[i];
3652                } else {
3653                    print("unrecognised parameters");
3654                    print("input_file [output_file] [-x]");
3655                }
3656        }
3657    }
3658
3659    if (!name) {
3660        print('No files present in the fileset; Check your pattern match in build.xml');
3661        quit(1);
3662    }
3663    path = name.split("/");path.pop();path=path.join("/")
3664
3665    var input = readFile(name);
3666
3667    if (!input) {
3668        print('lesscss: couldn\'t open file ' + name);
3669        quit(1);
3670    }
3671
3672    var result;
3673    try {
3674        var parser = new less.Parser();
3675        parser.parse(input, function (e, root) {
3676            if (e) {
3677                error(e, name);
3678                quit(1);
3679            } else {
3680                result = root.toCSS({compress: compress || false});
3681                if (output) {
3682                    writeFile(output, result);
3683                    print("Written to " + output);
3684                } else {
3685                    print(result);
3686                }
3687                quit(0);
3688            }
3689        });
3690    }
3691    catch(e) {
3692        error(e, name);
3693        quit(1);
3694    }
3695    print("done");
3696}(arguments));
3697
3698function error(e, filename) {
3699
3700    var content = "Error : " + filename + "\n";
3701   
3702    filename = e.filename || filename;
3703   
3704    if (e.message) {
3705        content += e.message + "\n";
3706    }
3707
3708    var errorline = function (e, i, classname) {
3709        if (e.extract[i]) {
3710            content +=
3711                String(parseInt(e.line) + (i - 1)) +
3712                ":" + e.extract[i] + "\n";
3713        }
3714    };
3715
3716    if (e.stack) {
3717        content += e.stack;
3718    } else if (e.extract) {
3719        content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n';
3720        errorline(e, 0);
3721        errorline(e, 1);
3722        errorline(e, 2);
3723    }
3724   print(content);
3725}
Note: See TracBrowser for help on using the repository browser.