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

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

Added Dojo 1.9.3 release.

File size: 134.0 KB
Line 
1//
2// Stub out `require` in rhino
3//
4function require(arg) {
5    return less[arg.split('/')[1]];
6};
7
8
9// ecma-5.js
10//
11// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License
12// -- tlrobinson Tom Robinson
13// dantman Daniel Friesen
14
15//
16// Array
17//
18if (!Array.isArray) {
19    Array.isArray = function(obj) {
20        return Object.prototype.toString.call(obj) === "[object Array]" ||
21               (obj instanceof Array);
22    };
23}
24if (!Array.prototype.forEach) {
25    Array.prototype.forEach =  function(block, thisObject) {
26        var len = this.length >>> 0;
27        for (var i = 0; i < len; i++) {
28            if (i in this) {
29                block.call(thisObject, this[i], i, this);
30            }
31        }
32    };
33}
34if (!Array.prototype.map) {
35    Array.prototype.map = function(fun /*, thisp*/) {
36        var len = this.length >>> 0;
37        var res = new Array(len);
38        var thisp = arguments[1];
39
40        for (var i = 0; i < len; i++) {
41            if (i in this) {
42                res[i] = fun.call(thisp, this[i], i, this);
43            }
44        }
45        return res;
46    };
47}
48if (!Array.prototype.filter) {
49    Array.prototype.filter = function (block /*, thisp */) {
50        var values = [];
51        var thisp = arguments[1];
52        for (var i = 0; i < this.length; i++) {
53            if (block.call(thisp, this[i])) {
54                values.push(this[i]);
55            }
56        }
57        return values;
58    };
59}
60if (!Array.prototype.reduce) {
61    Array.prototype.reduce = function(fun /*, initial*/) {
62        var len = this.length >>> 0;
63        var i = 0;
64
65        // no value to return if no initial value and an empty array
66        if (len === 0 && arguments.length === 1) throw new TypeError();
67
68        if (arguments.length >= 2) {
69            var rv = arguments[1];
70        } else {
71            do {
72                if (i in this) {
73                    rv = this[i++];
74                    break;
75                }
76                // if array contains no values, no initial value to return
77                if (++i >= len) throw new TypeError();
78            } while (true);
79        }
80        for (; i < len; i++) {
81            if (i in this) {
82                rv = fun.call(null, rv, this[i], i, this);
83            }
84        }
85        return rv;
86    };
87}
88if (!Array.prototype.indexOf) {
89    Array.prototype.indexOf = function (value /*, fromIndex */ ) {
90        var length = this.length;
91        var i = arguments[1] || 0;
92
93        if (!length)     return -1;
94        if (i >= length) return -1;
95        if (i < 0)       i += length;
96
97        for (; i < length; i++) {
98            if (!Object.prototype.hasOwnProperty.call(this, i)) { continue }
99            if (value === this[i]) return i;
100        }
101        return -1;
102    };
103}
104
105//
106// Object
107//
108if (!Object.keys) {
109    Object.keys = function (object) {
110        var keys = [];
111        for (var name in object) {
112            if (Object.prototype.hasOwnProperty.call(object, name)) {
113                keys.push(name);
114            }
115        }
116        return keys;
117    };
118}
119
120//
121// String
122//
123if (!String.prototype.trim) {
124    String.prototype.trim = function () {
125        return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, '');
126    };
127}
128var less, tree, 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                    if (!$(')')) { return null; }
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               
1261                save();
1262
1263                if (env.dumpLineNumbers)
1264                    debugInfo = getDebugInfo(i, input, env);
1265
1266                while (s = $(this.selector)) {
1267                    selectors.push(s);
1268                    $(this.comment);
1269                    if (! $(',')) { break }
1270                    $(this.comment);
1271                }
1272
1273                if (selectors.length > 0 && (rules = $(this.block))) {
1274                    var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports);
1275                    if (env.dumpLineNumbers)
1276                        ruleset.debugInfo = debugInfo;
1277                    return ruleset;
1278                } else {
1279                    // Backtrack
1280                    furthest = i;
1281                    restore();
1282                }
1283            },
1284            rule: function () {
1285                var name, value, c = input.charAt(i), important, match;
1286                save();
1287
1288                if (c === '.' || c === '#' || c === '&') { return }
1289
1290                if (name = $(this.variable) || $(this.property)) {
1291                    if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) {
1292                        i += match[0].length - 1;
1293                        value = new(tree.Anonymous)(match[1]);
1294                    } else if (name === "font") {
1295                        value = $(this.font);
1296                    } else {
1297                        value = $(this.value);
1298                    }
1299                    important = $(this.important);
1300
1301                    if (value && $(this.end)) {
1302                        return new(tree.Rule)(name, value, important, memo);
1303                    } else {
1304                        furthest = i;
1305                        restore();
1306                    }
1307                }
1308            },
1309
1310            //
1311            // An @import directive
1312            //
1313            //     @import "lib";
1314            //
1315            // Depending on our environemnt, importing is done differently:
1316            // In the browser, it's an XHR request, in Node, it would be a
1317            // file-system operation. The function used for importing is
1318            // stored in `import`, which we pass to the Import constructor.
1319            //
1320            "import": function () {
1321                var path, features, index = i;
1322               
1323                save();
1324               
1325                var dir = $(/^@import(?:-(once))?\s+/);
1326
1327                if (dir && (path = $(this.entities.quoted) || $(this.entities.url))) {
1328                    features = $(this.mediaFeatures);
1329                    if ($(';')) {
1330                        return new(tree.Import)(path, imports, features, (dir[1] === 'once'), index, env.rootpath);
1331                    }
1332                }
1333               
1334                restore();
1335            },
1336
1337            mediaFeature: function () {
1338                var e, p, nodes = [];
1339
1340                do {
1341                    if (e = $(this.entities.keyword)) {
1342                        nodes.push(e);
1343                    } else if ($('(')) {
1344                        p = $(this.property);
1345                        e = $(this.entity);
1346                        if ($(')')) {
1347                            if (p && e) {
1348                                nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, i, true)));
1349                            } else if (e) {
1350                                nodes.push(new(tree.Paren)(e));
1351                            } else {
1352                                return null;
1353                            }
1354                        } else { return null }
1355                    }
1356                } while (e);
1357
1358                if (nodes.length > 0) {
1359                    return new(tree.Expression)(nodes);
1360                }
1361            },
1362
1363            mediaFeatures: function () {
1364                var e, features = [];
1365
1366                do {
1367                  if (e = $(this.mediaFeature)) {
1368                      features.push(e);
1369                      if (! $(',')) { break }
1370                  } else if (e = $(this.entities.variable)) {
1371                      features.push(e);
1372                      if (! $(',')) { break }
1373                  }
1374                } while (e);
1375
1376                return features.length > 0 ? features : null;
1377            },
1378
1379            media: function () {
1380                var features, rules, media, debugInfo;
1381
1382                if (env.dumpLineNumbers)
1383                    debugInfo = getDebugInfo(i, input, env);
1384
1385                if ($(/^@media/)) {
1386                    features = $(this.mediaFeatures);
1387
1388                    if (rules = $(this.block)) {
1389                        media = new(tree.Media)(rules, features);
1390                        if(env.dumpLineNumbers)
1391                            media.debugInfo = debugInfo;
1392                        return media;
1393                    }
1394                }
1395            },
1396
1397            //
1398            // A CSS Directive
1399            //
1400            //     @charset "utf-8";
1401            //
1402            directive: function () {
1403                var name, value, rules, identifier, e, nodes, nonVendorSpecificName,
1404                    hasBlock, hasIdentifier, hasExpression;
1405
1406                if (input.charAt(i) !== '@') return;
1407
1408                if (value = $(this['import']) || $(this.media)) {
1409                    return value;
1410                }
1411               
1412                save();
1413
1414                name = $(/^@[a-z-]+/);
1415               
1416                if (!name) return;
1417
1418                nonVendorSpecificName = name;
1419                if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) {
1420                    nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1);
1421                }
1422
1423                switch(nonVendorSpecificName) {
1424                    case "@font-face":
1425                        hasBlock = true;
1426                        break;
1427                    case "@viewport":
1428                    case "@top-left":
1429                    case "@top-left-corner":
1430                    case "@top-center":
1431                    case "@top-right":
1432                    case "@top-right-corner":
1433                    case "@bottom-left":
1434                    case "@bottom-left-corner":
1435                    case "@bottom-center":
1436                    case "@bottom-right":
1437                    case "@bottom-right-corner":
1438                    case "@left-top":
1439                    case "@left-middle":
1440                    case "@left-bottom":
1441                    case "@right-top":
1442                    case "@right-middle":
1443                    case "@right-bottom":
1444                        hasBlock = true;
1445                        break;
1446                    case "@page":
1447                    case "@document":
1448                    case "@supports":
1449                    case "@keyframes":
1450                        hasBlock = true;
1451                        hasIdentifier = true;
1452                        break;
1453                    case "@namespace":
1454                        hasExpression = true;
1455                        break;
1456                }
1457
1458                if (hasIdentifier) {
1459                    name += " " + ($(/^[^{]+/) || '').trim();
1460                }
1461
1462                if (hasBlock)
1463                {
1464                    if (rules = $(this.block)) {
1465                        return new(tree.Directive)(name, rules);
1466                    }
1467                } else {
1468                    if ((value = hasExpression ? $(this.expression) : $(this.entity)) && $(';')) {
1469                        var directive = new(tree.Directive)(name, value);
1470                        if (env.dumpLineNumbers) {
1471                            directive.debugInfo = getDebugInfo(i, input, env);
1472                        }
1473                        return directive;
1474                    }
1475                }
1476               
1477                restore();
1478            },
1479            font: function () {
1480                var value = [], expression = [], weight, shorthand, font, e;
1481
1482                while (e = $(this.shorthand) || $(this.entity)) {
1483                    expression.push(e);
1484                }
1485                value.push(new(tree.Expression)(expression));
1486
1487                if ($(',')) {
1488                    while (e = $(this.expression)) {
1489                        value.push(e);
1490                        if (! $(',')) { break }
1491                    }
1492                }
1493                return new(tree.Value)(value);
1494            },
1495
1496            //
1497            // A Value is a comma-delimited list of Expressions
1498            //
1499            //     font-family: Baskerville, Georgia, serif;
1500            //
1501            // In a Rule, a Value represents everything after the `:`,
1502            // and before the `;`.
1503            //
1504            value: function () {
1505                var e, expressions = [], important;
1506
1507                while (e = $(this.expression)) {
1508                    expressions.push(e);
1509                    if (! $(',')) { break }
1510                }
1511
1512                if (expressions.length > 0) {
1513                    return new(tree.Value)(expressions);
1514                }
1515            },
1516            important: function () {
1517                if (input.charAt(i) === '!') {
1518                    return $(/^! *important/);
1519                }
1520            },
1521            sub: function () {
1522                var e;
1523
1524                if ($('(') && (e = $(this.expression)) && $(')')) {
1525                    return e;
1526                }
1527            },
1528            multiplication: function () {
1529                var m, a, op, operation;
1530                if (m = $(this.operand)) {
1531                    while (!peek(/^\/[*\/]/) && (op = ($('/') || $('*'))) && (a = $(this.operand))) {
1532                        operation = new(tree.Operation)(op, [operation || m, a]);
1533                    }
1534                    return operation || m;
1535                }
1536            },
1537            addition: function () {
1538                var m, a, op, operation;
1539                if (m = $(this.multiplication)) {
1540                    while ((op = $(/^[-+]\s+/) || (!isWhitespace(input.charAt(i - 1)) && ($('+') || $('-')))) &&
1541                           (a = $(this.multiplication))) {
1542                        operation = new(tree.Operation)(op, [operation || m, a]);
1543                    }
1544                    return operation || m;
1545                }
1546            },
1547            conditions: function () {
1548                var a, b, index = i, condition;
1549
1550                if (a = $(this.condition)) {
1551                    while ($(',') && (b = $(this.condition))) {
1552                        condition = new(tree.Condition)('or', condition || a, b, index);
1553                    }
1554                    return condition || a;
1555                }
1556            },
1557            condition: function () {
1558                var a, b, c, op, index = i, negate = false;
1559
1560                if ($(/^not/)) { negate = true }
1561                expect('(');
1562                if (a = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) {
1563                    if (op = $(/^(?:>=|=<|[<=>])/)) {
1564                        if (b = $(this.addition) || $(this.entities.keyword) || $(this.entities.quoted)) {
1565                            c = new(tree.Condition)(op, a, b, index, negate);
1566                        } else {
1567                            error('expected expression');
1568                        }
1569                    } else {
1570                        c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate);
1571                    }
1572                    expect(')');
1573                    return $(/^and/) ? new(tree.Condition)('and', c, $(this.condition)) : c;
1574                }
1575            },
1576
1577            //
1578            // An operand is anything that can be part of an operation,
1579            // such as a Color, or a Variable
1580            //
1581            operand: function () {
1582                var negate, p = input.charAt(i + 1);
1583
1584                if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') }
1585                var o = $(this.sub) || $(this.entities.dimension) ||
1586                        $(this.entities.color) || $(this.entities.variable) ||
1587                        $(this.entities.call);
1588                return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o])
1589                              : o;
1590            },
1591
1592            //
1593            // Expressions either represent mathematical operations,
1594            // or white-space delimited Entities.
1595            //
1596            //     1px solid black
1597            //     @var * 2
1598            //
1599            expression: function () {
1600                var e, delim, entities = [], d;
1601
1602                while (e = $(this.addition) || $(this.entity)) {
1603                    entities.push(e);
1604                }
1605                if (entities.length > 0) {
1606                    return new(tree.Expression)(entities);
1607                }
1608            },
1609            property: function () {
1610                var name;
1611
1612                if (name = $(/^(\*?-?[_a-z0-9-]+)\s*:/)) {
1613                    return name[1];
1614                }
1615            }
1616        }
1617    };
1618};
1619
1620if (less.mode === 'browser' || less.mode === 'rhino') {
1621    //
1622    // Used by `@import` directives
1623    //
1624    less.Parser.importer = function (path, paths, callback, env) {
1625        if (!/^([a-z-]+:)?\//.test(path) && paths.length > 0) {
1626            path = paths[0] + path;
1627        }
1628        // We pass `true` as 3rd argument, to force the reload of the import.
1629        // This is so we can get the syntax tree as opposed to just the CSS output,
1630        // as we need this to evaluate the current stylesheet.
1631        loadStyleSheet({
1632            href: path,
1633            title: path,
1634            type: env.mime,
1635            contents: env.contents,
1636            files: env.files,
1637            rootpath: env.rootpath,
1638            entryPath: env.entryPath,
1639            relativeUrls: env.relativeUrls },
1640        function (e, root, data, sheet, _, path) {
1641            if (e && typeof(env.errback) === "function") {
1642                env.errback.call(null, path, paths, callback, env);
1643            } else {
1644                callback.call(null, e, root, path);
1645            }
1646        }, true);
1647    };
1648}
1649
1650(function (tree) {
1651
1652tree.functions = {
1653    rgb: function (r, g, b) {
1654        return this.rgba(r, g, b, 1.0);
1655    },
1656    rgba: function (r, g, b, a) {
1657        var rgb = [r, g, b].map(function (c) { return scaled(c, 256); });
1658        a = number(a);
1659        return new(tree.Color)(rgb, a);
1660    },
1661    hsl: function (h, s, l) {
1662        return this.hsla(h, s, l, 1.0);
1663    },
1664    hsla: function (h, s, l, a) {
1665        h = (number(h) % 360) / 360;
1666        s = number(s); l = number(l); a = number(a);
1667
1668        var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
1669        var m1 = l * 2 - m2;
1670
1671        return this.rgba(hue(h + 1/3) * 255,
1672                         hue(h)       * 255,
1673                         hue(h - 1/3) * 255,
1674                         a);
1675
1676        function hue(h) {
1677            h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
1678            if      (h * 6 < 1) return m1 + (m2 - m1) * h * 6;
1679            else if (h * 2 < 1) return m2;
1680            else if (h * 3 < 2) return m1 + (m2 - m1) * (2/3 - h) * 6;
1681            else                return m1;
1682        }
1683    },
1684
1685    hsv: function(h, s, v) {
1686        return this.hsva(h, s, v, 1.0);
1687    },
1688
1689    hsva: function(h, s, v, a) {
1690        h = ((number(h) % 360) / 360) * 360;
1691        s = number(s); v = number(v); a = number(a);
1692
1693        var i, f;
1694        i = Math.floor((h / 60) % 6);
1695        f = (h / 60) - i;
1696
1697        var vs = [v,
1698                  v * (1 - s),
1699                  v * (1 - f * s),
1700                  v * (1 - (1 - f) * s)];
1701        var perm = [[0, 3, 1],
1702                    [2, 0, 1],
1703                    [1, 0, 3],
1704                    [1, 2, 0],
1705                    [3, 1, 0],
1706                    [0, 1, 2]];
1707
1708        return this.rgba(vs[perm[i][0]] * 255,
1709                         vs[perm[i][1]] * 255,
1710                         vs[perm[i][2]] * 255,
1711                         a);
1712    },
1713
1714    hue: function (color) {
1715        return new(tree.Dimension)(Math.round(color.toHSL().h));
1716    },
1717    saturation: function (color) {
1718        return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%');
1719    },
1720    lightness: function (color) {
1721        return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%');
1722    },
1723    red: function (color) {
1724        return new(tree.Dimension)(color.rgb[0]);
1725    },
1726    green: function (color) {
1727        return new(tree.Dimension)(color.rgb[1]);
1728    },
1729    blue: function (color) {
1730        return new(tree.Dimension)(color.rgb[2]);
1731    },
1732    alpha: function (color) {
1733        return new(tree.Dimension)(color.toHSL().a);
1734    },
1735    luma: function (color) {
1736        return new(tree.Dimension)(Math.round((0.2126 * (color.rgb[0]/255) +
1737            0.7152 * (color.rgb[1]/255) +
1738            0.0722 * (color.rgb[2]/255)) *
1739            color.alpha * 100), '%');
1740    },
1741    saturate: function (color, amount) {
1742        var hsl = color.toHSL();
1743
1744        hsl.s += amount.value / 100;
1745        hsl.s = clamp(hsl.s);
1746        return hsla(hsl);
1747    },
1748    desaturate: function (color, amount) {
1749        var hsl = color.toHSL();
1750
1751        hsl.s -= amount.value / 100;
1752        hsl.s = clamp(hsl.s);
1753        return hsla(hsl);
1754    },
1755    lighten: function (color, amount) {
1756        var hsl = color.toHSL();
1757
1758        hsl.l += amount.value / 100;
1759        hsl.l = clamp(hsl.l);
1760        return hsla(hsl);
1761    },
1762    darken: function (color, amount) {
1763        var hsl = color.toHSL();
1764
1765        hsl.l -= amount.value / 100;
1766        hsl.l = clamp(hsl.l);
1767        return hsla(hsl);
1768    },
1769    fadein: function (color, amount) {
1770        var hsl = color.toHSL();
1771
1772        hsl.a += amount.value / 100;
1773        hsl.a = clamp(hsl.a);
1774        return hsla(hsl);
1775    },
1776    fadeout: function (color, amount) {
1777        var hsl = color.toHSL();
1778
1779        hsl.a -= amount.value / 100;
1780        hsl.a = clamp(hsl.a);
1781        return hsla(hsl);
1782    },
1783    fade: function (color, amount) {
1784        var hsl = color.toHSL();
1785
1786        hsl.a = amount.value / 100;
1787        hsl.a = clamp(hsl.a);
1788        return hsla(hsl);
1789    },
1790    spin: function (color, amount) {
1791        var hsl = color.toHSL();
1792        var hue = (hsl.h + amount.value) % 360;
1793
1794        hsl.h = hue < 0 ? 360 + hue : hue;
1795
1796        return hsla(hsl);
1797    },
1798    //
1799    // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
1800    // http://sass-lang.com
1801    //
1802    mix: function (color1, color2, weight) {
1803        if (!weight) {
1804            weight = new(tree.Dimension)(50);
1805        }
1806        var p = weight.value / 100.0;
1807        var w = p * 2 - 1;
1808        var a = color1.toHSL().a - color2.toHSL().a;
1809
1810        var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
1811        var w2 = 1 - w1;
1812
1813        var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2,
1814                   color1.rgb[1] * w1 + color2.rgb[1] * w2,
1815                   color1.rgb[2] * w1 + color2.rgb[2] * w2];
1816
1817        var alpha = color1.alpha * p + color2.alpha * (1 - p);
1818
1819        return new(tree.Color)(rgb, alpha);
1820    },
1821    greyscale: function (color) {
1822        return this.desaturate(color, new(tree.Dimension)(100));
1823    },
1824    contrast: function (color, dark, light, threshold) {
1825        // filter: contrast(3.2);
1826        // should be kept as is, so check for color
1827        if (!color.rgb) {
1828            return null;
1829        }
1830        if (typeof light === 'undefined') {
1831            light = this.rgba(255, 255, 255, 1.0);
1832        }
1833        if (typeof dark === 'undefined') {
1834            dark = this.rgba(0, 0, 0, 1.0);
1835        }
1836        if (typeof threshold === 'undefined') {
1837            threshold = 0.43;
1838        } else {
1839            threshold = threshold.value;
1840        }
1841        if (((0.2126 * (color.rgb[0]/255) + 0.7152 * (color.rgb[1]/255) + 0.0722 * (color.rgb[2]/255)) * color.alpha) < threshold) {
1842            return light;
1843        } else {
1844            return dark;
1845        }
1846    },
1847    e: function (str) {
1848        return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str);
1849    },
1850    escape: function (str) {
1851        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"));
1852    },
1853    '%': function (quoted /* arg, arg, ...*/) {
1854        var args = Array.prototype.slice.call(arguments, 1),
1855            str = quoted.value;
1856
1857        for (var i = 0; i < args.length; i++) {
1858            str = str.replace(/%[sda]/i, function(token) {
1859                var value = token.match(/s/i) ? args[i].value : args[i].toCSS();
1860                return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value;
1861            });
1862        }
1863        str = str.replace(/%%/g, '%');
1864        return new(tree.Quoted)('"' + str + '"', str);
1865    },
1866    unit: function (val, unit) {
1867        return new(tree.Dimension)(val.value, unit ? unit.toCSS() : "");
1868    },
1869    round: function (n, f) {
1870        var fraction = typeof(f) === "undefined" ? 0 : f.value;
1871        return this._math(function(num) { return num.toFixed(fraction); }, n);
1872    },
1873    ceil: function (n) {
1874        return this._math(Math.ceil, n);
1875    },
1876    floor: function (n) {
1877        return this._math(Math.floor, n);
1878    },
1879    _math: function (fn, n) {
1880        if (n instanceof tree.Dimension) {
1881            return new(tree.Dimension)(fn(parseFloat(n.value)), n.unit);
1882        } else if (typeof(n) === 'number') {
1883            return fn(n);
1884        } else {
1885            throw { type: "Argument", message: "argument must be a number" };
1886        }
1887    },
1888    argb: function (color) {
1889        return new(tree.Anonymous)(color.toARGB());
1890
1891    },
1892    percentage: function (n) {
1893        return new(tree.Dimension)(n.value * 100, '%');
1894    },
1895    color: function (n) {
1896        if (n instanceof tree.Quoted) {
1897            return new(tree.Color)(n.value.slice(1));
1898        } else {
1899            throw { type: "Argument", message: "argument must be a string" };
1900        }
1901    },
1902    iscolor: function (n) {
1903        return this._isa(n, tree.Color);
1904    },
1905    isnumber: function (n) {
1906        return this._isa(n, tree.Dimension);
1907    },
1908    isstring: function (n) {
1909        return this._isa(n, tree.Quoted);
1910    },
1911    iskeyword: function (n) {
1912        return this._isa(n, tree.Keyword);
1913    },
1914    isurl: function (n) {
1915        return this._isa(n, tree.URL);
1916    },
1917    ispixel: function (n) {
1918        return (n instanceof tree.Dimension) && n.unit === 'px' ? tree.True : tree.False;
1919    },
1920    ispercentage: function (n) {
1921        return (n instanceof tree.Dimension) && n.unit === '%' ? tree.True : tree.False;
1922    },
1923    isem: function (n) {
1924        return (n instanceof tree.Dimension) && n.unit === 'em' ? tree.True : tree.False;
1925    },
1926    _isa: function (n, Type) {
1927        return (n instanceof Type) ? tree.True : tree.False;
1928    },
1929   
1930    /* Blending modes */
1931   
1932    multiply: function(color1, color2) {
1933        var r = color1.rgb[0] * color2.rgb[0] / 255;
1934        var g = color1.rgb[1] * color2.rgb[1] / 255;
1935        var b = color1.rgb[2] * color2.rgb[2] / 255;
1936        return this.rgb(r, g, b);
1937    },
1938    screen: function(color1, color2) {
1939        var r = 255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255;
1940        var g = 255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255;
1941        var b = 255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255;
1942        return this.rgb(r, g, b);
1943    },
1944    overlay: function(color1, color2) {
1945        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;
1946        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;
1947        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;
1948        return this.rgb(r, g, b);
1949    },
1950    softlight: function(color1, color2) {
1951        var t = color2.rgb[0] * color1.rgb[0] / 255;
1952        var r = t + color1.rgb[0] * (255 - (255 - color1.rgb[0]) * (255 - color2.rgb[0]) / 255 - t) / 255;
1953        t = color2.rgb[1] * color1.rgb[1] / 255;
1954        var g = t + color1.rgb[1] * (255 - (255 - color1.rgb[1]) * (255 - color2.rgb[1]) / 255 - t) / 255;
1955        t = color2.rgb[2] * color1.rgb[2] / 255;
1956        var b = t + color1.rgb[2] * (255 - (255 - color1.rgb[2]) * (255 - color2.rgb[2]) / 255 - t) / 255;
1957        return this.rgb(r, g, b);
1958    },
1959    hardlight: function(color1, color2) {
1960        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;
1961        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;
1962        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;
1963        return this.rgb(r, g, b);
1964    },
1965    difference: function(color1, color2) {
1966        var r = Math.abs(color1.rgb[0] - color2.rgb[0]);
1967        var g = Math.abs(color1.rgb[1] - color2.rgb[1]);
1968        var b = Math.abs(color1.rgb[2] - color2.rgb[2]);
1969        return this.rgb(r, g, b);
1970    },
1971    exclusion: function(color1, color2) {
1972        var r = color1.rgb[0] + color2.rgb[0] * (255 - color1.rgb[0] - color1.rgb[0]) / 255;
1973        var g = color1.rgb[1] + color2.rgb[1] * (255 - color1.rgb[1] - color1.rgb[1]) / 255;
1974        var b = color1.rgb[2] + color2.rgb[2] * (255 - color1.rgb[2] - color1.rgb[2]) / 255;
1975        return this.rgb(r, g, b);
1976    },
1977    average: function(color1, color2) {
1978        var r = (color1.rgb[0] + color2.rgb[0]) / 2;
1979        var g = (color1.rgb[1] + color2.rgb[1]) / 2;
1980        var b = (color1.rgb[2] + color2.rgb[2]) / 2;
1981        return this.rgb(r, g, b);
1982    },
1983    negation: function(color1, color2) {
1984        var r = 255 - Math.abs(255 - color2.rgb[0] - color1.rgb[0]);
1985        var g = 255 - Math.abs(255 - color2.rgb[1] - color1.rgb[1]);
1986        var b = 255 - Math.abs(255 - color2.rgb[2] - color1.rgb[2]);
1987        return this.rgb(r, g, b);
1988    },
1989    tint: function(color, amount) {
1990        return this.mix(this.rgb(255,255,255), color, amount);
1991    },
1992    shade: function(color, amount) {
1993        return this.mix(this.rgb(0, 0, 0), color, amount);
1994    }
1995};
1996
1997function hsla(color) {
1998    return tree.functions.hsla(color.h, color.s, color.l, color.a);
1999}
2000
2001function scaled(n, size) {
2002    if (n instanceof tree.Dimension && n.unit == '%') {
2003        return parseFloat(n.value * size / 100);
2004    } else {
2005        return number(n);
2006    }
2007}
2008
2009function number(n) {
2010    if (n instanceof tree.Dimension) {
2011        return parseFloat(n.unit == '%' ? n.value / 100 : n.value);
2012    } else if (typeof(n) === 'number') {
2013        return n;
2014    } else {
2015        throw {
2016            error: "RuntimeError",
2017            message: "color functions take numbers as parameters"
2018        };
2019    }
2020}
2021
2022function clamp(val) {
2023    return Math.min(1, Math.max(0, val));
2024}
2025
2026})(require('./tree'));
2027(function (tree) {
2028    tree.colors = {
2029        'aliceblue':'#f0f8ff',
2030        'antiquewhite':'#faebd7',
2031        'aqua':'#00ffff',
2032        'aquamarine':'#7fffd4',
2033        'azure':'#f0ffff',
2034        'beige':'#f5f5dc',
2035        'bisque':'#ffe4c4',
2036        'black':'#000000',
2037        'blanchedalmond':'#ffebcd',
2038        'blue':'#0000ff',
2039        'blueviolet':'#8a2be2',
2040        'brown':'#a52a2a',
2041        'burlywood':'#deb887',
2042        'cadetblue':'#5f9ea0',
2043        'chartreuse':'#7fff00',
2044        'chocolate':'#d2691e',
2045        'coral':'#ff7f50',
2046        'cornflowerblue':'#6495ed',
2047        'cornsilk':'#fff8dc',
2048        'crimson':'#dc143c',
2049        'cyan':'#00ffff',
2050        'darkblue':'#00008b',
2051        'darkcyan':'#008b8b',
2052        'darkgoldenrod':'#b8860b',
2053        'darkgray':'#a9a9a9',
2054        'darkgrey':'#a9a9a9',
2055        'darkgreen':'#006400',
2056        'darkkhaki':'#bdb76b',
2057        'darkmagenta':'#8b008b',
2058        'darkolivegreen':'#556b2f',
2059        'darkorange':'#ff8c00',
2060        'darkorchid':'#9932cc',
2061        'darkred':'#8b0000',
2062        'darksalmon':'#e9967a',
2063        'darkseagreen':'#8fbc8f',
2064        'darkslateblue':'#483d8b',
2065        'darkslategray':'#2f4f4f',
2066        'darkslategrey':'#2f4f4f',
2067        'darkturquoise':'#00ced1',
2068        'darkviolet':'#9400d3',
2069        'deeppink':'#ff1493',
2070        'deepskyblue':'#00bfff',
2071        'dimgray':'#696969',
2072        'dimgrey':'#696969',
2073        'dodgerblue':'#1e90ff',
2074        'firebrick':'#b22222',
2075        'floralwhite':'#fffaf0',
2076        'forestgreen':'#228b22',
2077        'fuchsia':'#ff00ff',
2078        'gainsboro':'#dcdcdc',
2079        'ghostwhite':'#f8f8ff',
2080        'gold':'#ffd700',
2081        'goldenrod':'#daa520',
2082        'gray':'#808080',
2083        'grey':'#808080',
2084        'green':'#008000',
2085        'greenyellow':'#adff2f',
2086        'honeydew':'#f0fff0',
2087        'hotpink':'#ff69b4',
2088        'indianred':'#cd5c5c',
2089        'indigo':'#4b0082',
2090        'ivory':'#fffff0',
2091        'khaki':'#f0e68c',
2092        'lavender':'#e6e6fa',
2093        'lavenderblush':'#fff0f5',
2094        'lawngreen':'#7cfc00',
2095        'lemonchiffon':'#fffacd',
2096        'lightblue':'#add8e6',
2097        'lightcoral':'#f08080',
2098        'lightcyan':'#e0ffff',
2099        'lightgoldenrodyellow':'#fafad2',
2100        'lightgray':'#d3d3d3',
2101        'lightgrey':'#d3d3d3',
2102        'lightgreen':'#90ee90',
2103        'lightpink':'#ffb6c1',
2104        'lightsalmon':'#ffa07a',
2105        'lightseagreen':'#20b2aa',
2106        'lightskyblue':'#87cefa',
2107        'lightslategray':'#778899',
2108        'lightslategrey':'#778899',
2109        'lightsteelblue':'#b0c4de',
2110        'lightyellow':'#ffffe0',
2111        'lime':'#00ff00',
2112        'limegreen':'#32cd32',
2113        'linen':'#faf0e6',
2114        'magenta':'#ff00ff',
2115        'maroon':'#800000',
2116        'mediumaquamarine':'#66cdaa',
2117        'mediumblue':'#0000cd',
2118        'mediumorchid':'#ba55d3',
2119        'mediumpurple':'#9370d8',
2120        'mediumseagreen':'#3cb371',
2121        'mediumslateblue':'#7b68ee',
2122        'mediumspringgreen':'#00fa9a',
2123        'mediumturquoise':'#48d1cc',
2124        'mediumvioletred':'#c71585',
2125        'midnightblue':'#191970',
2126        'mintcream':'#f5fffa',
2127        'mistyrose':'#ffe4e1',
2128        'moccasin':'#ffe4b5',
2129        'navajowhite':'#ffdead',
2130        'navy':'#000080',
2131        'oldlace':'#fdf5e6',
2132        'olive':'#808000',
2133        'olivedrab':'#6b8e23',
2134        'orange':'#ffa500',
2135        'orangered':'#ff4500',
2136        'orchid':'#da70d6',
2137        'palegoldenrod':'#eee8aa',
2138        'palegreen':'#98fb98',
2139        'paleturquoise':'#afeeee',
2140        'palevioletred':'#d87093',
2141        'papayawhip':'#ffefd5',
2142        'peachpuff':'#ffdab9',
2143        'peru':'#cd853f',
2144        'pink':'#ffc0cb',
2145        'plum':'#dda0dd',
2146        'powderblue':'#b0e0e6',
2147        'purple':'#800080',
2148        'red':'#ff0000',
2149        'rosybrown':'#bc8f8f',
2150        'royalblue':'#4169e1',
2151        'saddlebrown':'#8b4513',
2152        'salmon':'#fa8072',
2153        'sandybrown':'#f4a460',
2154        'seagreen':'#2e8b57',
2155        'seashell':'#fff5ee',
2156        'sienna':'#a0522d',
2157        'silver':'#c0c0c0',
2158        'skyblue':'#87ceeb',
2159        'slateblue':'#6a5acd',
2160        'slategray':'#708090',
2161        'slategrey':'#708090',
2162        'snow':'#fffafa',
2163        'springgreen':'#00ff7f',
2164        'steelblue':'#4682b4',
2165        'tan':'#d2b48c',
2166        'teal':'#008080',
2167        'thistle':'#d8bfd8',
2168        'tomato':'#ff6347',
2169        // 'transparent':'rgba(0,0,0,0)',
2170        'turquoise':'#40e0d0',
2171        'violet':'#ee82ee',
2172        'wheat':'#f5deb3',
2173        'white':'#ffffff',
2174        'whitesmoke':'#f5f5f5',
2175        'yellow':'#ffff00',
2176        'yellowgreen':'#9acd32'
2177    };
2178})(require('./tree'));
2179(function (tree) {
2180
2181tree.Alpha = function (val) {
2182    this.value = val;
2183};
2184tree.Alpha.prototype = {
2185    toCSS: function () {
2186        return "alpha(opacity=" +
2187               (this.value.toCSS ? this.value.toCSS() : this.value) + ")";
2188    },
2189    eval: function (env) {
2190        if (this.value.eval) { this.value = this.value.eval(env) }
2191        return this;
2192    }
2193};
2194
2195})(require('../tree'));
2196(function (tree) {
2197
2198tree.Anonymous = function (string) {
2199    this.value = string.value || string;
2200};
2201tree.Anonymous.prototype = {
2202    toCSS: function () {
2203        return this.value;
2204    },
2205    eval: function () { return this },
2206    compare: function (x) {
2207        if (!x.toCSS) {
2208            return -1;
2209        }
2210       
2211        var left = this.toCSS(),
2212            right = x.toCSS();
2213       
2214        if (left === right) {
2215            return 0;
2216        }
2217       
2218        return left < right ? -1 : 1;
2219    }
2220};
2221
2222})(require('../tree'));
2223(function (tree) {
2224
2225tree.Assignment = function (key, val) {
2226    this.key = key;
2227    this.value = val;
2228};
2229tree.Assignment.prototype = {
2230    toCSS: function () {
2231        return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value);
2232    },
2233    eval: function (env) {
2234        if (this.value.eval) {
2235            return new(tree.Assignment)(this.key, this.value.eval(env));
2236        }
2237        return this;
2238    }
2239};
2240
2241})(require('../tree'));(function (tree) {
2242
2243//
2244// A function call node.
2245//
2246tree.Call = function (name, args, index, filename) {
2247    this.name = name;
2248    this.args = args;
2249    this.index = index;
2250    this.filename = filename;
2251};
2252tree.Call.prototype = {
2253    //
2254    // When evaluating a function call,
2255    // we either find the function in `tree.functions` [1],
2256    // in which case we call it, passing the  evaluated arguments,
2257    // if this returns null or we cannot find the function, we
2258    // simply print it out as it appeared originally [2].
2259    //
2260    // The *functions.js* file contains the built-in functions.
2261    //
2262    // The reason why we evaluate the arguments, is in the case where
2263    // we try to pass a variable to a function, like: `saturate(@color)`.
2264    // The function should receive the value, not the variable.
2265    //
2266    eval: function (env) {
2267        var args = this.args.map(function (a) { return a.eval(env) }),
2268            result;
2269
2270        if (this.name in tree.functions) { // 1.
2271            try {
2272                result = tree.functions[this.name].apply(tree.functions, args);
2273                if (result != null) {
2274                    return result;
2275                }
2276            } catch (e) {
2277                throw { type: e.type || "Runtime",
2278                        message: "error evaluating function `" + this.name + "`" +
2279                                 (e.message ? ': ' + e.message : ''),
2280                        index: this.index, filename: this.filename };
2281            }
2282        }
2283       
2284        // 2.
2285        return new(tree.Anonymous)(this.name +
2286            "(" + args.map(function (a) { return a.toCSS(env) }).join(', ') + ")");
2287    },
2288
2289    toCSS: function (env) {
2290        return this.eval(env).toCSS();
2291    }
2292};
2293
2294})(require('../tree'));
2295(function (tree) {
2296//
2297// RGB Colors - #ff0014, #eee
2298//
2299tree.Color = function (rgb, a) {
2300    //
2301    // The end goal here, is to parse the arguments
2302    // into an integer triplet, such as `128, 255, 0`
2303    //
2304    // This facilitates operations and conversions.
2305    //
2306    if (Array.isArray(rgb)) {
2307        this.rgb = rgb;
2308    } else if (rgb.length == 6) {
2309        this.rgb = rgb.match(/.{2}/g).map(function (c) {
2310            return parseInt(c, 16);
2311        });
2312    } else {
2313        this.rgb = rgb.split('').map(function (c) {
2314            return parseInt(c + c, 16);
2315        });
2316    }
2317    this.alpha = typeof(a) === 'number' ? a : 1;
2318};
2319tree.Color.prototype = {
2320    eval: function () { return this },
2321
2322    //
2323    // If we have some transparency, the only way to represent it
2324    // is via `rgba`. Otherwise, we use the hex representation,
2325    // which has better compatibility with older browsers.
2326    // Values are capped between `0` and `255`, rounded and zero-padded.
2327    //
2328    toCSS: function () {
2329        if (this.alpha < 1.0) {
2330            return "rgba(" + this.rgb.map(function (c) {
2331                return Math.round(c);
2332            }).concat(this.alpha).join(', ') + ")";
2333        } else {
2334            return '#' + this.rgb.map(function (i) {
2335                i = Math.round(i);
2336                i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16);
2337                return i.length === 1 ? '0' + i : i;
2338            }).join('');
2339        }
2340    },
2341
2342    //
2343    // Operations have to be done per-channel, if not,
2344    // channels will spill onto each other. Once we have
2345    // our result, in the form of an integer triplet,
2346    // we create a new Color node to hold the result.
2347    //
2348    operate: function (op, other) {
2349        var result = [];
2350
2351        if (! (other instanceof tree.Color)) {
2352            other = other.toColor();
2353        }
2354
2355        for (var c = 0; c < 3; c++) {
2356            result[c] = tree.operate(op, this.rgb[c], other.rgb[c]);
2357        }
2358        return new(tree.Color)(result, this.alpha + other.alpha);
2359    },
2360
2361    toHSL: function () {
2362        var r = this.rgb[0] / 255,
2363            g = this.rgb[1] / 255,
2364            b = this.rgb[2] / 255,
2365            a = this.alpha;
2366
2367        var max = Math.max(r, g, b), min = Math.min(r, g, b);
2368        var h, s, l = (max + min) / 2, d = max - min;
2369
2370        if (max === min) {
2371            h = s = 0;
2372        } else {
2373            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
2374
2375            switch (max) {
2376                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
2377                case g: h = (b - r) / d + 2;               break;
2378                case b: h = (r - g) / d + 4;               break;
2379            }
2380            h /= 6;
2381        }
2382        return { h: h * 360, s: s, l: l, a: a };
2383    },
2384    toARGB: function () {
2385        var argb = [Math.round(this.alpha * 255)].concat(this.rgb);
2386        return '#' + argb.map(function (i) {
2387            i = Math.round(i);
2388            i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16);
2389            return i.length === 1 ? '0' + i : i;
2390        }).join('');
2391    },
2392    compare: function (x) {
2393        if (!x.rgb) {
2394            return -1;
2395        }
2396       
2397        return (x.rgb[0] === this.rgb[0] &&
2398            x.rgb[1] === this.rgb[1] &&
2399            x.rgb[2] === this.rgb[2] &&
2400            x.alpha === this.alpha) ? 0 : -1;
2401    }
2402};
2403
2404
2405})(require('../tree'));
2406(function (tree) {
2407
2408tree.Comment = function (value, silent) {
2409    this.value = value;
2410    this.silent = !!silent;
2411};
2412tree.Comment.prototype = {
2413    toCSS: function (env) {
2414        return env.compress ? '' : this.value;
2415    },
2416    eval: function () { return this }
2417};
2418
2419})(require('../tree'));
2420(function (tree) {
2421
2422tree.Condition = function (op, l, r, i, negate) {
2423    this.op = op.trim();
2424    this.lvalue = l;
2425    this.rvalue = r;
2426    this.index = i;
2427    this.negate = negate;
2428};
2429tree.Condition.prototype.eval = function (env) {
2430    var a = this.lvalue.eval(env),
2431        b = this.rvalue.eval(env);
2432
2433    var i = this.index, result;
2434
2435    var result = (function (op) {
2436        switch (op) {
2437            case 'and':
2438                return a && b;
2439            case 'or':
2440                return a || b;
2441            default:
2442                if (a.compare) {
2443                    result = a.compare(b);
2444                } else if (b.compare) {
2445                    result = b.compare(a);
2446                } else {
2447                    throw { type: "Type",
2448                            message: "Unable to perform comparison",
2449                            index: i };
2450                }
2451                switch (result) {
2452                    case -1: return op === '<' || op === '=<';
2453                    case  0: return op === '=' || op === '>=' || op === '=<';
2454                    case  1: return op === '>' || op === '>=';
2455                }
2456        }
2457    })(this.op);
2458    return this.negate ? !result : result;
2459};
2460
2461})(require('../tree'));
2462(function (tree) {
2463
2464//
2465// A number with a unit
2466//
2467tree.Dimension = function (value, unit) {
2468    this.value = parseFloat(value);
2469    this.unit = unit || null;
2470};
2471
2472tree.Dimension.prototype = {
2473    eval: function () { return this },
2474    toColor: function () {
2475        return new(tree.Color)([this.value, this.value, this.value]);
2476    },
2477    toCSS: function () {
2478        var css = this.value + this.unit;
2479        return css;
2480    },
2481
2482    // In an operation between two Dimensions,
2483    // we default to the first Dimension's unit,
2484    // so `1px + 2em` will yield `3px`.
2485    // In the future, we could implement some unit
2486    // conversions such that `100cm + 10mm` would yield
2487    // `101cm`.
2488    operate: function (op, other) {
2489        return new(tree.Dimension)
2490                  (tree.operate(op, this.value, other.value),
2491                  this.unit || other.unit);
2492    },
2493
2494    compare: function (other) {
2495        if (other instanceof tree.Dimension) {
2496            if (other.value > this.value) {
2497                return -1;
2498            } else if (other.value < this.value) {
2499                return 1;
2500            } else {
2501                if (other.unit && this.unit !== other.unit) {
2502                    return -1;
2503                }
2504                return 0;
2505            }
2506        } else {
2507            return -1;
2508        }
2509    }
2510};
2511
2512})(require('../tree'));
2513(function (tree) {
2514
2515tree.Directive = function (name, value) {
2516    this.name = name;
2517
2518    if (Array.isArray(value)) {
2519        this.ruleset = new(tree.Ruleset)([], value);
2520        this.ruleset.allowImports = true;
2521    } else {
2522        this.value = value;
2523    }
2524};
2525tree.Directive.prototype = {
2526    toCSS: function (ctx, env) {
2527        if (this.ruleset) {
2528            this.ruleset.root = true;
2529            return this.name + (env.compress ? '{' : ' {\n  ') +
2530                   this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n  ') +
2531                               (env.compress ? '}': '\n}\n');
2532        } else {
2533            return this.name + ' ' + this.value.toCSS() + ';\n';
2534        }
2535    },
2536    eval: function (env) {
2537        var evaldDirective = this;
2538        if (this.ruleset) {
2539            env.frames.unshift(this);
2540            evaldDirective = new(tree.Directive)(this.name);
2541            evaldDirective.ruleset = this.ruleset.eval(env);
2542            env.frames.shift();
2543        }
2544        return evaldDirective;
2545    },
2546    variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) },
2547    find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) },
2548    rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }
2549};
2550
2551})(require('../tree'));
2552(function (tree) {
2553
2554tree.Element = function (combinator, value, index) {
2555    this.combinator = combinator instanceof tree.Combinator ?
2556                      combinator : new(tree.Combinator)(combinator);
2557
2558    if (typeof(value) === 'string') {
2559        this.value = value.trim();
2560    } else if (value) {
2561        this.value = value;
2562    } else {
2563        this.value = "";
2564    }
2565    this.index = index;
2566};
2567tree.Element.prototype.eval = function (env) {
2568    return new(tree.Element)(this.combinator,
2569                             this.value.eval ? this.value.eval(env) : this.value,
2570                             this.index);
2571};
2572tree.Element.prototype.toCSS = function (env) {
2573        var value = (this.value.toCSS ? this.value.toCSS(env) : this.value);
2574        if (value == '' && this.combinator.value.charAt(0) == '&') {
2575                return '';
2576        } else {
2577                return this.combinator.toCSS(env || {}) + value;
2578        }
2579};
2580
2581tree.Combinator = function (value) {
2582    if (value === ' ') {
2583        this.value = ' ';
2584    } else {
2585        this.value = value ? value.trim() : "";
2586    }
2587};
2588tree.Combinator.prototype.toCSS = function (env) {
2589    return {
2590        ''  : '',
2591        ' ' : ' ',
2592        ':' : ' :',
2593        '+' : env.compress ? '+' : ' + ',
2594        '~' : env.compress ? '~' : ' ~ ',
2595        '>' : env.compress ? '>' : ' > ',
2596        '|' : env.compress ? '|' : ' | '
2597    }[this.value];
2598};
2599
2600})(require('../tree'));
2601(function (tree) {
2602
2603tree.Expression = function (value) { this.value = value };
2604tree.Expression.prototype = {
2605    eval: function (env) {
2606        if (this.value.length > 1) {
2607            return new(tree.Expression)(this.value.map(function (e) {
2608                return e.eval(env);
2609            }));
2610        } else if (this.value.length === 1) {
2611            return this.value[0].eval(env);
2612        } else {
2613            return this;
2614        }
2615    },
2616    toCSS: function (env) {
2617        return this.value.map(function (e) {
2618            return e.toCSS ? e.toCSS(env) : '';
2619        }).join(' ');
2620    }
2621};
2622
2623})(require('../tree'));
2624(function (tree) {
2625//
2626// CSS @import node
2627//
2628// The general strategy here is that we don't want to wait
2629// for the parsing to be completed, before we start importing
2630// the file. That's because in the context of a browser,
2631// most of the time will be spent waiting for the server to respond.
2632//
2633// On creation, we push the import path to our import queue, though
2634// `import,push`, we also pass it a callback, which it'll call once
2635// the file has been fetched, and parsed.
2636//
2637tree.Import = function (path, imports, features, once, index, rootpath) {
2638    var that = this;
2639
2640    this.once = once;
2641    this.index = index;
2642    this._path = path;
2643    this.features = features && new(tree.Value)(features);
2644    this.rootpath = rootpath;
2645               
2646    // The '.less' extension is optional
2647    if (path instanceof tree.Quoted) {
2648        this.path = /(\.[a-z]*$)|([\?;].*)$/.test(path.value) ? path.value : path.value + '.less';
2649    } else {
2650        this.path = path.value.value || path.value;
2651    }
2652
2653    this.css = /css([\?;].*)?$/.test(this.path);
2654
2655    // Only pre-compile .less files
2656    if (! this.css) {
2657        imports.push(this.path, function (e, root, imported) {
2658            if (e) { e.index = index }
2659            if (imported && that.once) that.skip = imported;
2660            that.root = root || new(tree.Ruleset)([], []);
2661        });
2662    }
2663};
2664
2665//
2666// The actual import node doesn't return anything, when converted to CSS.
2667// The reason is that it's used at the evaluation stage, so that the rules
2668// it imports can be treated like any other rules.
2669//
2670// In `eval`, we make sure all Import nodes get evaluated, recursively, so
2671// we end up with a flat structure, which can easily be imported in the parent
2672// ruleset.
2673//
2674tree.Import.prototype = {
2675    toCSS: function (env) {
2676        var features = this.features ? ' ' + this.features.toCSS(env) : '';
2677
2678        if (this.css) {
2679            // Add the base path if the import is relative
2680            if (typeof this._path.value === "string" && !/^(?:[a-z-]+:|\/)/.test(this._path.value)) {
2681                this._path.value = this.rootpath + this._path.value;
2682            }
2683            return "@import " + this._path.toCSS() + features + ';\n';
2684        } else {
2685            return "";
2686        }
2687    },
2688    eval: function (env) {
2689        var ruleset, features = this.features && this.features.eval(env);
2690
2691        if (this.skip) return [];
2692
2693        if (this.css) {
2694            return this;
2695        } else {
2696            ruleset = new(tree.Ruleset)([], this.root.rules.slice(0));
2697
2698            ruleset.evalImports(env);
2699
2700            return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules;
2701        }
2702    }
2703};
2704
2705})(require('../tree'));
2706(function (tree) {
2707
2708tree.JavaScript = function (string, index, escaped) {
2709    this.escaped = escaped;
2710    this.expression = string;
2711    this.index = index;
2712};
2713tree.JavaScript.prototype = {
2714    eval: function (env) {
2715        var result,
2716            that = this,
2717            context = {};
2718
2719        var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) {
2720            return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env));
2721        });
2722
2723        try {
2724            expression = new(Function)('return (' + expression + ')');
2725        } catch (e) {
2726            throw { message: "JavaScript evaluation error: `" + expression + "`" ,
2727                    index: this.index };
2728        }
2729
2730        for (var k in env.frames[0].variables()) {
2731            context[k.slice(1)] = {
2732                value: env.frames[0].variables()[k].value,
2733                toJS: function () {
2734                    return this.value.eval(env).toCSS();
2735                }
2736            };
2737        }
2738
2739        try {
2740            result = expression.call(context);
2741        } catch (e) {
2742            throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" ,
2743                    index: this.index };
2744        }
2745        if (typeof(result) === 'string') {
2746            return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index);
2747        } else if (Array.isArray(result)) {
2748            return new(tree.Anonymous)(result.join(', '));
2749        } else {
2750            return new(tree.Anonymous)(result);
2751        }
2752    }
2753};
2754
2755})(require('../tree'));
2756
2757(function (tree) {
2758
2759tree.Keyword = function (value) { this.value = value };
2760tree.Keyword.prototype = {
2761    eval: function () { return this },
2762    toCSS: function () { return this.value },
2763    compare: function (other) {
2764        if (other instanceof tree.Keyword) {
2765            return other.value === this.value ? 0 : 1;
2766        } else {
2767            return -1;
2768        }
2769    }
2770};
2771
2772tree.True = new(tree.Keyword)('true');
2773tree.False = new(tree.Keyword)('false');
2774
2775})(require('../tree'));
2776(function (tree) {
2777
2778tree.Media = function (value, features) {
2779    var selectors = this.emptySelectors();
2780
2781    this.features = new(tree.Value)(features);
2782    this.ruleset = new(tree.Ruleset)(selectors, value);
2783    this.ruleset.allowImports = true;
2784};
2785tree.Media.prototype = {
2786    toCSS: function (ctx, env) {
2787        var features = this.features.toCSS(env);
2788
2789        this.ruleset.root = (ctx.length === 0 || ctx[0].multiMedia);
2790        return '@media ' + features + (env.compress ? '{' : ' {\n  ') +
2791               this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n  ') +
2792                           (env.compress ? '}': '\n}\n');
2793    },
2794    eval: function (env) {
2795        if (!env.mediaBlocks) {
2796            env.mediaBlocks = [];
2797            env.mediaPath = [];
2798        }
2799       
2800        var media = new(tree.Media)([], []);
2801        if(this.debugInfo) {
2802            this.ruleset.debugInfo = this.debugInfo;
2803            media.debugInfo = this.debugInfo;
2804        }
2805        media.features = this.features.eval(env);
2806       
2807        env.mediaPath.push(media);
2808        env.mediaBlocks.push(media);
2809       
2810        env.frames.unshift(this.ruleset);
2811        media.ruleset = this.ruleset.eval(env);
2812        env.frames.shift();
2813       
2814        env.mediaPath.pop();
2815
2816        return env.mediaPath.length === 0 ? media.evalTop(env) :
2817                    media.evalNested(env)
2818    },
2819    variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) },
2820    find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) },
2821    rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) },
2822    emptySelectors: function() {
2823        var el = new(tree.Element)('', '&', 0);
2824        return [new(tree.Selector)([el])];
2825    },
2826
2827    evalTop: function (env) {
2828        var result = this;
2829
2830        // Render all dependent Media blocks.
2831        if (env.mediaBlocks.length > 1) {
2832            var selectors = this.emptySelectors();
2833            result = new(tree.Ruleset)(selectors, env.mediaBlocks);
2834            result.multiMedia = true;
2835        }
2836
2837        delete env.mediaBlocks;
2838        delete env.mediaPath;
2839
2840        return result;
2841    },
2842    evalNested: function (env) {
2843        var i, value,
2844            path = env.mediaPath.concat([this]);
2845
2846        // Extract the media-query conditions separated with `,` (OR).
2847        for (i = 0; i < path.length; i++) {
2848            value = path[i].features instanceof tree.Value ?
2849                        path[i].features.value : path[i].features;
2850            path[i] = Array.isArray(value) ? value : [value];
2851        }
2852
2853        // Trace all permutations to generate the resulting media-query.
2854        //
2855        // (a, b and c) with nested (d, e) ->
2856        //    a and d
2857        //    a and e
2858        //    b and c and d
2859        //    b and c and e
2860        this.features = new(tree.Value)(this.permute(path).map(function (path) {
2861            path = path.map(function (fragment) {
2862                return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment);
2863            });
2864
2865            for(i = path.length - 1; i > 0; i--) {
2866                path.splice(i, 0, new(tree.Anonymous)("and"));
2867            }
2868
2869            return new(tree.Expression)(path);
2870        }));
2871
2872        // Fake a tree-node that doesn't output anything.
2873        return new(tree.Ruleset)([], []);
2874    },
2875    permute: function (arr) {
2876      if (arr.length === 0) {
2877          return [];
2878      } else if (arr.length === 1) {
2879          return arr[0];
2880      } else {
2881          var result = [];
2882          var rest = this.permute(arr.slice(1));
2883          for (var i = 0; i < rest.length; i++) {
2884              for (var j = 0; j < arr[0].length; j++) {
2885                  result.push([arr[0][j]].concat(rest[i]));
2886              }
2887          }
2888          return result;
2889      }
2890    },
2891    bubbleSelectors: function (selectors) {
2892      this.ruleset = new(tree.Ruleset)(selectors.slice(0), [this.ruleset]);
2893    }
2894};
2895
2896})(require('../tree'));
2897(function (tree) {
2898
2899tree.mixin = {};
2900tree.mixin.Call = function (elements, args, index, filename, important) {
2901    this.selector = new(tree.Selector)(elements);
2902    this.arguments = args;
2903    this.index = index;
2904    this.filename = filename;
2905    this.important = important;
2906};
2907tree.mixin.Call.prototype = {
2908    eval: function (env) {
2909        var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound;
2910
2911        args = this.arguments && this.arguments.map(function (a) {
2912            return { name: a.name, value: a.value.eval(env) };
2913        });
2914
2915        for (i = 0; i < env.frames.length; i++) {
2916            if ((mixins = env.frames[i].find(this.selector)).length > 0) {
2917                isOneFound = true;
2918                for (m = 0; m < mixins.length; m++) {
2919                    mixin = mixins[m];
2920                    isRecursive = false;
2921                    for(f = 0; f < env.frames.length; f++) {
2922                        if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) {
2923                            isRecursive = true;
2924                            break;
2925                        }
2926                    }
2927                    if (isRecursive) {
2928                        continue;
2929                    }
2930                    if (mixin.matchArgs(args, env)) {
2931                        if (!mixin.matchCondition || mixin.matchCondition(args, env)) {
2932                            try {
2933                                Array.prototype.push.apply(
2934                                      rules, mixin.eval(env, args, this.important).rules);
2935                            } catch (e) {
2936                                throw { message: e.message, index: this.index, filename: this.filename, stack: e.stack };
2937                            }
2938                        }
2939                        match = true;
2940                    }
2941                }
2942                if (match) {
2943                    return rules;
2944                }
2945            }
2946        }
2947        if (isOneFound) {
2948            throw { type:    'Runtime',
2949                    message: 'No matching definition was found for `' +
2950                              this.selector.toCSS().trim() + '('      +
2951                              (args ? args.map(function (a) {
2952                                  var argValue = "";
2953                                  if (a.name) {
2954                                      argValue += a.name + ":";
2955                                  }
2956                                  if (a.value.toCSS) {
2957                                      argValue += a.value.toCSS();
2958                                  } else {
2959                                      argValue += "???";
2960                                  }
2961                                  return argValue;
2962                              }).join(', ') : "") + ")`",
2963                    index:   this.index, filename: this.filename };
2964        } else {
2965            throw { type: 'Name',
2966                message: this.selector.toCSS().trim() + " is undefined",
2967                index: this.index, filename: this.filename };
2968        }
2969    }
2970};
2971
2972tree.mixin.Definition = function (name, params, rules, condition, variadic) {
2973    this.name = name;
2974    this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])];
2975    this.params = params;
2976    this.condition = condition;
2977    this.variadic = variadic;
2978    this.arity = params.length;
2979    this.rules = rules;
2980    this._lookups = {};
2981    this.required = params.reduce(function (count, p) {
2982        if (!p.name || (p.name && !p.value)) { return count + 1 }
2983        else                                 { return count }
2984    }, 0);
2985    this.parent = tree.Ruleset.prototype;
2986    this.frames = [];
2987};
2988tree.mixin.Definition.prototype = {
2989    toCSS:     function ()     { return "" },
2990    variable:  function (name) { return this.parent.variable.call(this, name) },
2991    variables: function ()     { return this.parent.variables.call(this) },
2992    find:      function ()     { return this.parent.find.apply(this, arguments) },
2993    rulesets:  function ()     { return this.parent.rulesets.apply(this) },
2994
2995    evalParams: function (env, mixinEnv, args, evaldArguments) {
2996        var frame = new(tree.Ruleset)(null, []), varargs, arg, params = this.params.slice(0), i, j, val, name, isNamedFound, argIndex;
2997       
2998        if (args) {
2999            args = args.slice(0);
3000
3001            for(i = 0; i < args.length; i++) {
3002                arg = args[i];
3003                if (name = (arg && arg.name)) {
3004                    isNamedFound = false;
3005                    for(j = 0; j < params.length; j++) {
3006                        if (!evaldArguments[j] && name === params[j].name) {
3007                            evaldArguments[j] = arg.value.eval(env);
3008                            frame.rules.unshift(new(tree.Rule)(name, arg.value.eval(env)));
3009                            isNamedFound = true;
3010                            break;
3011                        }
3012                    }
3013                    if (isNamedFound) {
3014                        args.splice(i, 1);
3015                        i--;
3016                        continue;
3017                    } else {
3018                        throw { type: 'Runtime', message: "Named argument for " + this.name +
3019                            ' ' + args[i].name + ' not found' };
3020                    }
3021                }
3022            }
3023        }
3024        argIndex = 0;
3025        for (i = 0; i < params.length; i++) {
3026            if (evaldArguments[i]) continue;
3027           
3028            arg = args && args[argIndex];
3029
3030            if (name = params[i].name) {
3031                if (params[i].variadic && args) {
3032                    varargs = [];
3033                    for (j = argIndex; j < args.length; j++) {
3034                        varargs.push(args[j].value.eval(env));
3035                    }
3036                    frame.rules.unshift(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env)));
3037                } else {
3038                    val = arg && arg.value;
3039                    if (val) {
3040                        val = val.eval(env);
3041                    } else if (params[i].value) {
3042                        val = params[i].value.eval(mixinEnv);
3043                    } else {
3044                        throw { type: 'Runtime', message: "wrong number of arguments for " + this.name +
3045                            ' (' + args.length + ' for ' + this.arity + ')' };
3046                    }
3047                   
3048                    frame.rules.unshift(new(tree.Rule)(name, val));
3049                    evaldArguments[i] = val;
3050                }
3051            }
3052           
3053            if (params[i].variadic && args) {
3054                for (j = argIndex; j < args.length; j++) {
3055                    evaldArguments[j] = args[j].value.eval(env);
3056                }
3057            }
3058            argIndex++;
3059        }
3060
3061        return frame;
3062    },
3063    eval: function (env, args, important) {
3064        var _arguments = [],
3065            mixinFrames = this.frames.concat(env.frames),
3066            frame = this.evalParams(env, {frames: mixinFrames}, args, _arguments),
3067            context, rules, start, ruleset;
3068
3069        frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env)));
3070
3071        rules = important ?
3072            this.parent.makeImportant.apply(this).rules : this.rules.slice(0);
3073
3074        ruleset = new(tree.Ruleset)(null, rules).eval({
3075            frames: [this, frame].concat(mixinFrames)
3076        });
3077        ruleset.originalRuleset = this;
3078        return ruleset;
3079    },
3080    matchCondition: function (args, env) {
3081        if (this.condition && !this.condition.eval({
3082            frames: [this.evalParams(env, {frames: this.frames.concat(env.frames)}, args, [])].concat(env.frames)
3083        }))                                                           { return false }
3084        return true;
3085    },
3086    matchArgs: function (args, env) {
3087        var argsLength = (args && args.length) || 0, len, frame;
3088
3089        if (! this.variadic) {
3090            if (argsLength < this.required)                               { return false }
3091            if (argsLength > this.params.length)                          { return false }
3092            if ((this.required > 0) && (argsLength > this.params.length)) { return false }
3093        }
3094
3095        len = Math.min(argsLength, this.arity);
3096
3097        for (var i = 0; i < len; i++) {
3098            if (!this.params[i].name && !this.params[i].variadic) {
3099                if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) {
3100                    return false;
3101                }
3102            }
3103        }
3104        return true;
3105    }
3106};
3107
3108})(require('../tree'));
3109(function (tree) {
3110
3111tree.Operation = function (op, operands) {
3112    this.op = op.trim();
3113    this.operands = operands;
3114};
3115tree.Operation.prototype.eval = function (env) {
3116    var a = this.operands[0].eval(env),
3117        b = this.operands[1].eval(env),
3118        temp;
3119
3120    if (a instanceof tree.Dimension && b instanceof tree.Color) {
3121        if (this.op === '*' || this.op === '+') {
3122            temp = b, b = a, a = temp;
3123        } else {
3124            throw { name: "OperationError",
3125                    message: "Can't substract or divide a color from a number" };
3126        }
3127    }
3128    if (!a.operate) {
3129        throw { name: "OperationError",
3130                message: "Operation on an invalid type" };
3131    }
3132
3133    return a.operate(this.op, b);
3134};
3135
3136tree.operate = function (op, a, b) {
3137    switch (op) {
3138        case '+': return a + b;
3139        case '-': return a - b;
3140        case '*': return a * b;
3141        case '/': return a / b;
3142    }
3143};
3144
3145})(require('../tree'));
3146
3147(function (tree) {
3148
3149tree.Paren = function (node) {
3150    this.value = node;
3151};
3152tree.Paren.prototype = {
3153    toCSS: function (env) {
3154        return '(' + this.value.toCSS(env) + ')';
3155    },
3156    eval: function (env) {
3157        return new(tree.Paren)(this.value.eval(env));
3158    }
3159};
3160
3161})(require('../tree'));
3162(function (tree) {
3163
3164tree.Quoted = function (str, content, escaped, i) {
3165    this.escaped = escaped;
3166    this.value = content || '';
3167    this.quote = str.charAt(0);
3168    this.index = i;
3169};
3170tree.Quoted.prototype = {
3171    toCSS: function () {
3172        if (this.escaped) {
3173            return this.value;
3174        } else {
3175            return this.quote + this.value + this.quote;
3176        }
3177    },
3178    eval: function (env) {
3179        var that = this;
3180        var value = this.value.replace(/`([^`]+)`/g, function (_, exp) {
3181            return new(tree.JavaScript)(exp, that.index, true).eval(env).value;
3182        }).replace(/@\{([\w-]+)\}/g, function (_, name) {
3183            var v = new(tree.Variable)('@' + name, that.index).eval(env);
3184            return (v instanceof tree.Quoted) ? v.value : v.toCSS();
3185        });
3186        return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index);
3187    },
3188    compare: function (x) {
3189        if (!x.toCSS) {
3190            return -1;
3191        }
3192       
3193        var left = this.toCSS(),
3194            right = x.toCSS();
3195       
3196        if (left === right) {
3197            return 0;
3198        }
3199       
3200        return left < right ? -1 : 1;
3201    }
3202};
3203
3204})(require('../tree'));
3205(function (tree) {
3206
3207tree.Ratio = function (value) {
3208    this.value = value;
3209};
3210tree.Ratio.prototype = {
3211    toCSS: function (env) {
3212        return this.value;
3213    },
3214    eval: function () { return this }
3215};
3216
3217})(require('../tree'));
3218(function (tree) {
3219
3220tree.Rule = function (name, value, important, index, inline) {
3221    this.name = name;
3222    this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]);
3223    this.important = important ? ' ' + important.trim() : '';
3224    this.index = index;
3225    this.inline = inline || false;
3226
3227    if (name.charAt(0) === '@') {
3228        this.variable = true;
3229    } else { this.variable = false }
3230};
3231tree.Rule.prototype.toCSS = function (env) {
3232    if (this.variable) { return "" }
3233    else {
3234        return this.name + (env.compress ? ':' : ': ') +
3235               this.value.toCSS(env) +
3236               this.important + (this.inline ? "" : ";");
3237    }
3238};
3239
3240tree.Rule.prototype.eval = function (context) {
3241    return new(tree.Rule)(this.name,
3242                          this.value.eval(context),
3243                          this.important,
3244                          this.index, this.inline);
3245};
3246
3247tree.Rule.prototype.makeImportant = function () {
3248    return new(tree.Rule)(this.name,
3249                          this.value,
3250                          "!important",
3251                          this.index, this.inline);
3252};
3253
3254tree.Shorthand = function (a, b) {
3255    this.a = a;
3256    this.b = b;
3257};
3258
3259tree.Shorthand.prototype = {
3260    toCSS: function (env) {
3261        return this.a.toCSS(env) + "/" + this.b.toCSS(env);
3262    },
3263    eval: function () { return this }
3264};
3265
3266})(require('../tree'));
3267(function (tree) {
3268
3269tree.Ruleset = function (selectors, rules, strictImports) {
3270    this.selectors = selectors;
3271    this.rules = rules;
3272    this._lookups = {};
3273    this.strictImports = strictImports;
3274};
3275tree.Ruleset.prototype = {
3276    eval: function (env) {
3277        var selectors = this.selectors && this.selectors.map(function (s) { return s.eval(env) });
3278        var ruleset = new(tree.Ruleset)(selectors, this.rules.slice(0), this.strictImports);
3279        var rules;
3280       
3281        ruleset.originalRuleset = this;
3282        ruleset.root = this.root;
3283        ruleset.allowImports = this.allowImports;
3284
3285        if(this.debugInfo) {
3286            ruleset.debugInfo = this.debugInfo;
3287        }
3288
3289        // push the current ruleset to the frames stack
3290        env.frames.unshift(ruleset);
3291
3292        // Evaluate imports
3293        if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) {
3294            ruleset.evalImports(env);
3295        }
3296
3297        // Store the frames around mixin definitions,
3298        // so they can be evaluated like closures when the time comes.
3299        for (var i = 0; i < ruleset.rules.length; i++) {
3300            if (ruleset.rules[i] instanceof tree.mixin.Definition) {
3301                ruleset.rules[i].frames = env.frames.slice(0);
3302            }
3303        }
3304       
3305        var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0;
3306
3307        // Evaluate mixin calls.
3308        for (var i = 0; i < ruleset.rules.length; i++) {
3309            if (ruleset.rules[i] instanceof tree.mixin.Call) {
3310                rules = ruleset.rules[i].eval(env);
3311                ruleset.rules.splice.apply(ruleset.rules, [i, 1].concat(rules));
3312                i += rules.length-1;
3313                ruleset.resetCache();
3314            }
3315        }
3316
3317        // Evaluate everything else
3318        for (var i = 0, rule; i < ruleset.rules.length; i++) {
3319            rule = ruleset.rules[i];
3320
3321            if (! (rule instanceof tree.mixin.Definition)) {
3322                ruleset.rules[i] = rule.eval ? rule.eval(env) : rule;
3323            }
3324        }
3325
3326        // Pop the stack
3327        env.frames.shift();
3328       
3329        if (env.mediaBlocks) {
3330            for(var i = mediaBlockCount; i < env.mediaBlocks.length; i++) {
3331                env.mediaBlocks[i].bubbleSelectors(selectors);
3332            }
3333        }
3334
3335        return ruleset;
3336    },
3337    evalImports: function(env) {
3338        var i, rules;
3339        for (i = 0; i < this.rules.length; i++) {
3340            if (this.rules[i] instanceof tree.Import) {
3341                rules = this.rules[i].eval(env);
3342                if (typeof rules.length === "number") {
3343                    this.rules.splice.apply(this.rules, [i, 1].concat(rules));
3344                    i+= rules.length-1;
3345                } else {
3346                    this.rules.splice(i, 1, rules);
3347                }
3348                this.resetCache();
3349            }
3350        }
3351    },
3352    makeImportant: function() {
3353        return new tree.Ruleset(this.selectors, this.rules.map(function (r) {
3354                    if (r.makeImportant) {
3355                        return r.makeImportant();
3356                    } else {
3357                        return r;
3358                    }
3359                }), this.strictImports);
3360    },
3361    matchArgs: function (args) {
3362        return !args || args.length === 0;
3363    },
3364    resetCache: function () {
3365        this._rulesets = null;
3366        this._variables = null;
3367        this._lookups = {};
3368    },
3369    variables: function () {
3370        if (this._variables) { return this._variables }
3371        else {
3372            return this._variables = this.rules.reduce(function (hash, r) {
3373                if (r instanceof tree.Rule && r.variable === true) {
3374                    hash[r.name] = r;
3375                }
3376                return hash;
3377            }, {});
3378        }
3379    },
3380    variable: function (name) {
3381        return this.variables()[name];
3382    },
3383    rulesets: function () {
3384        if (this._rulesets) { return this._rulesets }
3385        else {
3386            return this._rulesets = this.rules.filter(function (r) {
3387                return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition);
3388            });
3389        }
3390    },
3391    find: function (selector, self) {
3392        self = self || this;
3393        var rules = [], rule, match,
3394            key = selector.toCSS();
3395
3396        if (key in this._lookups) { return this._lookups[key] }
3397
3398        this.rulesets().forEach(function (rule) {
3399            if (rule !== self) {
3400                for (var j = 0; j < rule.selectors.length; j++) {
3401                    if (match = selector.match(rule.selectors[j])) {
3402                        if (selector.elements.length > rule.selectors[j].elements.length) {
3403                            Array.prototype.push.apply(rules, rule.find(
3404                                new(tree.Selector)(selector.elements.slice(1)), self));
3405                        } else {
3406                            rules.push(rule);
3407                        }
3408                        break;
3409                    }
3410                }
3411            }
3412        });
3413        return this._lookups[key] = rules;
3414    },
3415    //
3416    // Entry point for code generation
3417    //
3418    //     `context` holds an array of arrays.
3419    //
3420    toCSS: function (context, env) {
3421        var css = [],      // The CSS output
3422            rules = [],    // node.Rule instances
3423           _rules = [],    //
3424            rulesets = [], // node.Ruleset instances
3425            paths = [],    // Current selectors
3426            selector,      // The fully rendered selector
3427            debugInfo,     // Line number debugging
3428            rule;
3429
3430        if (! this.root) {
3431            this.joinSelectors(paths, context, this.selectors);
3432        }
3433
3434        // Compile rules and rulesets
3435        for (var i = 0; i < this.rules.length; i++) {
3436            rule = this.rules[i];
3437
3438            if (rule.rules || (rule instanceof tree.Media)) {
3439                rulesets.push(rule.toCSS(paths, env));
3440            } else if (rule instanceof tree.Directive) {
3441                var cssValue = rule.toCSS(paths, env);
3442                // Output only the first @charset definition as such - convert the others
3443                // to comments in case debug is enabled
3444                if (rule.name === "@charset") {
3445                    // Only output the debug info together with subsequent @charset definitions
3446                    // a comment (or @media statement) before the actual @charset directive would
3447                    // be considered illegal css as it has to be on the first line
3448                    if (env.charset) {
3449                        if (rule.debugInfo) {
3450                            rulesets.push(tree.debugInfo(env, rule));
3451                            rulesets.push(new tree.Comment("/* "+cssValue.replace(/\n/g, "")+" */\n").toCSS(env));
3452                        }
3453                        continue;
3454                    }
3455                    env.charset = true;
3456                }
3457                rulesets.push(cssValue);
3458            } else if (rule instanceof tree.Comment) {
3459                if (!rule.silent) {
3460                    if (this.root) {
3461                        rulesets.push(rule.toCSS(env));
3462                    } else {
3463                        rules.push(rule.toCSS(env));
3464                    }
3465                }
3466            } else {
3467                if (rule.toCSS && !rule.variable) {
3468                    rules.push(rule.toCSS(env));
3469                } else if (rule.value && !rule.variable) {
3470                    rules.push(rule.value.toString());
3471                }
3472            }
3473        }
3474
3475        rulesets = rulesets.join('');
3476
3477        // If this is the root node, we don't render
3478        // a selector, or {}.
3479        // Otherwise, only output if this ruleset has rules.
3480        if (this.root) {
3481            css.push(rules.join(env.compress ? '' : '\n'));
3482        } else {
3483            if (rules.length > 0) {
3484                debugInfo = tree.debugInfo(env, this);
3485                selector = paths.map(function (p) {
3486                    return p.map(function (s) {
3487                        return s.toCSS(env);
3488                    }).join('').trim();
3489                }).join(env.compress ? ',' : ',\n');
3490
3491                // Remove duplicates
3492                for (var i = rules.length - 1; i >= 0; i--) {
3493                    if (_rules.indexOf(rules[i]) === -1) {
3494                        _rules.unshift(rules[i]);
3495                    }
3496                }
3497                rules = _rules;
3498
3499                css.push(debugInfo + selector +
3500                        (env.compress ? '{' : ' {\n  ') +
3501                        rules.join(env.compress ? '' : '\n  ') +
3502                        (env.compress ? '}' : '\n}\n'));
3503            }
3504        }
3505        css.push(rulesets);
3506
3507        return css.join('')  + (env.compress ? '\n' : '');
3508    },
3509
3510    joinSelectors: function (paths, context, selectors) {
3511        for (var s = 0; s < selectors.length; s++) {
3512            this.joinSelector(paths, context, selectors[s]);
3513        }
3514    },
3515
3516    joinSelector: function (paths, context, selector) {
3517
3518        var i, j, k,
3519            hasParentSelector, newSelectors, el, sel, parentSel,
3520            newSelectorPath, afterParentJoin, newJoinedSelector,
3521            newJoinedSelectorEmpty, lastSelector, currentElements,
3522            selectorsMultiplied;
3523   
3524        for (i = 0; i < selector.elements.length; i++) {
3525            el = selector.elements[i];
3526            if (el.value === '&') {
3527                hasParentSelector = true;
3528            }
3529        }
3530   
3531        if (!hasParentSelector) {
3532            if (context.length > 0) {
3533                for(i = 0; i < context.length; i++) {
3534                    paths.push(context[i].concat(selector));
3535                }
3536            }
3537            else {
3538                paths.push([selector]);
3539            }
3540            return;
3541        }
3542
3543        // The paths are [[Selector]]
3544        // The first list is a list of comma seperated selectors
3545        // The inner list is a list of inheritance seperated selectors
3546        // e.g.
3547        // .a, .b {
3548        //   .c {
3549        //   }
3550        // }
3551        // == [[.a] [.c]] [[.b] [.c]]
3552        //
3553
3554        // the elements from the current selector so far
3555        currentElements = [];
3556        // the current list of new selectors to add to the path.
3557        // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
3558        // by the parents
3559        newSelectors = [[]];
3560
3561        for (i = 0; i < selector.elements.length; i++) {
3562            el = selector.elements[i];
3563            // non parent reference elements just get added
3564            if (el.value !== "&") {
3565                currentElements.push(el);
3566            } else {
3567                // the new list of selectors to add
3568                selectorsMultiplied = [];
3569
3570                // merge the current list of non parent selector elements
3571                // on to the current list of selectors to add
3572                if (currentElements.length > 0) {
3573                    this.mergeElementsOnToSelectors(currentElements, newSelectors);
3574                }
3575
3576                // loop through our current selectors
3577                for(j = 0; j < newSelectors.length; j++) {
3578                    sel = newSelectors[j];
3579                    // if we don't have any parent paths, the & might be in a mixin so that it can be used
3580                    // whether there are parents or not
3581                    if (context.length == 0) {
3582                        // the combinator used on el should now be applied to the next element instead so that
3583                        // it is not lost
3584                        if (sel.length > 0) {
3585                            sel[0].elements = sel[0].elements.slice(0);
3586                            sel[0].elements.push(new(tree.Element)(el.combinator, '', 0)); //new Element(el.Combinator,  ""));
3587                        }
3588                        selectorsMultiplied.push(sel);
3589                    }
3590                    else {
3591                        // and the parent selectors
3592                        for(k = 0; k < context.length; k++) {
3593                            parentSel = context[k];
3594                            // We need to put the current selectors
3595                            // then join the last selector's elements on to the parents selectors
3596
3597                            // our new selector path
3598                            newSelectorPath = [];
3599                            // selectors from the parent after the join
3600                            afterParentJoin = [];
3601                            newJoinedSelectorEmpty = true;
3602
3603                            //construct the joined selector - if & is the first thing this will be empty,
3604                            // if not newJoinedSelector will be the last set of elements in the selector
3605                            if (sel.length > 0) {
3606                                newSelectorPath = sel.slice(0);
3607                                lastSelector = newSelectorPath.pop();
3608                                newJoinedSelector = new(tree.Selector)(lastSelector.elements.slice(0));
3609                                newJoinedSelectorEmpty = false;
3610                            }
3611                            else {
3612                                newJoinedSelector = new(tree.Selector)([]);
3613                            }
3614
3615                            //put together the parent selectors after the join
3616                            if (parentSel.length > 1) {
3617                                afterParentJoin = afterParentJoin.concat(parentSel.slice(1));
3618                            }
3619
3620                            if (parentSel.length > 0) {
3621                                newJoinedSelectorEmpty = false;
3622
3623                                // join the elements so far with the first part of the parent
3624                                newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, 0));
3625                                newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1));
3626                            }
3627
3628                            if (!newJoinedSelectorEmpty) {
3629                                // now add the joined selector
3630                                newSelectorPath.push(newJoinedSelector);
3631                            }
3632
3633                            // and the rest of the parent
3634                            newSelectorPath = newSelectorPath.concat(afterParentJoin);
3635
3636                            // add that to our new set of selectors
3637                            selectorsMultiplied.push(newSelectorPath);
3638                        }
3639                    }
3640                }
3641
3642                // our new selectors has been multiplied, so reset the state
3643                newSelectors = selectorsMultiplied;
3644                currentElements = [];
3645            }
3646        }
3647
3648        // if we have any elements left over (e.g. .a& .b == .b)
3649        // add them on to all the current selectors
3650        if (currentElements.length > 0) {
3651            this.mergeElementsOnToSelectors(currentElements, newSelectors);
3652        }
3653
3654        for(i = 0; i < newSelectors.length; i++) {
3655            paths.push(newSelectors[i]);
3656        }
3657    },
3658   
3659    mergeElementsOnToSelectors: function(elements, selectors) {
3660        var i, sel;
3661
3662        if (selectors.length == 0) {
3663            selectors.push([ new(tree.Selector)(elements) ]);
3664            return;
3665        }
3666
3667        for(i = 0; i < selectors.length; i++) {
3668            sel = selectors[i];
3669
3670            // if the previous thing in sel is a parent this needs to join on to it
3671            if (sel.length > 0) {
3672                sel[sel.length - 1] = new(tree.Selector)(sel[sel.length - 1].elements.concat(elements));
3673            }
3674            else {
3675                sel.push(new(tree.Selector)(elements));
3676            }
3677        }
3678    }
3679};
3680})(require('../tree'));
3681(function (tree) {
3682
3683tree.Selector = function (elements) {
3684    this.elements = elements;
3685};
3686tree.Selector.prototype.match = function (other) {
3687    var elements = this.elements,
3688        len = elements.length,
3689        oelements, olen, max, i;
3690
3691    oelements = other.elements.slice(
3692        (other.elements.length && other.elements[0].value === "&") ? 1 : 0);
3693    olen = oelements.length;
3694    max = Math.min(len, olen)
3695
3696    if (olen === 0 || len < olen) {
3697        return false;
3698    } else {
3699        for (i = 0; i < max; i++) {
3700            if (elements[i].value !== oelements[i].value) {
3701                return false;
3702            }
3703        }
3704    }
3705    return true;
3706};
3707tree.Selector.prototype.eval = function (env) {
3708    return new(tree.Selector)(this.elements.map(function (e) {
3709        return e.eval(env);
3710    }));
3711};
3712tree.Selector.prototype.toCSS = function (env) {
3713    if (this._css) { return this._css }
3714   
3715    if (this.elements[0].combinator.value === "") {
3716        this._css = ' ';
3717    } else {
3718        this._css = '';
3719    }
3720   
3721    this._css += this.elements.map(function (e) {
3722        if (typeof(e) === 'string') {
3723            return ' ' + e.trim();
3724        } else {
3725            return e.toCSS(env);
3726        }
3727    }).join('');
3728   
3729    return this._css;
3730};
3731
3732})(require('../tree'));
3733(function (tree) {
3734
3735tree.UnicodeDescriptor = function (value) {
3736    this.value = value;
3737};
3738tree.UnicodeDescriptor.prototype = {
3739    toCSS: function (env) {
3740        return this.value;
3741    },
3742    eval: function () { return this }
3743};
3744
3745})(require('../tree'));
3746(function (tree) {
3747
3748tree.URL = function (val, rootpath) {
3749    this.value = val;
3750    this.rootpath = rootpath;
3751};
3752tree.URL.prototype = {
3753    toCSS: function () {
3754        return "url(" + this.value.toCSS() + ")";
3755    },
3756    eval: function (ctx) {
3757        var val = this.value.eval(ctx), rootpath;
3758
3759        // Add the base path if the URL is relative
3760        if (typeof val.value === "string" && !/^(?:[a-z-]+:|\/)/.test(val.value)) {
3761            rootpath = this.rootpath;
3762            if (!val.quote) {
3763                rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; });
3764            }
3765            val.value = rootpath + val.value;
3766        }
3767
3768        return new(tree.URL)(val, this.rootpath);
3769    }
3770};
3771
3772})(require('../tree'));
3773(function (tree) {
3774
3775tree.Value = function (value) {
3776    this.value = value;
3777    this.is = 'value';
3778};
3779tree.Value.prototype = {
3780    eval: function (env) {
3781        if (this.value.length === 1) {
3782            return this.value[0].eval(env);
3783        } else {
3784            return new(tree.Value)(this.value.map(function (v) {
3785                return v.eval(env);
3786            }));
3787        }
3788    },
3789    toCSS: function (env) {
3790        return this.value.map(function (e) {
3791            return e.toCSS(env);
3792        }).join(env.compress ? ',' : ', ');
3793    }
3794};
3795
3796})(require('../tree'));
3797(function (tree) {
3798
3799tree.Variable = function (name, index, file) { this.name = name, this.index = index, this.file = file };
3800tree.Variable.prototype = {
3801    eval: function (env) {
3802        var variable, v, name = this.name;
3803
3804        if (name.indexOf('@@') == 0) {
3805            name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value;
3806        }
3807       
3808        if (this.evaluating) {
3809            throw { type: 'Name',
3810                    message: "Recursive variable definition for " + name,
3811                    filename: this.file,
3812                    index: this.index };
3813        }
3814       
3815        this.evaluating = true;
3816
3817        if (variable = tree.find(env.frames, function (frame) {
3818            if (v = frame.variable(name)) {
3819                return v.value.eval(env);
3820            }
3821        })) {
3822            this.evaluating = false;
3823            return variable;
3824        }
3825        else {
3826            throw { type: 'Name',
3827                    message: "variable " + name + " is undefined",
3828                    filename: this.file,
3829                    index: this.index };
3830        }
3831    }
3832};
3833
3834})(require('../tree'));
3835(function (tree) {
3836
3837tree.debugInfo = function(env, ctx) {
3838    var result="";
3839    if (env.dumpLineNumbers && !env.compress) {
3840        switch(env.dumpLineNumbers) {
3841            case 'comments':
3842                result = tree.debugInfo.asComment(ctx);
3843                break;
3844            case 'mediaquery':
3845                result = tree.debugInfo.asMediaQuery(ctx);
3846                break;
3847            case 'all':
3848                result = tree.debugInfo.asComment(ctx)+tree.debugInfo.asMediaQuery(ctx);
3849                break;
3850        }
3851    }
3852    return result;
3853};
3854
3855tree.debugInfo.asComment = function(ctx) {
3856    return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n';
3857};
3858
3859tree.debugInfo.asMediaQuery = function(ctx) {
3860    return '@media -sass-debug-info{filename{font-family:' +
3861        ('file://' + ctx.debugInfo.fileName).replace(/[\/:.]/g, '\\$&') +
3862        '}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n';
3863};
3864
3865tree.find = function (obj, fun) {
3866    for (var i = 0, r; i < obj.length; i++) {
3867        if (r = fun.call(obj, obj[i])) { return r }
3868    }
3869    return null;
3870};
3871tree.jsify = function (obj) {
3872    if (Array.isArray(obj.value) && (obj.value.length > 1)) {
3873        return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']';
3874    } else {
3875        return obj.toCSS(false);
3876    }
3877};
3878
3879})(require('./tree'));
3880var name;
3881
3882function loadStyleSheet(sheet, callback, reload, remaining) {
3883    var endOfPath = Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')),
3884        sheetName = name.slice(0, endOfPath + 1) + sheet.href,
3885        contents = sheet.contents || {},
3886        input = readFile(sheetName);
3887       
3888    contents[sheetName] = input;
3889       
3890    var parser = new less.Parser({
3891        paths: [sheet.href.replace(/[\w\.-]+$/, '')],
3892        contents: contents
3893    });
3894    parser.parse(input, function (e, root) {
3895        if (e) {
3896            return error(e, sheetName);
3897        }
3898        try {
3899            callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName);
3900        } catch(e) {
3901            error(e, sheetName);
3902        }
3903    });
3904}
3905
3906function writeFile(filename, content) {
3907    var fstream = new java.io.FileWriter(filename);
3908    var out = new java.io.BufferedWriter(fstream);
3909    out.write(content);
3910    out.close();
3911}
3912
3913// Command line integration via Rhino
3914(function (args) {
3915    var output,
3916        compress = false,
3917        i;
3918       
3919    for(i = 0; i < args.length; i++) {
3920        switch(args[i]) {
3921            case "-x":
3922                compress = true;
3923                break;
3924            default:
3925                if (!name) {
3926                    name = args[i];
3927                } else if (!output) {
3928                    output = args[i];
3929                } else {
3930                    print("unrecognised parameters");
3931                    print("input_file [output_file] [-x]");
3932                }
3933        }
3934    }
3935
3936    if (!name) {
3937        print('No files present in the fileset; Check your pattern match in build.xml');
3938        quit(1);
3939    }
3940    path = name.split("/");path.pop();path=path.join("/")
3941
3942    var input = readFile(name);
3943
3944    if (!input) {
3945        print('lesscss: couldn\'t open file ' + name);
3946        quit(1);
3947    }
3948
3949    var result;
3950    try {
3951        var parser = new less.Parser();
3952        parser.parse(input, function (e, root) {
3953            if (e) {
3954                error(e, name);
3955                quit(1);
3956            } else {
3957                result = root.toCSS({compress: compress || false});
3958                if (output) {
3959                    writeFile(output, result);
3960                    print("Written to " + output);
3961                } else {
3962                    print(result);
3963                }
3964                quit(0);
3965            }
3966        });
3967    }
3968    catch(e) {
3969        error(e, name);
3970        quit(1);
3971    }
3972    print("done");
3973}(arguments));
3974
3975function error(e, filename) {
3976
3977    var content = "Error : " + filename + "\n";
3978   
3979    filename = e.filename || filename;
3980   
3981    if (e.message) {
3982        content += e.message + "\n";
3983    }
3984
3985    var errorline = function (e, i, classname) {
3986        if (e.extract[i]) {
3987            content +=
3988                String(parseInt(e.line) + (i - 1)) +
3989                ":" + e.extract[i] + "\n";
3990        }
3991    };
3992
3993    if (e.stack) {
3994        content += e.stack;
3995    } else if (e.extract) {
3996        content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n';
3997        errorline(e, 0);
3998        errorline(e, 1);
3999        errorline(e, 2);
4000    }
4001   print(content);
4002}
Note: See TracBrowser for help on using the repository browser.