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

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

Added Dojo 1.9.3 release.

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