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