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

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

Added Dojo 1.9.3 release.

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