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