source: Dev/trunk/src/client/util/less/dist/less-1.1.6.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: 98.2 KB
Line 
1//
2// LESS - Leaner CSS v1.1.6
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    less = {};
142    tree = less.tree = {};
143    less.mode = 'rhino';
144} else if (typeof(window) === 'undefined') {
145    // Node.js
146    less = exports,
147    tree = require('./tree');
148    less.mode = 'node';
149} else {
150    // Browser
151    if (typeof(window.less) === 'undefined') { window.less = {} }
152    less = window.less,
153    tree = window.less.tree = {};
154    less.mode = 'browser';
155}
156//
157// less.js - parser
158//
159//    A relatively straight-forward predictive parser.
160//    There is no tokenization/lexing stage, the input is parsed
161//    in one sweep.
162//
163//    To make the parser fast enough to run in the browser, several
164//    optimization had to be made:
165//
166//    - Matching and slicing on a huge input is often cause of slowdowns.
167//      The solution is to chunkify the input into smaller strings.
168//      The chunks are stored in the `chunks` var,
169//      `j` holds the current chunk index, and `current` holds
170//      the index of the current chunk in relation to `input`.
171//      This gives us an almost 4x speed-up.
172//
173//    - In many cases, we don't need to match individual tokens;
174//      for example, if a value doesn't hold any variables, operations
175//      or dynamic references, the parser can effectively 'skip' it,
176//      treating it as a literal.
177//      An example would be '1px solid #000' - which evaluates to itself,
178//      we don't need to know what the individual components are.
179//      The drawback, of course is that you don't get the benefits of
180//      syntax-checking on the CSS. This gives us a 50% speed-up in the parser,
181//      and a smaller speed-up in the code-gen.
182//
183//
184//    Token matching is done with the `$` function, which either takes
185//    a terminal string or regexp, or a non-terminal function to call.
186//    It also takes care of moving all the indices forwards.
187//
188//
189less.Parser = function Parser(env) {
190    var input,       // LeSS input string
191        i,           // current index in `input`
192        j,           // current chunk
193        temp,        // temporarily holds a chunk's state, for backtracking
194        memo,        // temporarily holds `i`, when backtracking
195        furthest,    // furthest index the parser has gone to
196        chunks,      // chunkified input
197        current,     // index of current chunk, in `input`
198        parser;
199
200    var that = this;
201
202    // This function is called after all files
203    // have been imported through `@import`.
204    var finish = function () {};
205
206    var imports = this.imports = {
207        paths: env && env.paths || [],  // Search paths, when importing
208        queue: [],                      // Files which haven't been imported yet
209        files: {},                      // Holds the imported parse trees
210        mime:  env && env.mime,         // MIME type of .less files
211        push: function (path, callback) {
212            var that = this;
213            this.queue.push(path);
214
215            //
216            // Import a file asynchronously
217            //
218            less.Parser.importer(path, this.paths, function (root) {
219                that.queue.splice(that.queue.indexOf(path), 1); // Remove the path from the queue
220                that.files[path] = root;                        // Store the root
221
222                callback(root);
223
224                if (that.queue.length === 0) { finish() }       // Call `finish` if we're done importing
225            }, env);
226        }
227    };
228
229    function save()    { temp = chunks[j], memo = i, current = i }
230    function restore() { chunks[j] = temp, i = memo, current = i }
231
232    function sync() {
233        if (i > current) {
234            chunks[j] = chunks[j].slice(i - current);
235            current = i;
236        }
237    }
238    //
239    // Parse from a token, regexp or string, and move forward if match
240    //
241    function $(tok) {
242        var match, args, length, c, index, endIndex, k, mem;
243
244        //
245        // Non-terminal
246        //
247        if (tok instanceof Function) {
248            return tok.call(parser.parsers);
249        //
250        // Terminal
251        //
252        //     Either match a single character in the input,
253        //     or match a regexp in the current chunk (chunk[j]).
254        //
255        } else if (typeof(tok) === 'string') {
256            match = input.charAt(i) === tok ? tok : null;
257            length = 1;
258            sync ();
259        } else {
260            sync ();
261
262            if (match = tok.exec(chunks[j])) {
263                length = match[0].length;
264            } else {
265                return null;
266            }
267        }
268
269        // The match is confirmed, add the match length to `i`,
270        // and consume any extra white-space characters (' ' || '\n')
271        // which come after that. The reason for this is that LeSS's
272        // grammar is mostly white-space insensitive.
273        //
274        if (match) {
275            mem = i += length;
276            endIndex = i + chunks[j].length - length;
277
278            while (i < endIndex) {
279                c = input.charCodeAt(i);
280                if (! (c === 32 || c === 10 || c === 9)) { break }
281                i++;
282            }
283            chunks[j] = chunks[j].slice(length + (i - mem));
284            current = i;
285
286            if (chunks[j].length === 0 && j < chunks.length - 1) { j++ }
287
288            if(typeof(match) === 'string') {
289                return match;
290            } else {
291                return match.length === 1 ? match[0] : match;
292            }
293        }
294    }
295
296    // Same as $(), but don't change the state of the parser,
297    // just return the match.
298    function peek(tok) {
299        if (typeof(tok) === 'string') {
300            return input.charAt(i) === tok;
301        } else {
302            if (tok.test(chunks[j])) {
303                return true;
304            } else {
305                return false;
306            }
307        }
308    }
309
310    this.env = env = env || {};
311
312    // The optimization level dictates the thoroughness of the parser,
313    // the lower the number, the less nodes it will create in the tree.
314    // This could matter for debugging, or if you want to access
315    // the individual nodes in the tree.
316    this.optimization = ('optimization' in this.env) ? this.env.optimization : 1;
317
318    this.env.filename = this.env.filename || null;
319
320    //
321    // The Parser
322    //
323    return parser = {
324
325        imports: imports,
326        //
327        // Parse an input string into an abstract syntax tree,
328        // call `callback` when done.
329        //
330        parse: function (str, callback) {
331            var root, start, end, zone, line, lines, buff = [], c, error = null;
332
333            i = j = current = furthest = 0;
334            chunks = [];
335            input = str.replace(/\r\n/g, '\n');
336
337            // Split the input into chunks.
338            chunks = (function (chunks) {
339                var j = 0,
340                    skip = /[^"'`\{\}\/\(\)]+/g,
341                    comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,
342                    level = 0,
343                    match,
344                    chunk = chunks[0],
345                    inParam,
346                    inString;
347
348                for (var i = 0, c, cc; i < input.length; i++) {
349                    skip.lastIndex = i;
350                    if (match = skip.exec(input)) {
351                        if (match.index === i) {
352                            i += match[0].length;
353                            chunk.push(match[0]);
354                        }
355                    }
356                    c = input.charAt(i);
357                    comment.lastIndex = i;
358
359                    if (!inString && !inParam && c === '/') {
360                        cc = input.charAt(i + 1);
361                        if (cc === '/' || cc === '*') {
362                            if (match = comment.exec(input)) {
363                                if (match.index === i) {
364                                    i += match[0].length;
365                                    chunk.push(match[0]);
366                                    c = input.charAt(i);
367                                }
368                            }
369                        }
370                    }
371
372                    if        (c === '{' && !inString && !inParam) { level ++;
373                        chunk.push(c);
374                    } else if (c === '}' && !inString && !inParam) { level --;
375                        chunk.push(c);
376                        chunks[++j] = chunk = [];
377                    } else if (c === '(' && !inString && !inParam) {
378                        chunk.push(c);
379                        inParam = true;
380                    } else if (c === ')' && !inString && inParam) {
381                        chunk.push(c);
382                        inParam = false;
383                    } else {
384                        if (c === '"' || c === "'" || c === '`') {
385                            if (! inString) {
386                                inString = c;
387                            } else {
388                                inString = inString === c ? false : inString;
389                            }
390                        }
391                        chunk.push(c);
392                    }
393                }
394                if (level > 0) {
395                    throw {
396                        type: 'Syntax',
397                        message: "Missing closing `}`",
398                        filename: env.filename
399                    };
400                }
401
402                return chunks.map(function (c) { return c.join('') });;
403            })([[]]);
404
405            // Start with the primary rule.
406            // The whole syntax tree is held under a Ruleset node,
407            // with the `root` property set to true, so no `{}` are
408            // output. The callback is called when the input is parsed.
409            root = new(tree.Ruleset)([], $(this.parsers.primary));
410            root.root = true;
411
412            root.toCSS = (function (evaluate) {
413                var line, lines, column;
414
415                return function (options, variables) {
416                    var frames = [];
417
418                    options = options || {};
419                    //
420                    // Allows setting variables with a hash, so:
421                    //
422                    //   `{ color: new(tree.Color)('#f01') }` will become:
423                    //
424                    //   new(tree.Rule)('@color',
425                    //     new(tree.Value)([
426                    //       new(tree.Expression)([
427                    //         new(tree.Color)('#f01')
428                    //       ])
429                    //     ])
430                    //   )
431                    //
432                    if (typeof(variables) === 'object' && !Array.isArray(variables)) {
433                        variables = Object.keys(variables).map(function (k) {
434                            var value = variables[k];
435
436                            if (! (value instanceof tree.Value)) {
437                                if (! (value instanceof tree.Expression)) {
438                                    value = new(tree.Expression)([value]);
439                                }
440                                value = new(tree.Value)([value]);
441                            }
442                            return new(tree.Rule)('@' + k, value, false, 0);
443                        });
444                        frames = [new(tree.Ruleset)(null, variables)];
445                    }
446
447                    try {
448                        var css = evaluate.call(this, { frames: frames })
449                                          .toCSS([], { compress: options.compress || false });
450                    } catch (e) {
451                        lines = input.split('\n');
452                        line = getLine(e.index);
453
454                        for (var n = e.index, column = -1;
455                                 n >= 0 && input.charAt(n) !== '\n';
456                                 n--) { column++ }
457
458                        throw {
459                            type: e.type,
460                            message: e.message,
461                            filename: env.filename,
462                            index: e.index,
463                            line: typeof(line) === 'number' ? line + 1 : null,
464                            callLine: e.call && (getLine(e.call) + 1),
465                            callExtract: lines[getLine(e.call)],
466                            stack: e.stack,
467                            column: column,
468                            extract: [
469                                lines[line - 1],
470                                lines[line],
471                                lines[line + 1]
472                            ]
473                        };
474                    }
475                    if (options.yuicompress && less.mode === 'node') {
476                        return require('./cssmin').compressor.cssmin(css);
477                    } else if (options.compress) {
478                        return css.replace(/(\s)+/g, "$1");
479                    } else {
480                        return css;
481                    }
482
483                    function getLine(index) {
484                        return index ? (input.slice(0, index).match(/\n/g) || "").length : null;
485                    }
486                };
487            })(root.eval);
488
489            // If `i` is smaller than the `input.length - 1`,
490            // it means the parser wasn't able to parse the whole
491            // string, so we've got a parsing error.
492            //
493            // We try to extract a \n delimited string,
494            // showing the line where the parse error occured.
495            // We split it up into two parts (the part which parsed,
496            // and the part which didn't), so we can color them differently.
497            if (i < input.length - 1) {
498                i = furthest;
499                lines = input.split('\n');
500                line = (input.slice(0, i).match(/\n/g) || "").length + 1;
501
502                for (var n = i, column = -1; n >= 0 && input.charAt(n) !== '\n'; n--) { column++ }
503
504                error = {
505                    name: "ParseError",
506                    message: "Syntax Error on line " + line,
507                    index: i,
508                    filename: env.filename,
509                    line: line,
510                    column: column,
511                    extract: [
512                        lines[line - 2],
513                        lines[line - 1],
514                        lines[line]
515                    ]
516                };
517            }
518
519            if (this.imports.queue.length > 0) {
520                finish = function () { callback(error, root) };
521            } else {
522                callback(error, root);
523            }
524        },
525
526        //
527        // Here in, the parsing rules/functions
528        //
529        // The basic structure of the syntax tree generated is as follows:
530        //
531        //   Ruleset ->  Rule -> Value -> Expression -> Entity
532        //
533        // Here's some LESS code:
534        //
535        //    .class {
536        //      color: #fff;
537        //      border: 1px solid #000;
538        //      width: @w + 4px;
539        //      > .child {...}
540        //    }
541        //
542        // And here's what the parse tree might look like:
543        //
544        //     Ruleset (Selector '.class', [
545        //         Rule ("color",  Value ([Expression [Color #fff]]))
546        //         Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
547        //         Rule ("width",  Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]]))
548        //         Ruleset (Selector [Element '>', '.child'], [...])
549        //     ])
550        //
551        //  In general, most rules will try to parse a token with the `$()` function, and if the return
552        //  value is truly, will return a new node, of the relevant type. Sometimes, we need to check
553        //  first, before parsing, that's when we use `peek()`.
554        //
555        parsers: {
556            //
557            // The `primary` rule is the *entry* and *exit* point of the parser.
558            // The rules here can appear at any level of the parse tree.
559            //
560            // The recursive nature of the grammar is an interplay between the `block`
561            // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
562            // as represented by this simplified grammar:
563            //
564            //     primary  →  (ruleset | rule)+
565            //     ruleset  →  selector+ block
566            //     block    →  '{' primary '}'
567            //
568            // Only at one point is the primary rule not called from the
569            // block rule: at the root level.
570            //
571            primary: function () {
572                var node, root = [];
573
574                while ((node = $(this.mixin.definition) || $(this.rule)    ||  $(this.ruleset) ||
575                               $(this.mixin.call)       || $(this.comment) ||  $(this.directive))
576                               || $(/^[\s\n]+/)) {
577                    node && root.push(node);
578                }
579                return root;
580            },
581
582            // We create a Comment node for CSS comments `/* */`,
583            // but keep the LeSS comments `//` silent, by just skipping
584            // over them.
585            comment: function () {
586                var comment;
587
588                if (input.charAt(i) !== '/') return;
589
590                if (input.charAt(i + 1) === '/') {
591                    return new(tree.Comment)($(/^\/\/.*/), true);
592                } else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) {
593                    return new(tree.Comment)(comment);
594                }
595            },
596
597            //
598            // Entities are tokens which can be found inside an Expression
599            //
600            entities: {
601                //
602                // A string, which supports escaping " and '
603                //
604                //     "milky way" 'he\'s the one!'
605                //
606                quoted: function () {
607                    var str, j = i, e;
608
609                    if (input.charAt(j) === '~') { j++, e = true } // Escaped strings
610                    if (input.charAt(j) !== '"' && input.charAt(j) !== "'") return;
611
612                    e && $('~');
613
614                    if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) {
615                        return new(tree.Quoted)(str[0], str[1] || str[2], e);
616                    }
617                },
618
619                //
620                // A catch-all word, such as:
621                //
622                //     black border-collapse
623                //
624                keyword: function () {
625                    var k;
626
627                    if (k = $(/^[_A-Za-z-][_A-Za-z0-9-]*/)) {
628                        if (tree.colors.hasOwnProperty(k)) {
629                            // detect named color
630                            return new(tree.Color)(tree.colors[k].slice(1));
631                        } else {
632                            return new(tree.Keyword)(k)
633                        }
634                    }
635                },
636
637                //
638                // A function call
639                //
640                //     rgb(255, 0, 255)
641                //
642                // We also try to catch IE's `alpha()`, but let the `alpha` parser
643                // deal with the details.
644                //
645                // The arguments are parsed with the `entities.arguments` parser.
646                //
647                call: function () {
648                    var name, args, index = i;
649
650                    if (! (name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(chunks[j]))) return;
651
652                    name = name[1].toLowerCase();
653
654                    if (name === 'url') { return null }
655                    else                { i += name.length }
656
657                    if (name === 'alpha') { return $(this.alpha) }
658
659                    $('('); // Parse the '(' and consume whitespace.
660
661                    args = $(this.entities.arguments);
662
663                    if (! $(')')) return;
664
665                    if (name) { return new(tree.Call)(name, args, index) }
666                },
667                arguments: function () {
668                    var args = [], arg;
669
670                    while (arg = $(this.entities.assignment) || $(this.expression)) {
671                        args.push(arg);
672                        if (! $(',')) { break }
673                    }
674                    return args;
675                },
676                literal: function () {
677                    return $(this.entities.dimension) ||
678                           $(this.entities.color) ||
679                           $(this.entities.quoted);
680                },
681
682                // Assignments are argument entities for calls.
683                // They are present in ie filter properties as shown below.
684                //
685                //     filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
686                //
687
688                assignment: function () {
689                    var key, value;
690                    if ((key = $(/^\w+(?=\s?=)/i)) && $('=') && (value = $(this.entity))) {
691                        return new(tree.Assignment)(key, value);
692                    }
693                },
694
695                //
696                // Parse url() tokens
697                //
698                // We use a specific rule for urls, because they don't really behave like
699                // standard function calls. The difference is that the argument doesn't have
700                // to be enclosed within a string, so it can't be parsed as an Expression.
701                //
702                url: function () {
703                    var value;
704
705                    if (input.charAt(i) !== 'u' || !$(/^url\(/)) return;
706                    value = $(this.entities.quoted)  || $(this.entities.variable) ||
707                            $(this.entities.dataURI) || $(/^[-\w%@$\/.&=:;#+?~]+/) || "";
708                    if (! $(')')) throw new(Error)("missing closing ) for url()");
709
710                    return new(tree.URL)((value.value || value.data || value instanceof tree.Variable)
711                                        ? value : new(tree.Anonymous)(value), imports.paths);
712                },
713
714                dataURI: function () {
715                    var obj;
716
717                    if ($(/^data:/)) {
718                        obj         = {};
719                        obj.mime    = $(/^[^\/]+\/[^,;)]+/)     || '';
720                        obj.charset = $(/^;\s*charset=[^,;)]+/) || '';
721                        obj.base64  = $(/^;\s*base64/)          || '';
722                        obj.data    = $(/^,\s*[^)]+/);
723
724                        if (obj.data) { return obj }
725                    }
726                },
727
728                //
729                // A Variable entity, such as `@fink`, in
730                //
731                //     width: @fink + 2px
732                //
733                // We use a different parser for variable definitions,
734                // see `parsers.variable`.
735                //
736                variable: function () {
737                    var name, index = i;
738
739                    if (input.charAt(i) === '@' && (name = $(/^@@?[\w-]+/))) {
740                        return new(tree.Variable)(name, index);
741                    }
742                },
743
744                //
745                // A Hexadecimal color
746                //
747                //     #4F3C2F
748                //
749                // `rgb` and `hsl` colors are parsed through the `entities.call` parser.
750                //
751                color: function () {
752                    var rgb;
753
754                    if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) {
755                        return new(tree.Color)(rgb[1]);
756                    }
757                },
758
759                //
760                // A Dimension, that is, a number and a unit
761                //
762                //     0.5em 95%
763                //
764                dimension: function () {
765                    var value, c = input.charCodeAt(i);
766                    if ((c > 57 || c < 45) || c === 47) return;
767
768                    if (value = $(/^(-?\d*\.?\d+)(px|%|em|rem|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/)) {
769                        return new(tree.Dimension)(value[1], value[2]);
770                    }
771                },
772
773                //
774                // JavaScript code to be evaluated
775                //
776                //     `window.location.href`
777                //
778                javascript: function () {
779                    var str, j = i, e;
780
781                    if (input.charAt(j) === '~') { j++, e = true } // Escaped strings
782                    if (input.charAt(j) !== '`') { return }
783
784                    e && $('~');
785
786                    if (str = $(/^`([^`]*)`/)) {
787                        return new(tree.JavaScript)(str[1], i, e);
788                    }
789                }
790            },
791
792            //
793            // The variable part of a variable definition. Used in the `rule` parser
794            //
795            //     @fink:
796            //
797            variable: function () {
798                var name;
799
800                if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] }
801            },
802
803            //
804            // A font size/line-height shorthand
805            //
806            //     small/12px
807            //
808            // We need to peek first, or we'll match on keywords and dimensions
809            //
810            shorthand: function () {
811                var a, b;
812
813                if (! peek(/^[@\w.%-]+\/[@\w.-]+/)) return;
814
815                if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) {
816                    return new(tree.Shorthand)(a, b);
817                }
818            },
819
820            //
821            // Mixins
822            //
823            mixin: {
824                //
825                // A Mixin call, with an optional argument list
826                //
827                //     #mixins > .square(#fff);
828                //     .rounded(4px, black);
829                //     .button;
830                //
831                // The `while` loop is there because mixins can be
832                // namespaced, but we only support the child and descendant
833                // selector for now.
834                //
835                call: function () {
836                    var elements = [], e, c, args, index = i, s = input.charAt(i);
837
838                    if (s !== '.' && s !== '#') { return }
839
840                    while (e = $(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)) {
841                        elements.push(new(tree.Element)(c, e, i));
842                        c = $('>');
843                    }
844                    $('(') && (args = $(this.entities.arguments)) && $(')');
845
846                    if (elements.length > 0 && ($(';') || peek('}'))) {
847                        return new(tree.mixin.Call)(elements, args, index);
848                    }
849                },
850
851                //
852                // A Mixin definition, with a list of parameters
853                //
854                //     .rounded (@radius: 2px, @color) {
855                //        ...
856                //     }
857                //
858                // Until we have a finer grained state-machine, we have to
859                // do a look-ahead, to make sure we don't have a mixin call.
860                // See the `rule` function for more information.
861                //
862                // We start by matching `.rounded (`, and then proceed on to
863                // the argument list, which has optional default values.
864                // We store the parameters in `params`, with a `value` key,
865                // if there is a value, such as in the case of `@radius`.
866                //
867                // Once we've got our params list, and a closing `)`, we parse
868                // the `{...}` block.
869                //
870                definition: function () {
871                    var name, params = [], match, ruleset, param, value;
872
873                    if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') ||
874                        peek(/^[^{]*(;|})/)) return;
875
876                    if (match = $(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)) {
877                        name = match[1];
878
879                        while (param = $(this.entities.variable) || $(this.entities.literal)
880                                                                 || $(this.entities.keyword)) {
881                            // Variable
882                            if (param instanceof tree.Variable) {
883                                if ($(':')) {
884                                    if (value = $(this.expression)) {
885                                        params.push({ name: param.name, value: value });
886                                    } else {
887                                        throw new(Error)("Expected value");
888                                    }
889                                } else {
890                                    params.push({ name: param.name });
891                                }
892                            } else {
893                                params.push({ value: param });
894                            }
895                            if (! $(',')) { break }
896                        }
897                        if (! $(')')) throw new(Error)("Expected )");
898
899                        ruleset = $(this.block);
900
901                        if (ruleset) {
902                            return new(tree.mixin.Definition)(name, params, ruleset);
903                        }
904                    }
905                }
906            },
907
908            //
909            // Entities are the smallest recognized token,
910            // and can be found inside a rule's value.
911            //
912            entity: function () {
913                return $(this.entities.literal) || $(this.entities.variable) || $(this.entities.url) ||
914                       $(this.entities.call)    || $(this.entities.keyword)  || $(this.entities.javascript) ||
915                       $(this.comment);
916            },
917
918            //
919            // A Rule terminator. Note that we use `peek()` to check for '}',
920            // because the `block` rule will be expecting it, but we still need to make sure
921            // it's there, if ';' was ommitted.
922            //
923            end: function () {
924                return $(';') || peek('}');
925            },
926
927            //
928            // IE's alpha function
929            //
930            //     alpha(opacity=88)
931            //
932            alpha: function () {
933                var value;
934
935                if (! $(/^\(opacity=/i)) return;
936                if (value = $(/^\d+/) || $(this.entities.variable)) {
937                    if (! $(')')) throw new(Error)("missing closing ) for alpha()");
938                    return new(tree.Alpha)(value);
939                }
940            },
941
942            //
943            // A Selector Element
944            //
945            //     div
946            //     + h1
947            //     #socks
948            //     input[type="text"]
949            //
950            // Elements are the building blocks for Selectors,
951            // they are made out of a `Combinator` (see combinator rule),
952            // and an element name, such as a tag a class, or `*`.
953            //
954            element: function () {
955                var e, t, c;
956
957                c = $(this.combinator);
958                e = $(/^(?:\d+\.\d+|\d+)%/) || $(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/) ||
959                    $('*') || $(this.attribute) || $(/^\([^)@]+\)/);
960
961                if (e) { return new(tree.Element)(c, e, i) }
962
963                if (c.value && c.value.charAt(0) === '&') {
964                    return new(tree.Element)(c, null, i);
965                }
966            },
967
968            //
969            // Combinators combine elements together, in a Selector.
970            //
971            // Because our parser isn't white-space sensitive, special care
972            // has to be taken, when parsing the descendant combinator, ` `,
973            // as it's an empty space. We have to check the previous character
974            // in the input, to see if it's a ` ` character. More info on how
975            // we deal with this in *combinator.js*.
976            //
977            combinator: function () {
978                var match, c = input.charAt(i);
979
980                if (c === '>' || c === '+' || c === '~') {
981                    i++;
982                    while (input.charAt(i) === ' ') { i++ }
983                    return new(tree.Combinator)(c);
984                } else if (c === '&') {
985                    match = '&';
986                    i++;
987                    if(input.charAt(i) === ' ') {
988                        match = '& ';
989                    }
990                    while (input.charAt(i) === ' ') { i++ }
991                    return new(tree.Combinator)(match);
992                } else if (c === ':' && input.charAt(i + 1) === ':') {
993                    i += 2;
994                    while (input.charAt(i) === ' ') { i++ }
995                    return new(tree.Combinator)('::');
996                } else if (input.charAt(i - 1) === ' ') {
997                    return new(tree.Combinator)(" ");
998                } else {
999                    return new(tree.Combinator)(null);
1000                }
1001            },
1002
1003            //
1004            // A CSS Selector
1005            //
1006            //     .class > div + h1
1007            //     li a:hover
1008            //
1009            // Selectors are made out of one or more Elements, see above.
1010            //
1011            selector: function () {
1012                var sel, e, elements = [], c, match;
1013
1014                while (e = $(this.element)) {
1015                    c = input.charAt(i);
1016                    elements.push(e)
1017                    if (c === '{' || c === '}' || c === ';' || c === ',') { break }
1018                }
1019
1020                if (elements.length > 0) { return new(tree.Selector)(elements) }
1021            },
1022            tag: function () {
1023                return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*');
1024            },
1025            attribute: function () {
1026                var attr = '', key, val, op;
1027
1028                if (! $('[')) return;
1029
1030                if (key = $(/^[a-zA-Z-]+/) || $(this.entities.quoted)) {
1031                    if ((op = $(/^[|~*$^]?=/)) &&
1032                        (val = $(this.entities.quoted) || $(/^[\w-]+/))) {
1033                        attr = [key, op, val.toCSS ? val.toCSS() : val].join('');
1034                    } else { attr = key }
1035                }
1036
1037                if (! $(']')) return;
1038
1039                if (attr) { return "[" + attr + "]" }
1040            },
1041
1042            //
1043            // The `block` rule is used by `ruleset` and `mixin.definition`.
1044            // It's a wrapper around the `primary` rule, with added `{}`.
1045            //
1046            block: function () {
1047                var content;
1048
1049                if ($('{') && (content = $(this.primary)) && $('}')) {
1050                    return content;
1051                }
1052            },
1053
1054            //
1055            // div, .class, body > p {...}
1056            //
1057            ruleset: function () {
1058                var selectors = [], s, rules, match;
1059                save();
1060
1061                while (s = $(this.selector)) {
1062                    selectors.push(s);
1063                    $(this.comment);
1064                    if (! $(',')) { break }
1065                    $(this.comment);
1066                }
1067
1068                if (selectors.length > 0 && (rules = $(this.block))) {
1069                    return new(tree.Ruleset)(selectors, rules);
1070                } else {
1071                    // Backtrack
1072                    furthest = i;
1073                    restore();
1074                }
1075            },
1076            rule: function () {
1077                var name, value, c = input.charAt(i), important, match;
1078                save();
1079
1080                if (c === '.' || c === '#' || c === '&') { return }
1081
1082                if (name = $(this.variable) || $(this.property)) {
1083                    if ((name.charAt(0) != '@') && (match = /^([^@+\/'"*`(;{}-]*);/.exec(chunks[j]))) {
1084                        i += match[0].length - 1;
1085                        value = new(tree.Anonymous)(match[1]);
1086                    } else if (name === "font") {
1087                        value = $(this.font);
1088                    } else {
1089                        value = $(this.value);
1090                    }
1091                    important = $(this.important);
1092
1093                    if (value && $(this.end)) {
1094                        return new(tree.Rule)(name, value, important, memo);
1095                    } else {
1096                        furthest = i;
1097                        restore();
1098                    }
1099                }
1100            },
1101
1102            //
1103            // An @import directive
1104            //
1105            //     @import "lib";
1106            //
1107            // Depending on our environemnt, importing is done differently:
1108            // In the browser, it's an XHR request, in Node, it would be a
1109            // file-system operation. The function used for importing is
1110            // stored in `import`, which we pass to the Import constructor.
1111            //
1112            "import": function () {
1113                var path;
1114                if ($(/^@import\s+/) &&
1115                    (path = $(this.entities.quoted) || $(this.entities.url)) &&
1116                    $(';')) {
1117                    return new(tree.Import)(path, imports);
1118                }
1119            },
1120
1121            //
1122            // A CSS Directive
1123            //
1124            //     @charset "utf-8";
1125            //
1126            directive: function () {
1127                var name, value, rules, types;
1128
1129                if (input.charAt(i) !== '@') return;
1130
1131                if (value = $(this['import'])) {
1132                    return value;
1133                } else if (name = $(/^@media|@page/) || $(/^@(?:-webkit-|-moz-|-o-|-ms-)[a-z0-9-]+/) || $('keyframes')) {
1134                    types = ($(/^[^{]+/) || '').trim();
1135                    if (rules = $(this.block)) {
1136                        return new(tree.Directive)(name + " " + types, rules);
1137                    }
1138                } else if (name = $(/^@[-a-z]+/)) {
1139                    if (name === '@font-face') {
1140                        if (rules = $(this.block)) {
1141                            return new(tree.Directive)(name, rules);
1142                        }
1143                    } else if ((value = $(this.entity)) && $(';')) {
1144                        return new(tree.Directive)(name, value);
1145                    }
1146                }
1147            },
1148            font: function () {
1149                var value = [], expression = [], weight, shorthand, font, e;
1150
1151                while (e = $(this.shorthand) || $(this.entity)) {
1152                    expression.push(e);
1153                }
1154                value.push(new(tree.Expression)(expression));
1155
1156                if ($(',')) {
1157                    while (e = $(this.expression)) {
1158                        value.push(e);
1159                        if (! $(',')) { break }
1160                    }
1161                }
1162                return new(tree.Value)(value);
1163            },
1164
1165            //
1166            // A Value is a comma-delimited list of Expressions
1167            //
1168            //     font-family: Baskerville, Georgia, serif;
1169            //
1170            // In a Rule, a Value represents everything after the `:`,
1171            // and before the `;`.
1172            //
1173            value: function () {
1174                var e, expressions = [], important;
1175
1176                while (e = $(this.expression)) {
1177                    expressions.push(e);
1178                    if (! $(',')) { break }
1179                }
1180
1181                if (expressions.length > 0) {
1182                    return new(tree.Value)(expressions);
1183                }
1184            },
1185            important: function () {
1186                if (input.charAt(i) === '!') {
1187                    return $(/^! *important/);
1188                }
1189            },
1190            sub: function () {
1191                var e;
1192
1193                if ($('(') && (e = $(this.expression)) && $(')')) {
1194                    return e;
1195                }
1196            },
1197            multiplication: function () {
1198                var m, a, op, operation;
1199                if (m = $(this.operand)) {
1200                    while (!peek(/^\/\*/) && (op = ($('/') || $('*'))) && (a = $(this.operand))) {
1201                        operation = new(tree.Operation)(op, [operation || m, a]);
1202                    }
1203                    return operation || m;
1204                }
1205            },
1206            addition: function () {
1207                var m, a, op, operation;
1208                if (m = $(this.multiplication)) {
1209                    while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) &&
1210                           (a = $(this.multiplication))) {
1211                        operation = new(tree.Operation)(op, [operation || m, a]);
1212                    }
1213                    return operation || m;
1214                }
1215            },
1216
1217            //
1218            // An operand is anything that can be part of an operation,
1219            // such as a Color, or a Variable
1220            //
1221            operand: function () {
1222                var negate, p = input.charAt(i + 1);
1223
1224                if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $('-') }
1225                var o = $(this.sub) || $(this.entities.dimension) ||
1226                        $(this.entities.color) || $(this.entities.variable) ||
1227                        $(this.entities.call);
1228                return negate ? new(tree.Operation)('*', [new(tree.Dimension)(-1), o])
1229                              : o;
1230            },
1231
1232            //
1233            // Expressions either represent mathematical operations,
1234            // or white-space delimited Entities.
1235            //
1236            //     1px solid black
1237            //     @var * 2
1238            //
1239            expression: function () {
1240                var e, delim, entities = [], d;
1241
1242                while (e = $(this.addition) || $(this.entity)) {
1243                    entities.push(e);
1244                }
1245                if (entities.length > 0) {
1246                    return new(tree.Expression)(entities);
1247                }
1248            },
1249            property: function () {
1250                var name;
1251
1252                if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) {
1253                    return name[1];
1254                }
1255            }
1256        }
1257    };
1258};
1259
1260if (less.mode === 'browser' || less.mode === 'rhino') {
1261    //
1262    // Used by `@import` directives
1263    //
1264    less.Parser.importer = function (path, paths, callback, env) {
1265        if (path.charAt(0) !== '/' && paths.length > 0) {
1266            path = paths[0] + path;
1267        }
1268        // We pass `true` as 3rd argument, to force the reload of the import.
1269        // This is so we can get the syntax tree as opposed to just the CSS output,
1270        // as we need this to evaluate the current stylesheet.
1271        loadStyleSheet({ href: path, title: path, type: env.mime }, callback, true);
1272    };
1273}
1274
1275(function (tree) {
1276
1277tree.functions = {
1278    rgb: function (r, g, b) {
1279        return this.rgba(r, g, b, 1.0);
1280    },
1281    rgba: function (r, g, b, a) {
1282        var rgb = [r, g, b].map(function (c) { return number(c) }),
1283            a = number(a);
1284        return new(tree.Color)(rgb, a);
1285    },
1286    hsl: function (h, s, l) {
1287        return this.hsla(h, s, l, 1.0);
1288    },
1289    hsla: function (h, s, l, a) {
1290        h = (number(h) % 360) / 360;
1291        s = number(s); l = number(l); a = number(a);
1292
1293        var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
1294        var m1 = l * 2 - m2;
1295
1296        return this.rgba(hue(h + 1/3) * 255,
1297                         hue(h)       * 255,
1298                         hue(h - 1/3) * 255,
1299                         a);
1300
1301        function hue(h) {
1302            h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
1303            if      (h * 6 < 1) return m1 + (m2 - m1) * h * 6;
1304            else if (h * 2 < 1) return m2;
1305            else if (h * 3 < 2) return m1 + (m2 - m1) * (2/3 - h) * 6;
1306            else                return m1;
1307        }
1308    },
1309    hue: function (color) {
1310        return new(tree.Dimension)(Math.round(color.toHSL().h));
1311    },
1312    saturation: function (color) {
1313        return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%');
1314    },
1315    lightness: function (color) {
1316        return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%');
1317    },
1318    alpha: function (color) {
1319        return new(tree.Dimension)(color.toHSL().a);
1320    },
1321    saturate: function (color, amount) {
1322        var hsl = color.toHSL();
1323
1324        hsl.s += amount.value / 100;
1325        hsl.s = clamp(hsl.s);
1326        return hsla(hsl);
1327    },
1328    desaturate: function (color, amount) {
1329        var hsl = color.toHSL();
1330
1331        hsl.s -= amount.value / 100;
1332        hsl.s = clamp(hsl.s);
1333        return hsla(hsl);
1334    },
1335    lighten: function (color, amount) {
1336        var hsl = color.toHSL();
1337
1338        hsl.l += amount.value / 100;
1339        hsl.l = clamp(hsl.l);
1340        return hsla(hsl);
1341    },
1342    darken: function (color, amount) {
1343        var hsl = color.toHSL();
1344
1345        hsl.l -= amount.value / 100;
1346        hsl.l = clamp(hsl.l);
1347        return hsla(hsl);
1348    },
1349    fadein: function (color, amount) {
1350        var hsl = color.toHSL();
1351
1352        hsl.a += amount.value / 100;
1353        hsl.a = clamp(hsl.a);
1354        return hsla(hsl);
1355    },
1356    fadeout: function (color, amount) {
1357        var hsl = color.toHSL();
1358
1359        hsl.a -= amount.value / 100;
1360        hsl.a = clamp(hsl.a);
1361        return hsla(hsl);
1362    },
1363    fade: function (color, amount) {
1364        var hsl = color.toHSL();
1365
1366        hsl.a = amount.value / 100;
1367        hsl.a = clamp(hsl.a);
1368        return hsla(hsl);
1369    },
1370    spin: function (color, amount) {
1371        var hsl = color.toHSL();
1372        var hue = (hsl.h + amount.value) % 360;
1373
1374        hsl.h = hue < 0 ? 360 + hue : hue;
1375
1376        return hsla(hsl);
1377    },
1378    //
1379    // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
1380    // http://sass-lang.com
1381    //
1382    mix: function (color1, color2, weight) {
1383        var p = weight.value / 100.0;
1384        var w = p * 2 - 1;
1385        var a = color1.toHSL().a - color2.toHSL().a;
1386
1387        var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
1388        var w2 = 1 - w1;
1389
1390        var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2,
1391                   color1.rgb[1] * w1 + color2.rgb[1] * w2,
1392                   color1.rgb[2] * w1 + color2.rgb[2] * w2];
1393
1394        var alpha = color1.alpha * p + color2.alpha * (1 - p);
1395
1396        return new(tree.Color)(rgb, alpha);
1397    },
1398    greyscale: function (color) {
1399        return this.desaturate(color, new(tree.Dimension)(100));
1400    },
1401    e: function (str) {
1402        return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str);
1403    },
1404    escape: function (str) {
1405        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"));
1406    },
1407    '%': function (quoted /* arg, arg, ...*/) {
1408        var args = Array.prototype.slice.call(arguments, 1),
1409            str = quoted.value;
1410
1411        for (var i = 0; i < args.length; i++) {
1412            str = str.replace(/%[sda]/i, function(token) {
1413                var value = token.match(/s/i) ? args[i].value : args[i].toCSS();
1414                return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value;
1415            });
1416        }
1417        str = str.replace(/%%/g, '%');
1418        return new(tree.Quoted)('"' + str + '"', str);
1419    },
1420    round: function (n) {
1421        return this._math('round', n);
1422    },
1423    ceil: function (n) {
1424        return this._math('ceil', n);
1425    },
1426    floor: function (n) {
1427        return this._math('floor', n);
1428    },
1429    _math: function (fn, n) {
1430        if (n instanceof tree.Dimension) {
1431            return new(tree.Dimension)(Math[fn](number(n)), n.unit);
1432        } else if (typeof(n) === 'number') {
1433            return Math[fn](n);
1434        } else {
1435            throw {
1436                error: "RuntimeError",
1437                message: "math functions take numbers as parameters"
1438            };
1439        }
1440    },
1441    argb: function (color) {
1442        return new(tree.Anonymous)(color.toARGB());
1443
1444    }
1445};
1446
1447function hsla(hsla) {
1448    return tree.functions.hsla(hsla.h, hsla.s, hsla.l, hsla.a);
1449}
1450
1451function number(n) {
1452    if (n instanceof tree.Dimension) {
1453        return parseFloat(n.unit == '%' ? n.value / 100 : n.value);
1454    } else if (typeof(n) === 'number') {
1455        return n;
1456    } else {
1457        throw {
1458            error: "RuntimeError",
1459            message: "color functions take numbers as parameters"
1460        };
1461    }
1462}
1463
1464function clamp(val) {
1465    return Math.min(1, Math.max(0, val));
1466}
1467
1468})(require('./tree'));
1469(function (tree) {
1470    tree.colors = {
1471        'aliceblue':'#f0f8ff',
1472        'antiquewhite':'#faebd7',
1473        'aqua':'#00ffff',
1474        'aquamarine':'#7fffd4',
1475        'azure':'#f0ffff',
1476        'beige':'#f5f5dc',
1477        'bisque':'#ffe4c4',
1478        'black':'#000000',
1479        'blanchedalmond':'#ffebcd',
1480        'blue':'#0000ff',
1481        'blueviolet':'#8a2be2',
1482        'brown':'#a52a2a',
1483        'burlywood':'#deb887',
1484        'cadetblue':'#5f9ea0',
1485        'chartreuse':'#7fff00',
1486        'chocolate':'#d2691e',
1487        'coral':'#ff7f50',
1488        'cornflowerblue':'#6495ed',
1489        'cornsilk':'#fff8dc',
1490        'crimson':'#dc143c',
1491        'cyan':'#00ffff',
1492        'darkblue':'#00008b',
1493        'darkcyan':'#008b8b',
1494        'darkgoldenrod':'#b8860b',
1495        'darkgray':'#a9a9a9',
1496        'darkgrey':'#a9a9a9',
1497        'darkgreen':'#006400',
1498        'darkkhaki':'#bdb76b',
1499        'darkmagenta':'#8b008b',
1500        'darkolivegreen':'#556b2f',
1501        'darkorange':'#ff8c00',
1502        'darkorchid':'#9932cc',
1503        'darkred':'#8b0000',
1504        'darksalmon':'#e9967a',
1505        'darkseagreen':'#8fbc8f',
1506        'darkslateblue':'#483d8b',
1507        'darkslategray':'#2f4f4f',
1508        'darkslategrey':'#2f4f4f',
1509        'darkturquoise':'#00ced1',
1510        'darkviolet':'#9400d3',
1511        'deeppink':'#ff1493',
1512        'deepskyblue':'#00bfff',
1513        'dimgray':'#696969',
1514        'dimgrey':'#696969',
1515        'dodgerblue':'#1e90ff',
1516        'firebrick':'#b22222',
1517        'floralwhite':'#fffaf0',
1518        'forestgreen':'#228b22',
1519        'fuchsia':'#ff00ff',
1520        'gainsboro':'#dcdcdc',
1521        'ghostwhite':'#f8f8ff',
1522        'gold':'#ffd700',
1523        'goldenrod':'#daa520',
1524        'gray':'#808080',
1525        'grey':'#808080',
1526        'green':'#008000',
1527        'greenyellow':'#adff2f',
1528        'honeydew':'#f0fff0',
1529        'hotpink':'#ff69b4',
1530        'indianred':'#cd5c5c',
1531        'indigo':'#4b0082',
1532        'ivory':'#fffff0',
1533        'khaki':'#f0e68c',
1534        'lavender':'#e6e6fa',
1535        'lavenderblush':'#fff0f5',
1536        'lawngreen':'#7cfc00',
1537        'lemonchiffon':'#fffacd',
1538        'lightblue':'#add8e6',
1539        'lightcoral':'#f08080',
1540        'lightcyan':'#e0ffff',
1541        'lightgoldenrodyellow':'#fafad2',
1542        'lightgray':'#d3d3d3',
1543        'lightgrey':'#d3d3d3',
1544        'lightgreen':'#90ee90',
1545        'lightpink':'#ffb6c1',
1546        'lightsalmon':'#ffa07a',
1547        'lightseagreen':'#20b2aa',
1548        'lightskyblue':'#87cefa',
1549        'lightslategray':'#778899',
1550        'lightslategrey':'#778899',
1551        'lightsteelblue':'#b0c4de',
1552        'lightyellow':'#ffffe0',
1553        'lime':'#00ff00',
1554        'limegreen':'#32cd32',
1555        'linen':'#faf0e6',
1556        'magenta':'#ff00ff',
1557        'maroon':'#800000',
1558        'mediumaquamarine':'#66cdaa',
1559        'mediumblue':'#0000cd',
1560        'mediumorchid':'#ba55d3',
1561        'mediumpurple':'#9370d8',
1562        'mediumseagreen':'#3cb371',
1563        'mediumslateblue':'#7b68ee',
1564        'mediumspringgreen':'#00fa9a',
1565        'mediumturquoise':'#48d1cc',
1566        'mediumvioletred':'#c71585',
1567        'midnightblue':'#191970',
1568        'mintcream':'#f5fffa',
1569        'mistyrose':'#ffe4e1',
1570        'moccasin':'#ffe4b5',
1571        'navajowhite':'#ffdead',
1572        'navy':'#000080',
1573        'oldlace':'#fdf5e6',
1574        'olive':'#808000',
1575        'olivedrab':'#6b8e23',
1576        'orange':'#ffa500',
1577        'orangered':'#ff4500',
1578        'orchid':'#da70d6',
1579        'palegoldenrod':'#eee8aa',
1580        'palegreen':'#98fb98',
1581        'paleturquoise':'#afeeee',
1582        'palevioletred':'#d87093',
1583        'papayawhip':'#ffefd5',
1584        'peachpuff':'#ffdab9',
1585        'peru':'#cd853f',
1586        'pink':'#ffc0cb',
1587        'plum':'#dda0dd',
1588        'powderblue':'#b0e0e6',
1589        'purple':'#800080',
1590        'red':'#ff0000',
1591        'rosybrown':'#bc8f8f',
1592        'royalblue':'#4169e1',
1593        'saddlebrown':'#8b4513',
1594        'salmon':'#fa8072',
1595        'sandybrown':'#f4a460',
1596        'seagreen':'#2e8b57',
1597        'seashell':'#fff5ee',
1598        'sienna':'#a0522d',
1599        'silver':'#c0c0c0',
1600        'skyblue':'#87ceeb',
1601        'slateblue':'#6a5acd',
1602        'slategray':'#708090',
1603        'slategrey':'#708090',
1604        'snow':'#fffafa',
1605        'springgreen':'#00ff7f',
1606        'steelblue':'#4682b4',
1607        'tan':'#d2b48c',
1608        'teal':'#008080',
1609        'thistle':'#d8bfd8',
1610        'tomato':'#ff6347',
1611        'turquoise':'#40e0d0',
1612        'violet':'#ee82ee',
1613        'wheat':'#f5deb3',
1614        'white':'#ffffff',
1615        'whitesmoke':'#f5f5f5',
1616        'yellow':'#ffff00',
1617        'yellowgreen':'#9acd32'
1618    };
1619})(require('./tree'));
1620(function (tree) {
1621
1622tree.Alpha = function (val) {
1623    this.value = val;
1624};
1625tree.Alpha.prototype = {
1626    toCSS: function () {
1627        return "alpha(opacity=" +
1628               (this.value.toCSS ? this.value.toCSS() : this.value) + ")";
1629    },
1630    eval: function (env) {
1631        if (this.value.eval) { this.value = this.value.eval(env) }
1632        return this;
1633    }
1634};
1635
1636})(require('../tree'));
1637(function (tree) {
1638
1639tree.Anonymous = function (string) {
1640    this.value = string.value || string;
1641};
1642tree.Anonymous.prototype = {
1643    toCSS: function () {
1644        return this.value;
1645    },
1646    eval: function () { return this }
1647};
1648
1649})(require('../tree'));
1650(function (tree) {
1651
1652tree.Assignment = function (key, val) {
1653    this.key = key;
1654    this.value = val;
1655};
1656tree.Assignment.prototype = {
1657    toCSS: function () {
1658        return this.key + '=' + (this.value.toCSS ? this.value.toCSS() : this.value);
1659    },
1660    eval: function (env) {
1661        if (this.value.eval) { this.value = this.value.eval(env) }
1662        return this;
1663    }
1664};
1665
1666})(require('../tree'));(function (tree) {
1667
1668//
1669// A function call node.
1670//
1671tree.Call = function (name, args, index) {
1672    this.name = name;
1673    this.args = args;
1674    this.index = index;
1675};
1676tree.Call.prototype = {
1677    //
1678    // When evaluating a function call,
1679    // we either find the function in `tree.functions` [1],
1680    // in which case we call it, passing the  evaluated arguments,
1681    // or we simply print it out as it appeared originally [2].
1682    //
1683    // The *functions.js* file contains the built-in functions.
1684    //
1685    // The reason why we evaluate the arguments, is in the case where
1686    // we try to pass a variable to a function, like: `saturate(@color)`.
1687    // The function should receive the value, not the variable.
1688    //
1689    eval: function (env) {
1690        var args = this.args.map(function (a) { return a.eval(env) });
1691
1692        if (this.name in tree.functions) { // 1.
1693            try {
1694                return tree.functions[this.name].apply(tree.functions, args);
1695            } catch (e) {
1696                throw { message: "error evaluating function `" + this.name + "`",
1697                        index: this.index };
1698            }
1699        } else { // 2.
1700            return new(tree.Anonymous)(this.name +
1701                   "(" + args.map(function (a) { return a.toCSS() }).join(', ') + ")");
1702        }
1703    },
1704
1705    toCSS: function (env) {
1706        return this.eval(env).toCSS();
1707    }
1708};
1709
1710})(require('../tree'));
1711(function (tree) {
1712//
1713// RGB Colors - #ff0014, #eee
1714//
1715tree.Color = function (rgb, a) {
1716    //
1717    // The end goal here, is to parse the arguments
1718    // into an integer triplet, such as `128, 255, 0`
1719    //
1720    // This facilitates operations and conversions.
1721    //
1722    if (Array.isArray(rgb)) {
1723        this.rgb = rgb;
1724    } else if (rgb.length == 6) {
1725        this.rgb = rgb.match(/.{2}/g).map(function (c) {
1726            return parseInt(c, 16);
1727        });
1728    } else {
1729        this.rgb = rgb.split('').map(function (c) {
1730            return parseInt(c + c, 16);
1731        });
1732    }
1733    this.alpha = typeof(a) === 'number' ? a : 1;
1734};
1735tree.Color.prototype = {
1736    eval: function () { return this },
1737
1738    //
1739    // If we have some transparency, the only way to represent it
1740    // is via `rgba`. Otherwise, we use the hex representation,
1741    // which has better compatibility with older browsers.
1742    // Values are capped between `0` and `255`, rounded and zero-padded.
1743    //
1744    toCSS: function () {
1745        if (this.alpha < 1.0) {
1746            return "rgba(" + this.rgb.map(function (c) {
1747                return Math.round(c);
1748            }).concat(this.alpha).join(', ') + ")";
1749        } else {
1750            return '#' + this.rgb.map(function (i) {
1751                i = Math.round(i);
1752                i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16);
1753                return i.length === 1 ? '0' + i : i;
1754            }).join('');
1755        }
1756    },
1757
1758    //
1759    // Operations have to be done per-channel, if not,
1760    // channels will spill onto each other. Once we have
1761    // our result, in the form of an integer triplet,
1762    // we create a new Color node to hold the result.
1763    //
1764    operate: function (op, other) {
1765        var result = [];
1766
1767        if (! (other instanceof tree.Color)) {
1768            other = other.toColor();
1769        }
1770
1771        for (var c = 0; c < 3; c++) {
1772            result[c] = tree.operate(op, this.rgb[c], other.rgb[c]);
1773        }
1774        return new(tree.Color)(result, this.alpha + other.alpha);
1775    },
1776
1777    toHSL: function () {
1778        var r = this.rgb[0] / 255,
1779            g = this.rgb[1] / 255,
1780            b = this.rgb[2] / 255,
1781            a = this.alpha;
1782
1783        var max = Math.max(r, g, b), min = Math.min(r, g, b);
1784        var h, s, l = (max + min) / 2, d = max - min;
1785
1786        if (max === min) {
1787            h = s = 0;
1788        } else {
1789            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
1790
1791            switch (max) {
1792                case r: h = (g - b) / d + (g < b ? 6 : 0); break;
1793                case g: h = (b - r) / d + 2;               break;
1794                case b: h = (r - g) / d + 4;               break;
1795            }
1796            h /= 6;
1797        }
1798        return { h: h * 360, s: s, l: l, a: a };
1799    },
1800    toARGB: function () {
1801        var argb = [Math.round(this.alpha * 255)].concat(this.rgb);
1802        return '#' + argb.map(function (i) {
1803            i = Math.round(i);
1804            i = (i > 255 ? 255 : (i < 0 ? 0 : i)).toString(16);
1805            return i.length === 1 ? '0' + i : i;
1806        }).join('');
1807    }
1808};
1809
1810
1811})(require('../tree'));
1812(function (tree) {
1813
1814tree.Comment = function (value, silent) {
1815    this.value = value;
1816    this.silent = !!silent;
1817};
1818tree.Comment.prototype = {
1819    toCSS: function (env) {
1820        return env.compress ? '' : this.value;
1821    },
1822    eval: function () { return this }
1823};
1824
1825})(require('../tree'));
1826(function (tree) {
1827
1828//
1829// A number with a unit
1830//
1831tree.Dimension = function (value, unit) {
1832    this.value = parseFloat(value);
1833    this.unit = unit || null;
1834};
1835
1836tree.Dimension.prototype = {
1837    eval: function () { return this },
1838    toColor: function () {
1839        return new(tree.Color)([this.value, this.value, this.value]);
1840    },
1841    toCSS: function () {
1842        var css = this.value + this.unit;
1843        return css;
1844    },
1845
1846    // In an operation between two Dimensions,
1847    // we default to the first Dimension's unit,
1848    // so `1px + 2em` will yield `3px`.
1849    // In the future, we could implement some unit
1850    // conversions such that `100cm + 10mm` would yield
1851    // `101cm`.
1852    operate: function (op, other) {
1853        return new(tree.Dimension)
1854                  (tree.operate(op, this.value, other.value),
1855                  this.unit || other.unit);
1856    }
1857};
1858
1859})(require('../tree'));
1860(function (tree) {
1861
1862tree.Directive = function (name, value) {
1863    this.name = name;
1864    if (Array.isArray(value)) {
1865        this.ruleset = new(tree.Ruleset)([], value);
1866    } else {
1867        this.value = value;
1868    }
1869};
1870tree.Directive.prototype = {
1871    toCSS: function (ctx, env) {
1872        if (this.ruleset) {
1873            this.ruleset.root = true;
1874            return this.name + (env.compress ? '{' : ' {\n  ') +
1875                   this.ruleset.toCSS(ctx, env).trim().replace(/\n/g, '\n  ') +
1876                               (env.compress ? '}': '\n}\n');
1877        } else {
1878            return this.name + ' ' + this.value.toCSS() + ';\n';
1879        }
1880    },
1881    eval: function (env) {
1882        env.frames.unshift(this);
1883        this.ruleset = this.ruleset && this.ruleset.eval(env);
1884        env.frames.shift();
1885        return this;
1886    },
1887    variable: function (name) { return tree.Ruleset.prototype.variable.call(this.ruleset, name) },
1888    find: function () { return tree.Ruleset.prototype.find.apply(this.ruleset, arguments) },
1889    rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.ruleset) }
1890};
1891
1892})(require('../tree'));
1893(function (tree) {
1894
1895tree.Element = function (combinator, value, index) {
1896    this.combinator = combinator instanceof tree.Combinator ?
1897                      combinator : new(tree.Combinator)(combinator);
1898    this.value = value ? value.trim() : "";
1899    this.index = index;
1900};
1901tree.Element.prototype.toCSS = function (env) {
1902    return this.combinator.toCSS(env || {}) + this.value;
1903};
1904
1905tree.Combinator = function (value) {
1906    if (value === ' ') {
1907        this.value = ' ';
1908    } else if (value === '& ') {
1909        this.value = '& ';
1910    } else {
1911        this.value = value ? value.trim() : "";
1912    }
1913};
1914tree.Combinator.prototype.toCSS = function (env) {
1915    return {
1916        ''  : '',
1917        ' ' : ' ',
1918        '&' : '',
1919        '& ' : ' ',
1920        ':' : ' :',
1921        '::': '::',
1922        '+' : env.compress ? '+' : ' + ',
1923        '~' : env.compress ? '~' : ' ~ ',
1924        '>' : env.compress ? '>' : ' > '
1925    }[this.value];
1926};
1927
1928})(require('../tree'));
1929(function (tree) {
1930
1931tree.Expression = function (value) { this.value = value };
1932tree.Expression.prototype = {
1933    eval: function (env) {
1934        if (this.value.length > 1) {
1935            return new(tree.Expression)(this.value.map(function (e) {
1936                return e.eval(env);
1937            }));
1938        } else if (this.value.length === 1) {
1939            return this.value[0].eval(env);
1940        } else {
1941            return this;
1942        }
1943    },
1944    toCSS: function (env) {
1945        return this.value.map(function (e) {
1946            return e.toCSS(env);
1947        }).join(' ');
1948    }
1949};
1950
1951})(require('../tree'));
1952(function (tree) {
1953//
1954// CSS @import node
1955//
1956// The general strategy here is that we don't want to wait
1957// for the parsing to be completed, before we start importing
1958// the file. That's because in the context of a browser,
1959// most of the time will be spent waiting for the server to respond.
1960//
1961// On creation, we push the import path to our import queue, though
1962// `import,push`, we also pass it a callback, which it'll call once
1963// the file has been fetched, and parsed.
1964//
1965tree.Import = function (path, imports) {
1966    var that = this;
1967
1968    this._path = path;
1969
1970    // The '.less' extension is optional
1971    if (path instanceof tree.Quoted) {
1972        this.path = /\.(le?|c)ss(\?.*)?$/.test(path.value) ? path.value : path.value + '.less';
1973    } else {
1974        this.path = path.value.value || path.value;
1975    }
1976
1977    this.css = /css(\?.*)?$/.test(this.path);
1978
1979    // Only pre-compile .less files
1980    if (! this.css) {
1981        imports.push(this.path, function (root) {
1982            if (! root) {
1983                throw new(Error)("Error parsing " + that.path);
1984            }
1985            that.root = root;
1986        });
1987    }
1988};
1989
1990//
1991// The actual import node doesn't return anything, when converted to CSS.
1992// The reason is that it's used at the evaluation stage, so that the rules
1993// it imports can be treated like any other rules.
1994//
1995// In `eval`, we make sure all Import nodes get evaluated, recursively, so
1996// we end up with a flat structure, which can easily be imported in the parent
1997// ruleset.
1998//
1999tree.Import.prototype = {
2000    toCSS: function () {
2001        if (this.css) {
2002            return "@import " + this._path.toCSS() + ';\n';
2003        } else {
2004            return "";
2005        }
2006    },
2007    eval: function (env) {
2008        var ruleset;
2009
2010        if (this.css) {
2011            return this;
2012        } else {
2013            ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0));
2014
2015            for (var i = 0; i < ruleset.rules.length; i++) {
2016                if (ruleset.rules[i] instanceof tree.Import) {
2017                    Array.prototype
2018                         .splice
2019                         .apply(ruleset.rules,
2020                                [i, 1].concat(ruleset.rules[i].eval(env)));
2021                }
2022            }
2023            return ruleset.rules;
2024        }
2025    }
2026};
2027
2028})(require('../tree'));
2029(function (tree) {
2030
2031tree.JavaScript = function (string, index, escaped) {
2032    this.escaped = escaped;
2033    this.expression = string;
2034    this.index = index;
2035};
2036tree.JavaScript.prototype = {
2037    eval: function (env) {
2038        var result,
2039            that = this,
2040            context = {};
2041
2042        var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) {
2043            return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env));
2044        });
2045
2046        try {
2047            expression = new(Function)('return (' + expression + ')');
2048        } catch (e) {
2049            throw { message: "JavaScript evaluation error: `" + expression + "`" ,
2050                    index: this.index };
2051        }
2052
2053        for (var k in env.frames[0].variables()) {
2054            context[k.slice(1)] = {
2055                value: env.frames[0].variables()[k].value,
2056                toJS: function () {
2057                    return this.value.eval(env).toCSS();
2058                }
2059            };
2060        }
2061
2062        try {
2063            result = expression.call(context);
2064        } catch (e) {
2065            throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" ,
2066                    index: this.index };
2067        }
2068        if (typeof(result) === 'string') {
2069            return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index);
2070        } else if (Array.isArray(result)) {
2071            return new(tree.Anonymous)(result.join(', '));
2072        } else {
2073            return new(tree.Anonymous)(result);
2074        }
2075    }
2076};
2077
2078})(require('../tree'));
2079
2080(function (tree) {
2081
2082tree.Keyword = function (value) { this.value = value };
2083tree.Keyword.prototype = {
2084    eval: function () { return this },
2085    toCSS: function () { return this.value }
2086};
2087
2088})(require('../tree'));
2089(function (tree) {
2090
2091tree.mixin = {};
2092tree.mixin.Call = function (elements, args, index) {
2093    this.selector = new(tree.Selector)(elements);
2094    this.arguments = args;
2095    this.index = index;
2096};
2097tree.mixin.Call.prototype = {
2098    eval: function (env) {
2099        var mixins, args, rules = [], match = false;
2100
2101        for (var i = 0; i < env.frames.length; i++) {
2102            if ((mixins = env.frames[i].find(this.selector)).length > 0) {
2103                args = this.arguments && this.arguments.map(function (a) { return a.eval(env) });
2104                for (var m = 0; m < mixins.length; m++) {
2105                    if (mixins[m].match(args, env)) {
2106                        try {
2107                            Array.prototype.push.apply(
2108                                  rules, mixins[m].eval(env, this.arguments).rules);
2109                            match = true;
2110                        } catch (e) {
2111                            throw { message: e.message, index: e.index, stack: e.stack, call: this.index };
2112                        }
2113                    }
2114                }
2115                if (match) {
2116                    return rules;
2117                } else {
2118                    throw { message: 'No matching definition was found for `' +
2119                                      this.selector.toCSS().trim() + '('      +
2120                                      this.arguments.map(function (a) {
2121                                          return a.toCSS();
2122                                      }).join(', ') + ")`",
2123                            index:   this.index };
2124                }
2125            }
2126        }
2127        throw { message: this.selector.toCSS().trim() + " is undefined",
2128                index: this.index };
2129    }
2130};
2131
2132tree.mixin.Definition = function (name, params, rules) {
2133    this.name = name;
2134    this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])];
2135    this.params = params;
2136    this.arity = params.length;
2137    this.rules = rules;
2138    this._lookups = {};
2139    this.required = params.reduce(function (count, p) {
2140        if (!p.name || (p.name && !p.value)) { return count + 1 }
2141        else                                 { return count }
2142    }, 0);
2143    this.parent = tree.Ruleset.prototype;
2144    this.frames = [];
2145};
2146tree.mixin.Definition.prototype = {
2147    toCSS:     function ()     { return "" },
2148    variable:  function (name) { return this.parent.variable.call(this, name) },
2149    variables: function ()     { return this.parent.variables.call(this) },
2150    find:      function ()     { return this.parent.find.apply(this, arguments) },
2151    rulesets:  function ()     { return this.parent.rulesets.apply(this) },
2152
2153    eval: function (env, args) {
2154        var frame = new(tree.Ruleset)(null, []), context, _arguments = [];
2155
2156        for (var i = 0, val; i < this.params.length; i++) {
2157            if (this.params[i].name) {
2158                if (val = (args && args[i]) || this.params[i].value) {
2159                    frame.rules.unshift(new(tree.Rule)(this.params[i].name, val.eval(env)));
2160                } else {
2161                    throw { message: "wrong number of arguments for " + this.name +
2162                            ' (' + args.length + ' for ' + this.arity + ')' };
2163                }
2164            }
2165        }
2166        for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) {
2167            _arguments.push(args[i] || this.params[i].value);
2168        }
2169        frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env)));
2170
2171        return new(tree.Ruleset)(null, this.rules.slice(0)).eval({
2172            frames: [this, frame].concat(this.frames, env.frames)
2173        });
2174    },
2175    match: function (args, env) {
2176        var argsLength = (args && args.length) || 0, len;
2177
2178        if (argsLength < this.required)                               { return false }
2179        if ((this.required > 0) && (argsLength > this.params.length)) { return false }
2180
2181        len = Math.min(argsLength, this.arity);
2182
2183        for (var i = 0; i < len; i++) {
2184            if (!this.params[i].name) {
2185                if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) {
2186                    return false;
2187                }
2188            }
2189        }
2190        return true;
2191    }
2192};
2193
2194})(require('../tree'));
2195(function (tree) {
2196
2197tree.Operation = function (op, operands) {
2198    this.op = op.trim();
2199    this.operands = operands;
2200};
2201tree.Operation.prototype.eval = function (env) {
2202    var a = this.operands[0].eval(env),
2203        b = this.operands[1].eval(env),
2204        temp;
2205
2206    if (a instanceof tree.Dimension && b instanceof tree.Color) {
2207        if (this.op === '*' || this.op === '+') {
2208            temp = b, b = a, a = temp;
2209        } else {
2210            throw { name: "OperationError",
2211                    message: "Can't substract or divide a color from a number" };
2212        }
2213    }
2214    return a.operate(this.op, b);
2215};
2216
2217tree.operate = function (op, a, b) {
2218    switch (op) {
2219        case '+': return a + b;
2220        case '-': return a - b;
2221        case '*': return a * b;
2222        case '/': return a / b;
2223    }
2224};
2225
2226})(require('../tree'));
2227(function (tree) {
2228
2229tree.Quoted = function (str, content, escaped, i) {
2230    this.escaped = escaped;
2231    this.value = content || '';
2232    this.quote = str.charAt(0);
2233    this.index = i;
2234};
2235tree.Quoted.prototype = {
2236    toCSS: function () {
2237        if (this.escaped) {
2238            return this.value;
2239        } else {
2240            return this.quote + this.value + this.quote;
2241        }
2242    },
2243    eval: function (env) {
2244        var that = this;
2245        var value = this.value.replace(/`([^`]+)`/g, function (_, exp) {
2246            return new(tree.JavaScript)(exp, that.index, true).eval(env).value;
2247        }).replace(/@\{([\w-]+)\}/g, function (_, name) {
2248            var v = new(tree.Variable)('@' + name, that.index).eval(env);
2249            return v.value || v.toCSS();
2250        });
2251        return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index);
2252    }
2253};
2254
2255})(require('../tree'));
2256(function (tree) {
2257
2258tree.Rule = function (name, value, important, index) {
2259    this.name = name;
2260    this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]);
2261    this.important = important ? ' ' + important.trim() : '';
2262    this.index = index;
2263
2264    if (name.charAt(0) === '@') {
2265        this.variable = true;
2266    } else { this.variable = false }
2267};
2268tree.Rule.prototype.toCSS = function (env) {
2269    if (this.variable) { return "" }
2270    else {
2271        return this.name + (env.compress ? ':' : ': ') +
2272               this.value.toCSS(env) +
2273               this.important + ";";
2274    }
2275};
2276
2277tree.Rule.prototype.eval = function (context) {
2278    return new(tree.Rule)(this.name, this.value.eval(context), this.important, this.index);
2279};
2280
2281tree.Shorthand = function (a, b) {
2282    this.a = a;
2283    this.b = b;
2284};
2285
2286tree.Shorthand.prototype = {
2287    toCSS: function (env) {
2288        return this.a.toCSS(env) + "/" + this.b.toCSS(env);
2289    },
2290    eval: function () { return this }
2291};
2292
2293})(require('../tree'));
2294(function (tree) {
2295
2296tree.Ruleset = function (selectors, rules) {
2297    this.selectors = selectors;
2298    this.rules = rules;
2299    this._lookups = {};
2300};
2301tree.Ruleset.prototype = {
2302    eval: function (env) {
2303        var ruleset = new(tree.Ruleset)(this.selectors, this.rules.slice(0));
2304
2305        ruleset.root = this.root;
2306
2307        // push the current ruleset to the frames stack
2308        env.frames.unshift(ruleset);
2309
2310        // Evaluate imports
2311        if (ruleset.root) {
2312            for (var i = 0; i < ruleset.rules.length; i++) {
2313                if (ruleset.rules[i] instanceof tree.Import) {
2314                    Array.prototype.splice
2315                         .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env)));
2316                }
2317            }
2318        }
2319
2320        // Store the frames around mixin definitions,
2321        // so they can be evaluated like closures when the time comes.
2322        for (var i = 0; i < ruleset.rules.length; i++) {
2323            if (ruleset.rules[i] instanceof tree.mixin.Definition) {
2324                ruleset.rules[i].frames = env.frames.slice(0);
2325            }
2326        }
2327
2328        // Evaluate mixin calls.
2329        for (var i = 0; i < ruleset.rules.length; i++) {
2330            if (ruleset.rules[i] instanceof tree.mixin.Call) {
2331                Array.prototype.splice
2332                     .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env)));
2333            }
2334        }
2335
2336        // Evaluate everything else
2337        for (var i = 0, rule; i < ruleset.rules.length; i++) {
2338            rule = ruleset.rules[i];
2339
2340            if (! (rule instanceof tree.mixin.Definition)) {
2341                ruleset.rules[i] = rule.eval ? rule.eval(env) : rule;
2342            }
2343        }
2344
2345        // Pop the stack
2346        env.frames.shift();
2347
2348        return ruleset;
2349    },
2350    match: function (args) {
2351        return !args || args.length === 0;
2352    },
2353    variables: function () {
2354        if (this._variables) { return this._variables }
2355        else {
2356            return this._variables = this.rules.reduce(function (hash, r) {
2357                if (r instanceof tree.Rule && r.variable === true) {
2358                    hash[r.name] = r;
2359                }
2360                return hash;
2361            }, {});
2362        }
2363    },
2364    variable: function (name) {
2365        return this.variables()[name];
2366    },
2367    rulesets: function () {
2368        if (this._rulesets) { return this._rulesets }
2369        else {
2370            return this._rulesets = this.rules.filter(function (r) {
2371                return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition);
2372            });
2373        }
2374    },
2375    find: function (selector, self) {
2376        self = self || this;
2377        var rules = [], rule, match,
2378            key = selector.toCSS();
2379
2380        if (key in this._lookups) { return this._lookups[key] }
2381
2382        this.rulesets().forEach(function (rule) {
2383            if (rule !== self) {
2384                for (var j = 0; j < rule.selectors.length; j++) {
2385                    if (match = selector.match(rule.selectors[j])) {
2386                        if (selector.elements.length > rule.selectors[j].elements.length) {
2387                            Array.prototype.push.apply(rules, rule.find(
2388                                new(tree.Selector)(selector.elements.slice(1)), self));
2389                        } else {
2390                            rules.push(rule);
2391                        }
2392                        break;
2393                    }
2394                }
2395            }
2396        });
2397        return this._lookups[key] = rules;
2398    },
2399    //
2400    // Entry point for code generation
2401    //
2402    //     `context` holds an array of arrays.
2403    //
2404    toCSS: function (context, env) {
2405        var css = [],      // The CSS output
2406            rules = [],    // node.Rule instances
2407            rulesets = [], // node.Ruleset instances
2408            paths = [],    // Current selectors
2409            selector,      // The fully rendered selector
2410            rule;
2411
2412        if (! this.root) {
2413            if (context.length === 0) {
2414                paths = this.selectors.map(function (s) { return [s] });
2415            } else {
2416                this.joinSelectors( paths, context, this.selectors );
2417            }
2418        }
2419
2420        // Compile rules and rulesets
2421        for (var i = 0; i < this.rules.length; i++) {
2422            rule = this.rules[i];
2423
2424            if (rule.rules || (rule instanceof tree.Directive)) {
2425                rulesets.push(rule.toCSS(paths, env));
2426            } else if (rule instanceof tree.Comment) {
2427                if (!rule.silent) {
2428                    if (this.root) {
2429                        rulesets.push(rule.toCSS(env));
2430                    } else {
2431                        rules.push(rule.toCSS(env));
2432                    }
2433                }
2434            } else {
2435                if (rule.toCSS && !rule.variable) {
2436                    rules.push(rule.toCSS(env));
2437                } else if (rule.value && !rule.variable) {
2438                    rules.push(rule.value.toString());
2439                }
2440            }
2441        }
2442
2443        rulesets = rulesets.join('');
2444
2445        // If this is the root node, we don't render
2446        // a selector, or {}.
2447        // Otherwise, only output if this ruleset has rules.
2448        if (this.root) {
2449            css.push(rules.join(env.compress ? '' : '\n'));
2450        } else {
2451            if (rules.length > 0) {
2452                selector = paths.map(function (p) {
2453                    return p.map(function (s) {
2454                        return s.toCSS(env);
2455                    }).join('').trim();
2456                }).join(env.compress ? ',' : (paths.length > 3 ? ',\n' : ', '));
2457                css.push(selector,
2458                        (env.compress ? '{' : ' {\n  ') +
2459                        rules.join(env.compress ? '' : '\n  ') +
2460                        (env.compress ? '}' : '\n}\n'));
2461            }
2462        }
2463        css.push(rulesets);
2464
2465        return css.join('') + (env.compress ? '\n' : '');
2466    },
2467
2468    joinSelectors: function (paths, context, selectors) {
2469        for (var s = 0; s < selectors.length; s++) {
2470            this.joinSelector(paths, context, selectors[s]);
2471        }
2472    },
2473
2474    joinSelector: function (paths, context, selector) {
2475        var before = [], after = [], beforeElements = [],
2476            afterElements = [], hasParentSelector = false, el;
2477
2478        for (var i = 0; i < selector.elements.length; i++) {
2479            el = selector.elements[i];
2480            if (el.combinator.value.charAt(0) === '&') {
2481                hasParentSelector = true;
2482            }
2483            if (hasParentSelector) afterElements.push(el);
2484            else                   beforeElements.push(el);
2485        }
2486
2487        if (! hasParentSelector) {
2488            afterElements = beforeElements;
2489            beforeElements = [];
2490        }
2491
2492        if (beforeElements.length > 0) {
2493            before.push(new(tree.Selector)(beforeElements));
2494        }
2495
2496        if (afterElements.length > 0) {
2497            after.push(new(tree.Selector)(afterElements));
2498        }
2499
2500        for (var c = 0; c < context.length; c++) {
2501            paths.push(before.concat(context[c]).concat(after));
2502        }
2503    }
2504};
2505})(require('../tree'));
2506(function (tree) {
2507
2508tree.Selector = function (elements) {
2509    this.elements = elements;
2510    if (this.elements[0].combinator.value === "") {
2511        this.elements[0].combinator.value = ' ';
2512    }
2513};
2514tree.Selector.prototype.match = function (other) {
2515    var len  = this.elements.length,
2516        olen = other.elements.length,
2517        max  = Math.min(len, olen);
2518
2519    if (len < olen) {
2520        return false;
2521    } else {
2522        for (var i = 0; i < max; i++) {
2523            if (this.elements[i].value !== other.elements[i].value) {
2524                return false;
2525            }
2526        }
2527    }
2528    return true;
2529};
2530tree.Selector.prototype.toCSS = function (env) {
2531    if (this._css) { return this._css }
2532
2533    return this._css = this.elements.map(function (e) {
2534        if (typeof(e) === 'string') {
2535            return ' ' + e.trim();
2536        } else {
2537            return e.toCSS(env);
2538        }
2539    }).join('');
2540};
2541
2542})(require('../tree'));
2543(function (tree) {
2544
2545tree.URL = function (val, paths) {
2546    if (val.data) {
2547        this.attrs = val;
2548    } else {
2549        // Add the base path if the URL is relative and we are in the browser
2550        if (typeof(window) !== 'undefined' && !/^(?:https?:\/\/|file:\/\/|data:|\/)/.test(val.value) && paths.length > 0) {
2551            val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value);
2552        }
2553        this.value = val;
2554        this.paths = paths;
2555    }
2556};
2557tree.URL.prototype = {
2558    toCSS: function () {
2559        return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data
2560                                    : this.value.toCSS()) + ")";
2561    },
2562    eval: function (ctx) {
2563        return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths);
2564    }
2565};
2566
2567})(require('../tree'));
2568(function (tree) {
2569
2570tree.Value = function (value) {
2571    this.value = value;
2572    this.is = 'value';
2573};
2574tree.Value.prototype = {
2575    eval: function (env) {
2576        if (this.value.length === 1) {
2577            return this.value[0].eval(env);
2578        } else {
2579            return new(tree.Value)(this.value.map(function (v) {
2580                return v.eval(env);
2581            }));
2582        }
2583    },
2584    toCSS: function (env) {
2585        return this.value.map(function (e) {
2586            return e.toCSS(env);
2587        }).join(env.compress ? ',' : ', ');
2588    }
2589};
2590
2591})(require('../tree'));
2592(function (tree) {
2593
2594tree.Variable = function (name, index) { this.name = name, this.index = index };
2595tree.Variable.prototype = {
2596    eval: function (env) {
2597        var variable, v, name = this.name;
2598
2599        if (name.indexOf('@@') == 0) {
2600            name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value;
2601        }
2602
2603        if (variable = tree.find(env.frames, function (frame) {
2604            if (v = frame.variable(name)) {
2605                return v.value.eval(env);
2606            }
2607        })) { return variable }
2608        else {
2609            throw { message: "variable " + name + " is undefined",
2610                    index: this.index };
2611        }
2612    }
2613};
2614
2615})(require('../tree'));
2616require('./tree').find = function (obj, fun) {
2617    for (var i = 0, r; i < obj.length; i++) {
2618        if (r = fun.call(obj, obj[i])) { return r }
2619    }
2620    return null;
2621};
2622require('./tree').jsify = function (obj) {
2623    if (Array.isArray(obj.value) && (obj.value.length > 1)) {
2624        return '[' + obj.value.map(function (v) { return v.toCSS(false) }).join(', ') + ']';
2625    } else {
2626        return obj.toCSS(false);
2627    }
2628};
2629//
2630// browser.js - client-side engine
2631//
2632
2633var isFileProtocol = (location.protocol === 'file:'    ||
2634                      location.protocol === 'chrome:'  ||
2635                      location.protocol === 'chrome-extension:'  ||
2636                      location.protocol === 'resource:');
2637
2638less.env = less.env || (location.hostname == '127.0.0.1' ||
2639                        location.hostname == '0.0.0.0'   ||
2640                        location.hostname == 'localhost' ||
2641                        location.port.length > 0         ||
2642                        isFileProtocol                   ? 'development'
2643                                                         : 'production');
2644
2645// Load styles asynchronously (default: false)
2646//
2647// This is set to `false` by default, so that the body
2648// doesn't start loading before the stylesheets are parsed.
2649// Setting this to `true` can result in flickering.
2650//
2651less.async = false;
2652
2653// Interval between watch polls
2654less.poll = less.poll || (isFileProtocol ? 1000 : 1500);
2655
2656//
2657// Watch mode
2658//
2659less.watch   = function () { return this.watchMode = true };
2660less.unwatch = function () { return this.watchMode = false };
2661
2662if (less.env === 'development') {
2663    less.optimization = 0;
2664
2665    if (/!watch/.test(location.hash)) {
2666        less.watch();
2667    }
2668    less.watchTimer = setInterval(function () {
2669        if (less.watchMode) {
2670            loadStyleSheets(function (root, sheet, env) {
2671                if (root) {
2672                    createCSS(root.toCSS(), sheet, env.lastModified);
2673                }
2674            });
2675        }
2676    }, less.poll);
2677} else {
2678    less.optimization = 3;
2679}
2680
2681var cache;
2682
2683try {
2684    cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage;
2685} catch (_) {
2686    cache = null;
2687}
2688
2689//
2690// Get all <link> tags with the 'rel' attribute set to "stylesheet/less"
2691//
2692var links = document.getElementsByTagName('link');
2693var typePattern = /^text\/(x-)?less$/;
2694
2695less.sheets = [];
2696
2697for (var i = 0; i < links.length; i++) {
2698    if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) &&
2699       (links[i].type.match(typePattern)))) {
2700        less.sheets.push(links[i]);
2701    }
2702}
2703
2704
2705less.refresh = function (reload) {
2706    var startTime, endTime;
2707    startTime = endTime = new(Date);
2708
2709    loadStyleSheets(function (root, sheet, env) {
2710        if (env.local) {
2711            log("loading " + sheet.href + " from cache.");
2712        } else {
2713            log("parsed " + sheet.href + " successfully.");
2714            createCSS(root.toCSS(), sheet, env.lastModified);
2715        }
2716        log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms');
2717        (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms');
2718        endTime = new(Date);
2719    }, reload);
2720
2721    loadStyles();
2722};
2723less.refreshStyles = loadStyles;
2724
2725less.refresh(less.env === 'development');
2726
2727function loadStyles() {
2728    var styles = document.getElementsByTagName('style');
2729    for (var i = 0; i < styles.length; i++) {
2730        if (styles[i].type.match(typePattern)) {
2731            new(less.Parser)().parse(styles[i].innerHTML || '', function (e, tree) {
2732                var css = tree.toCSS();
2733                var style = styles[i];
2734                try {
2735                    style.innerHTML = css;
2736                } catch (_) {
2737                    style.styleSheets.cssText = css;
2738                }
2739                style.type = 'text/css';
2740            });
2741        }
2742    }
2743}
2744
2745function loadStyleSheets(callback, reload) {
2746    for (var i = 0; i < less.sheets.length; i++) {
2747        loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1));
2748    }
2749}
2750
2751function loadStyleSheet(sheet, callback, reload, remaining) {
2752    var url       = window.location.href.replace(/[#?].*$/, '');
2753    var href      = sheet.href.replace(/\?.*$/, '');
2754    var css       = cache && cache.getItem(href);
2755    var timestamp = cache && cache.getItem(href + ':timestamp');
2756    var styles    = { css: css, timestamp: timestamp };
2757
2758    // Stylesheets in IE don't always return the full path
2759    if (! /^(https?|file):/.test(href)) {
2760        if (href.charAt(0) == "/") {
2761            href = window.location.protocol + "//" + window.location.host + href;
2762        } else {
2763            href = url.slice(0, url.lastIndexOf('/') + 1) + href;
2764        }
2765    }
2766
2767    xhr(sheet.href, sheet.type, function (data, lastModified) {
2768        if (!reload && styles && lastModified &&
2769           (new(Date)(lastModified).valueOf() ===
2770            new(Date)(styles.timestamp).valueOf())) {
2771            // Use local copy
2772            createCSS(styles.css, sheet);
2773            callback(null, sheet, { local: true, remaining: remaining });
2774        } else {
2775            // Use remote copy (re-parse)
2776            try {
2777                new(less.Parser)({
2778                    optimization: less.optimization,
2779                    paths: [href.replace(/[\w\.-]+$/, '')],
2780                    mime: sheet.type
2781                }).parse(data, function (e, root) {
2782                    if (e) { return error(e, href) }
2783                    try {
2784                        callback(root, sheet, { local: false, lastModified: lastModified, remaining: remaining });
2785                        removeNode(document.getElementById('less-error-message:' + extractId(href)));
2786                    } catch (e) {
2787                        error(e, href);
2788                    }
2789                });
2790            } catch (e) {
2791                error(e, href);
2792            }
2793        }
2794    }, function (status, url) {
2795        throw new(Error)("Couldn't load " + url + " (" + status + ")");
2796    });
2797}
2798
2799function extractId(href) {
2800    return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' )  // Remove protocol & domain
2801               .replace(/^\//,                 '' )  // Remove root /
2802               .replace(/\?.*$/,               '' )  // Remove query
2803               .replace(/\.[^\.\/]+$/,         '' )  // Remove file extension
2804               .replace(/[^\.\w-]+/g,          '-')  // Replace illegal characters
2805               .replace(/\./g,                 ':'); // Replace dots with colons(for valid id)
2806}
2807
2808function createCSS(styles, sheet, lastModified) {
2809    var css;
2810
2811    // Strip the query-string
2812    var href = sheet.href ? sheet.href.replace(/\?.*$/, '') : '';
2813
2814    // If there is no title set, use the filename, minus the extension
2815    var id = 'less:' + (sheet.title || extractId(href));
2816
2817    // If the stylesheet doesn't exist, create a new node
2818    if ((css = document.getElementById(id)) === null) {
2819        css = document.createElement('style');
2820        css.type = 'text/css';
2821        css.media = sheet.media || 'screen';
2822        css.id = id;
2823        document.getElementsByTagName('head')[0].appendChild(css);
2824    }
2825
2826    if (css.styleSheet) { // IE
2827        try {
2828            css.styleSheet.cssText = styles;
2829        } catch (e) {
2830            throw new(Error)("Couldn't reassign styleSheet.cssText.");
2831        }
2832    } else {
2833        (function (node) {
2834            if (css.childNodes.length > 0) {
2835                if (css.firstChild.nodeValue !== node.nodeValue) {
2836                    css.replaceChild(node, css.firstChild);
2837                }
2838            } else {
2839                css.appendChild(node);
2840            }
2841        })(document.createTextNode(styles));
2842    }
2843
2844    // Don't update the local store if the file wasn't modified
2845    if (lastModified && cache) {
2846        log('saving ' + href + ' to cache.');
2847        cache.setItem(href, styles);
2848        cache.setItem(href + ':timestamp', lastModified);
2849    }
2850}
2851
2852function xhr(url, type, callback, errback) {
2853    var xhr = getXMLHttpRequest();
2854    var async = isFileProtocol ? false : less.async;
2855
2856    if (typeof(xhr.overrideMimeType) === 'function') {
2857        xhr.overrideMimeType('text/css');
2858    }
2859    xhr.open('GET', url, async);
2860    xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5');
2861    xhr.send(null);
2862
2863    if (isFileProtocol) {
2864        if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) {
2865            callback(xhr.responseText);
2866        } else {
2867            errback(xhr.status, url);
2868        }
2869    } else if (async) {
2870        xhr.onreadystatechange = function () {
2871            if (xhr.readyState == 4) {
2872                handleResponse(xhr, callback, errback);
2873            }
2874        };
2875    } else {
2876        handleResponse(xhr, callback, errback);
2877    }
2878
2879    function handleResponse(xhr, callback, errback) {
2880        if (xhr.status >= 200 && xhr.status < 300) {
2881            callback(xhr.responseText,
2882                     xhr.getResponseHeader("Last-Modified"));
2883        } else if (typeof(errback) === 'function') {
2884            errback(xhr.status, url);
2885        }
2886    }
2887}
2888
2889function getXMLHttpRequest() {
2890    if (window.XMLHttpRequest) {
2891        return new(XMLHttpRequest);
2892    } else {
2893        try {
2894            return new(ActiveXObject)("MSXML2.XMLHTTP.3.0");
2895        } catch (e) {
2896            log("browser doesn't support AJAX.");
2897            return null;
2898        }
2899    }
2900}
2901
2902function removeNode(node) {
2903    return node && node.parentNode.removeChild(node);
2904}
2905
2906function log(str) {
2907    if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) }
2908}
2909
2910function error(e, href) {
2911    var id = 'less-error-message:' + extractId(href);
2912
2913    var template = ['<ul>',
2914                        '<li><label>[-1]</label><pre class="ctx">{0}</pre></li>',
2915                        '<li><label>[0]</label><pre>{current}</pre></li>',
2916                        '<li><label>[1]</label><pre class="ctx">{2}</pre></li>',
2917                    '</ul>'].join('\n');
2918
2919    var elem = document.createElement('div'), timer, content;
2920
2921    elem.id        = id;
2922    elem.className = "less-error-message";
2923
2924    content = '<h3>'  + (e.message || 'There is an error in your .less file') +
2925              '</h3>' + '<p><a href="' + href   + '">' + href + "</a> ";
2926
2927    if (e.extract) {
2928        content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':</p>' +
2929            template.replace(/\[(-?\d)\]/g, function (_, i) {
2930                return (parseInt(e.line) + parseInt(i)) || '';
2931            }).replace(/\{(\d)\}/g, function (_, i) {
2932                return e.extract[parseInt(i)] || '';
2933            }).replace(/\{current\}/, e.extract[1].slice(0, e.column) + '<span class="error">' +
2934                                      e.extract[1].slice(e.column)    + '</span>');
2935    }
2936    elem.innerHTML = content;
2937
2938    // CSS for error messages
2939    createCSS([
2940        '.less-error-message ul, .less-error-message li {',
2941            'list-style-type: none;',
2942            'margin-right: 15px;',
2943            'padding: 4px 0;',
2944            'margin: 0;',
2945        '}',
2946        '.less-error-message label {',
2947            'font-size: 12px;',
2948            'margin-right: 15px;',
2949            'padding: 4px 0;',
2950            'color: #cc7777;',
2951        '}',
2952        '.less-error-message pre {',
2953            'color: #ee4444;',
2954            'padding: 4px 0;',
2955            'margin: 0;',
2956            'display: inline-block;',
2957        '}',
2958        '.less-error-message pre.ctx {',
2959            'color: #dd4444;',
2960        '}',
2961        '.less-error-message h3 {',
2962            'font-size: 20px;',
2963            'font-weight: bold;',
2964            'padding: 15px 0 5px 0;',
2965            'margin: 0;',
2966        '}',
2967        '.less-error-message a {',
2968            'color: #10a',
2969        '}',
2970        '.less-error-message .error {',
2971            'color: red;',
2972            'font-weight: bold;',
2973            'padding-bottom: 2px;',
2974            'border-bottom: 1px dashed red;',
2975        '}'
2976    ].join('\n'), { title: 'error-message' });
2977
2978    elem.style.cssText = [
2979        "font-family: Arial, sans-serif",
2980        "border: 1px solid #e00",
2981        "background-color: #eee",
2982        "border-radius: 5px",
2983        "-webkit-border-radius: 5px",
2984        "-moz-border-radius: 5px",
2985        "color: #e00",
2986        "padding: 15px",
2987        "margin-bottom: 15px"
2988    ].join(';');
2989
2990    if (less.env == 'development') {
2991        timer = setInterval(function () {
2992            if (document.body) {
2993                if (document.getElementById(id)) {
2994                    document.body.replaceChild(elem, document.getElementById(id));
2995                } else {
2996                    document.body.insertBefore(elem, document.body.firstChild);
2997                }
2998                clearInterval(timer);
2999            }
3000        }, 10);
3001    }
3002}
3003
3004})(window);
Note: See TracBrowser for help on using the repository browser.