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

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

Added Dojo 1.9.3 release.

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