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