source: Dev/trunk/src/client/util/less/dist/less-1.1.0.js @ 529

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

Added Dojo 1.9.3 release.

File size: 88.3 KB
Line 
1//
2// LESS - Leaner CSS v1.1.0
3// http://lesscss.org
4//
5// Copyright (c) 2009-2011, Alexis Sellier
6// Licensed under the Apache 2.0 License.
7//
8(function (window, undefined) {
9//
10// Stub out `require` in the browser
11//
12function require(arg) {
13    return window.less[arg.split('/')[1]];
14};
15
16
17// ecma-5.js
18//
19// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License
20// -- tlrobinson Tom Robinson
21// dantman Daniel Friesen
22
23//
24// Array
25//
26if (!Array.isArray) {
27    Array.isArray = function(obj) {
28        return Object.prototype.toString.call(obj) === "[object Array]" ||
29               (obj instanceof Array);
30    };
31}
32if (!Array.prototype.forEach) {
33    Array.prototype.forEach =  function(block, thisObject) {
34        var len = this.length >>> 0;
35        for (var i = 0; i < len; i++) {
36            if (i in this) {
37                block.call(thisObject, this[i], i, this);
38            }
39        }
40    };
41}
42if (!Array.prototype.map) {
43    Array.prototype.map = function(fun /*, thisp*/) {
44        var len = this.length >>> 0;
45        var res = new Array(len);
46        var thisp = arguments[1];
47
48        for (var i = 0; i < len; i++) {
49            if (i in this) {
50                res[i] = fun.call(thisp, this[i], i, this);
51            }
52        }
53        return res;
54    };
55}
56if (!Array.prototype.filter) {
57    Array.prototype.filter = function (block /*, thisp */) {
58        var values = [];
59        var thisp = arguments[1];
60        for (var i = 0; i < this.length; i++) {
61            if (block.call(thisp, this[i])) {
62                values.push(this[i]);
63            }
64        }
65        return values;
66    };
67}
68if (!Array.prototype.reduce) {
69    Array.prototype.reduce = function(fun /*, initial*/) {
70        var len = this.length >>> 0;
71        var i = 0;
72
73        // no value to return if no initial value and an empty array
74        if (len === 0 && arguments.length === 1) throw new TypeError();
75
76        if (arguments.length >= 2) {
77            var rv = arguments[1];
78        } else {
79            do {
80                if (i in this) {
81                    rv = this[i++];
82                    break;
83                }
84                // if array contains no values, no initial value to return
85                if (++i >= len) throw new TypeError();
86            } while (true);
87        }
88        for (; i < len; i++) {
89            if (i in this) {
90                rv = fun.call(null, rv, this[i], i, this);
91            }
92        }
93        return rv;
94    };
95}
96if (!Array.prototype.indexOf) {
97    Array.prototype.indexOf = function (value /*, fromIndex */ ) {
98        var length = this.length;
99        var i = arguments[1] || 0;
100
101        if (!length)     return -1;
102        if (i >= length) return -1;
103        if (i < 0)       i += length;
104
105        for (; i < length; i++) {
106            if (!Object.prototype.hasOwnProperty.call(this, i)) { continue }
107            if (value === this[i]) return i;
108        }
109        return -1;
110    };
111}
112
113//
114// Object
115//
116if (!Object.keys) {
117    Object.keys = function (object) {
118        var keys = [];
119        for (var name in object) {
120            if (Object.prototype.hasOwnProperty.call(object, name)) {
121                keys.push(name);
122            }
123        }
124        return keys;
125    };
126}
127
128//
129// String
130//
131if (!String.prototype.trim) {
132    String.prototype.trim = function () {
133        return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, '');
134    };
135}
136var less, tree;
137
138if (typeof(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 {
1695            return this.value[0].eval(env);
1696        }
1697    },
1698    toCSS: function (env) {
1699        return this.value.map(function (e) {
1700            return e.toCSS(env);
1701        }).join(' ');
1702    }
1703};
1704
1705})(require('less/tree'));
1706(function (tree) {
1707//
1708// CSS @import node
1709//
1710// The general strategy here is that we don't want to wait
1711// for the parsing to be completed, before we start importing
1712// the file. That's because in the context of a browser,
1713// most of the time will be spent waiting for the server to respond.
1714//
1715// On creation, we push the import path to our import queue, though
1716// `import,push`, we also pass it a callback, which it'll call once
1717// the file has been fetched, and parsed.
1718//
1719tree.Import = function (path, imports) {
1720    var that = this;
1721
1722    this._path = path;
1723
1724    // The '.less' extension is optional
1725    if (path instanceof tree.Quoted) {
1726        this.path = /\.(le?|c)ss$/.test(path.value) ? path.value : path.value + '.less';
1727    } else {
1728        this.path = path.value.value || path.value;
1729    }
1730
1731    this.css = /css$/.test(this.path);
1732
1733    // Only pre-compile .less files
1734    if (! this.css) {
1735        imports.push(this.path, function (root) {
1736            if (! root) {
1737                throw new(Error)("Error parsing " + that.path);
1738            }
1739            that.root = root;
1740        });
1741    }
1742};
1743
1744//
1745// The actual import node doesn't return anything, when converted to CSS.
1746// The reason is that it's used at the evaluation stage, so that the rules
1747// it imports can be treated like any other rules.
1748//
1749// In `eval`, we make sure all Import nodes get evaluated, recursively, so
1750// we end up with a flat structure, which can easily be imported in the parent
1751// ruleset.
1752//
1753tree.Import.prototype = {
1754    toCSS: function () {
1755        if (this.css) {
1756            return "@import " + this._path.toCSS() + ';\n';
1757        } else {
1758            return "";
1759        }
1760    },
1761    eval: function (env) {
1762        var ruleset;
1763
1764        if (this.css) {
1765            return this;
1766        } else {
1767            ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0));
1768
1769            for (var i = 0; i < ruleset.rules.length; i++) {
1770                if (ruleset.rules[i] instanceof tree.Import) {
1771                    Array.prototype
1772                         .splice
1773                         .apply(ruleset.rules,
1774                                [i, 1].concat(ruleset.rules[i].eval(env)));
1775                }
1776            }
1777            return ruleset.rules;
1778        }
1779    }
1780};
1781
1782})(require('less/tree'));
1783(function (tree) {
1784
1785tree.JavaScript = function (string, index, escaped) {
1786    this.escaped = escaped;
1787    this.expression = string;
1788    this.index = index;
1789};
1790tree.JavaScript.prototype = {
1791    toCSS: function () {
1792        if (this.escaped) {
1793            return this.evaluated;
1794        } else {
1795            return JSON.stringify(this.evaluated);
1796        }
1797    },
1798    eval: function (env) {
1799        var result,
1800            context = {};
1801
1802        var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) {
1803            return new(tree.Variable)('@' + name).eval(env).value;
1804        });
1805
1806        expression = new(Function)('return (' + expression + ')');
1807
1808        for (var k in env.frames[0].variables()) {
1809            context[k.slice(1)] = {
1810                value: env.frames[0].variables()[k].value,
1811                toJS: function () {
1812                    return this.value.eval(env).toCSS();
1813                }
1814            };
1815        }
1816
1817        try {
1818            this.evaluated = expression.call(context);
1819        } catch (e) {
1820            throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" ,
1821                    index: this.index };
1822        }
1823        return this;
1824    }
1825};
1826
1827})(require('less/tree'));
1828
1829(function (tree) {
1830
1831tree.Keyword = function (value) { this.value = value };
1832tree.Keyword.prototype = {
1833    eval: function () { return this },
1834    toCSS: function () { return this.value }
1835};
1836
1837})(require('less/tree'));
1838(function (tree) {
1839
1840tree.mixin = {};
1841tree.mixin.Call = function (elements, args, index) {
1842    this.selector = new(tree.Selector)(elements);
1843    this.arguments = args;
1844    this.index = index;
1845};
1846tree.mixin.Call.prototype = {
1847    eval: function (env) {
1848        var mixins, rules = [], match = false;
1849
1850        for (var i = 0; i < env.frames.length; i++) {
1851            if ((mixins = env.frames[i].find(this.selector)).length > 0) {
1852                for (var m = 0; m < mixins.length; m++) {
1853                    if (mixins[m].match(this.arguments, env)) {
1854                        try {
1855                            Array.prototype.push.apply(
1856                                  rules, mixins[m].eval(env, this.arguments).rules);
1857                            match = true;
1858                        } catch (e) {
1859                            throw { message: e.message, index: e.index, stack: e.stack, call: this.index };
1860                        }
1861                    }
1862                }
1863                if (match) {
1864                    return rules;
1865                } else {
1866                    throw { message: 'No matching definition was found for `' +
1867                                      this.selector.toCSS().trim() + '('      +
1868                                      this.arguments.map(function (a) {
1869                                          return a.toCSS();
1870                                      }).join(', ') + ")`",
1871                            index:   this.index };
1872                }
1873            }
1874        }
1875        throw { message: this.selector.toCSS().trim() + " is undefined",
1876                index: this.index };
1877    }
1878};
1879
1880tree.mixin.Definition = function (name, params, rules) {
1881    this.name = name;
1882    this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])];
1883    this.params = params;
1884    this.arity = params.length;
1885    this.rules = rules;
1886    this._lookups = {};
1887    this.required = params.reduce(function (count, p) {
1888        if (!p.name || (p.name && !p.value)) { return count + 1 }
1889        else                                 { return count }
1890    }, 0);
1891    this.parent = tree.Ruleset.prototype;
1892    this.frames = [];
1893};
1894tree.mixin.Definition.prototype = {
1895    toCSS:     function ()     { return "" },
1896    variable:  function (name) { return this.parent.variable.call(this, name) },
1897    variables: function ()     { return this.parent.variables.call(this) },
1898    find:      function ()     { return this.parent.find.apply(this, arguments) },
1899    rulesets:  function ()     { return this.parent.rulesets.apply(this) },
1900
1901    eval: function (env, args) {
1902        var frame = new(tree.Ruleset)(null, []), context, _arguments = [];
1903
1904        for (var i = 0, val; i < this.params.length; i++) {
1905            if (this.params[i].name) {
1906                if (val = (args && args[i]) || this.params[i].value) {
1907                    frame.rules.unshift(new(tree.Rule)(this.params[i].name, val.eval(env)));
1908                } else {
1909                    throw { message: "wrong number of arguments for " + this.name +
1910                            ' (' + args.length + ' for ' + this.arity + ')' };
1911                }
1912            }
1913        }
1914        for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) {
1915            _arguments.push(args[i] || this.params[i].value);
1916        }
1917        frame.rules.unshift(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments)));
1918
1919        return new(tree.Ruleset)(null, this.rules.slice(0)).eval({
1920            frames: [this, frame].concat(this.frames, env.frames)
1921        });
1922    },
1923    match: function (args, env) {
1924        var argsLength = (args && args.length) || 0, len;
1925
1926        if (argsLength < this.required)                               { return false }
1927        if ((this.required > 0) && (argsLength > this.params.length)) { return false }
1928
1929        len = Math.min(argsLength, this.arity);
1930
1931        for (var i = 0; i < len; i++) {
1932            if (!this.params[i].name) {
1933                if (args[i].eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) {
1934                    return false;
1935                }
1936            }
1937        }
1938        return true;
1939    }
1940};
1941
1942})(require('less/tree'));
1943(function (tree) {
1944
1945tree.Operation = function (op, operands) {
1946    this.op = op.trim();
1947    this.operands = operands;
1948};
1949tree.Operation.prototype.eval = function (env) {
1950    var a = this.operands[0].eval(env),
1951        b = this.operands[1].eval(env),
1952        temp;
1953
1954    if (a instanceof tree.Dimension && b instanceof tree.Color) {
1955        if (this.op === '*' || this.op === '+') {
1956            temp = b, b = a, a = temp;
1957        } else {
1958            throw { name: "OperationError",
1959                    message: "Can't substract or divide a color from a number" };
1960        }
1961    }
1962    return a.operate(this.op, b);
1963};
1964
1965tree.operate = function (op, a, b) {
1966    switch (op) {
1967        case '+': return a + b;
1968        case '-': return a - b;
1969        case '*': return a * b;
1970        case '/': return a / b;
1971    }
1972};
1973
1974})(require('less/tree'));
1975(function (tree) {
1976
1977tree.Quoted = function (str, content, escaped, i) {
1978    this.escaped = escaped;
1979    this.value = content || '';
1980    this.quote = str.charAt(0);
1981    this.index = i;
1982};
1983tree.Quoted.prototype = {
1984    toCSS: function () {
1985        if (this.escaped) {
1986            return this.value;
1987        } else {
1988            return this.quote + this.value + this.quote;
1989        }
1990    },
1991    eval: function (env) {
1992        this.value = this.value.replace(/@\{([\w-]+)\}/g, function (_, name) {
1993            return new(tree.Variable)('@' + name).eval(env).value;
1994        }).replace(/`([^`]+)`/g, function (_, exp) {
1995            return new(tree.JavaScript)(exp, this.index, true).eval(env).toCSS();
1996        });
1997        return this;
1998    }
1999};
2000
2001})(require('less/tree'));
2002(function (tree) {
2003
2004tree.Rule = function (name, value, important, index) {
2005    this.name = name;
2006    this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]);
2007    this.important = important ? ' ' + important.trim() : '';
2008    this.index = index;
2009
2010    if (name.charAt(0) === '@') {
2011        this.variable = true;
2012    } else { this.variable = false }
2013};
2014tree.Rule.prototype.toCSS = function (env) {
2015    if (this.variable) { return "" }
2016    else {
2017        return this.name + (env.compress ? ':' : ': ') +
2018               this.value.toCSS(env) +
2019               this.important + ";";
2020    }
2021};
2022
2023tree.Rule.prototype.eval = function (context) {
2024    return new(tree.Rule)(this.name, this.value.eval(context), this.important, this.index);
2025};
2026
2027tree.Shorthand = function (a, b) {
2028    this.a = a;
2029    this.b = b;
2030};
2031
2032tree.Shorthand.prototype = {
2033    toCSS: function (env) {
2034        return this.a.toCSS(env) + "/" + this.b.toCSS(env);
2035    },
2036    eval: function () { return this }
2037};
2038
2039})(require('less/tree'));
2040(function (tree) {
2041
2042tree.Ruleset = function (selectors, rules) {
2043    this.selectors = selectors;
2044    this.rules = rules;
2045    this._lookups = {};
2046};
2047tree.Ruleset.prototype = {
2048    eval: function (env) {
2049        var ruleset = new(tree.Ruleset)(this.selectors, this.rules.slice(0));
2050
2051        ruleset.root = this.root;
2052
2053        // push the current ruleset to the frames stack
2054        env.frames.unshift(ruleset);
2055
2056        // Evaluate imports
2057        if (ruleset.root) {
2058            for (var i = 0; i < ruleset.rules.length; i++) {
2059                if (ruleset.rules[i] instanceof tree.Import) {
2060                    Array.prototype.splice
2061                         .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env)));
2062                }
2063            }
2064        }
2065
2066        // Store the frames around mixin definitions,
2067        // so they can be evaluated like closures when the time comes.
2068        for (var i = 0; i < ruleset.rules.length; i++) {
2069            if (ruleset.rules[i] instanceof tree.mixin.Definition) {
2070                ruleset.rules[i].frames = env.frames.slice(0);
2071            }
2072        }
2073
2074        // Evaluate mixin calls.
2075        for (var i = 0; i < ruleset.rules.length; i++) {
2076            if (ruleset.rules[i] instanceof tree.mixin.Call) {
2077                Array.prototype.splice
2078                     .apply(ruleset.rules, [i, 1].concat(ruleset.rules[i].eval(env)));
2079            }
2080        }
2081
2082        // Evaluate everything else
2083        for (var i = 0, rule; i < ruleset.rules.length; i++) {
2084            rule = ruleset.rules[i];
2085
2086            if (! (rule instanceof tree.mixin.Definition)) {
2087                ruleset.rules[i] = rule.eval ? rule.eval(env) : rule;
2088            }
2089        }
2090
2091        // Pop the stack
2092        env.frames.shift();
2093
2094        return ruleset;
2095    },
2096    match: function (args) {
2097        return !args || args.length === 0;
2098    },
2099    variables: function () {
2100        if (this._variables) { return this._variables }
2101        else {
2102            return this._variables = this.rules.reduce(function (hash, r) {
2103                if (r instanceof tree.Rule && r.variable === true) {
2104                    hash[r.name] = r;
2105                }
2106                return hash;
2107            }, {});
2108        }
2109    },
2110    variable: function (name) {
2111        return this.variables()[name];
2112    },
2113    rulesets: function () {
2114        if (this._rulesets) { return this._rulesets }
2115        else {
2116            return this._rulesets = this.rules.filter(function (r) {
2117                return (r instanceof tree.Ruleset) || (r instanceof tree.mixin.Definition);
2118            });
2119        }
2120    },
2121    find: function (selector, self) {
2122        self = self || this;
2123        var rules = [], rule, match,
2124            key = selector.toCSS();
2125
2126        if (key in this._lookups) { return this._lookups[key] }
2127
2128        this.rulesets().forEach(function (rule) {
2129            if (rule !== self) {
2130                for (var j = 0; j < rule.selectors.length; j++) {
2131                    if (match = selector.match(rule.selectors[j])) {
2132                        if (selector.elements.length > 1) {
2133                            Array.prototype.push.apply(rules, rule.find(
2134                                new(tree.Selector)(selector.elements.slice(1)), self));
2135                        } else {
2136                            rules.push(rule);
2137                        }
2138                        break;
2139                    }
2140                }
2141            }
2142        });
2143        return this._lookups[key] = rules;
2144    },
2145    //
2146    // Entry point for code generation
2147    //
2148    //     `context` holds an array of arrays.
2149    //
2150    toCSS: function (context, env) {
2151        var css = [],      // The CSS output
2152            rules = [],    // node.Rule instances
2153            rulesets = [], // node.Ruleset instances
2154            paths = [],    // Current selectors
2155            selector,      // The fully rendered selector
2156            rule;
2157
2158        if (! this.root) {
2159            if (context.length === 0) {
2160                paths = this.selectors.map(function (s) { return [s] });
2161            } else {
2162                for (var s = 0; s < this.selectors.length; s++) {
2163                    for (var c = 0; c < context.length; c++) {
2164                        paths.push(context[c].concat([this.selectors[s]]));
2165                    }
2166                }
2167            }
2168        }
2169
2170        // Compile rules and rulesets
2171        for (var i = 0; i < this.rules.length; i++) {
2172            rule = this.rules[i];
2173
2174            if (rule.rules || (rule instanceof tree.Directive)) {
2175                rulesets.push(rule.toCSS(paths, env));
2176            } else if (rule instanceof tree.Comment) {
2177                if (!rule.silent) {
2178                    if (this.root) {
2179                        rulesets.push(rule.toCSS(env));
2180                    } else {
2181                        rules.push(rule.toCSS(env));
2182                    }
2183                }
2184            } else {
2185                if (rule.toCSS && !rule.variable) {
2186                    rules.push(rule.toCSS(env));
2187                } else if (rule.value && !rule.variable) {
2188                    rules.push(rule.value.toString());
2189                }
2190            }
2191        }
2192
2193        rulesets = rulesets.join('');
2194
2195        // If this is the root node, we don't render
2196        // a selector, or {}.
2197        // Otherwise, only output if this ruleset has rules.
2198        if (this.root) {
2199            css.push(rules.join(env.compress ? '' : '\n'));
2200        } else {
2201            if (rules.length > 0) {
2202                selector = paths.map(function (p) {
2203                    return p.map(function (s) {
2204                        return s.toCSS(env);
2205                    }).join('').trim();
2206                }).join(env.compress ? ',' : (paths.length > 3 ? ',\n' : ', '));
2207                css.push(selector,
2208                        (env.compress ? '{' : ' {\n  ') +
2209                        rules.join(env.compress ? '' : '\n  ') +
2210                        (env.compress ? '}' : '\n}\n'));
2211            }
2212        }
2213        css.push(rulesets);
2214
2215        return css.join('') + (env.compress ? '\n' : '');
2216    }
2217};
2218})(require('less/tree'));
2219(function (tree) {
2220
2221tree.Selector = function (elements) {
2222    this.elements = elements;
2223    if (this.elements[0].combinator.value === "") {
2224        this.elements[0].combinator.value = ' ';
2225    }
2226};
2227tree.Selector.prototype.match = function (other) {
2228    if (this.elements[0].value === other.elements[0].value) {
2229        return true;
2230    } else {
2231        return false;
2232    }
2233};
2234tree.Selector.prototype.toCSS = function (env) {
2235    if (this._css) { return this._css }
2236
2237    return this._css = this.elements.map(function (e) {
2238        if (typeof(e) === 'string') {
2239            return ' ' + e.trim();
2240        } else {
2241            return e.toCSS(env);
2242        }
2243    }).join('');
2244};
2245
2246})(require('less/tree'));
2247(function (tree) {
2248
2249tree.URL = function (val, paths) {
2250    if (val.data) {
2251        this.attrs = val;
2252    } else {
2253        // Add the base path if the URL is relative and we are in the browser
2254        if (!/^(?:https?:\/|file:\/|data:\/)?\//.test(val.value) && paths.length > 0 && typeof(window) !== 'undefined') {
2255            val.value = paths[0] + (val.value.charAt(0) === '/' ? val.value.slice(1) : val.value);
2256        }
2257        this.value = val;
2258        this.paths = paths;
2259    }
2260};
2261tree.URL.prototype = {
2262    toCSS: function () {
2263        return "url(" + (this.attrs ? 'data:' + this.attrs.mime + this.attrs.charset + this.attrs.base64 + this.attrs.data
2264                                    : this.value.toCSS()) + ")";
2265    },
2266    eval: function (ctx) {
2267        return this.attrs ? this : new(tree.URL)(this.value.eval(ctx), this.paths);
2268    }
2269};
2270
2271})(require('less/tree'));
2272(function (tree) {
2273
2274tree.Value = function (value) {
2275    this.value = value;
2276    this.is = 'value';
2277};
2278tree.Value.prototype = {
2279    eval: function (env) {
2280        if (this.value.length === 1) {
2281            return this.value[0].eval(env);
2282        } else {
2283            return new(tree.Value)(this.value.map(function (v) {
2284                return v.eval(env);
2285            }));
2286        }
2287    },
2288    toCSS: function (env) {
2289        return this.value.map(function (e) {
2290            return e.toCSS(env);
2291        }).join(env.compress ? ',' : ', ');
2292    }
2293};
2294
2295})(require('less/tree'));
2296(function (tree) {
2297
2298tree.Variable = function (name, index) { this.name = name, this.index = index };
2299tree.Variable.prototype = {
2300    eval: function (env) {
2301        var variable, v, name = this.name;
2302
2303        if (name.indexOf('@@') == 0) {
2304            name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value;
2305        }
2306
2307        if (variable = tree.find(env.frames, function (frame) {
2308            if (v = frame.variable(name)) {
2309                return v.value.eval(env);
2310            }
2311        })) { return variable }
2312        else {
2313            throw { message: "variable " + name + " is undefined",
2314                    index: this.index };
2315        }
2316    }
2317};
2318
2319})(require('less/tree'));
2320require('less/tree').find = function (obj, fun) {
2321    for (var i = 0, r; i < obj.length; i++) {
2322        if (r = fun.call(obj, obj[i])) { return r }
2323    }
2324    return null;
2325};
2326//
2327// browser.js - client-side engine
2328//
2329
2330var isFileProtocol = (location.protocol === 'file:'    ||
2331                      location.protocol === 'chrome:'  ||
2332                      location.protocol === 'chrome-extension:'  ||
2333                      location.protocol === 'resource:');
2334
2335less.env = less.env || (location.hostname == '127.0.0.1' ||
2336                        location.hostname == '0.0.0.0'   ||
2337                        location.hostname == 'localhost' ||
2338                        location.port.length > 0         ||
2339                        isFileProtocol                   ? 'development'
2340                                                         : 'production');
2341
2342// Load styles asynchronously (default: false)
2343//
2344// This is set to `false` by default, so that the body
2345// doesn't start loading before the stylesheets are parsed.
2346// Setting this to `true` can result in flickering.
2347//
2348less.async = false;
2349
2350// Interval between watch polls
2351less.poll = less.poll || (isFileProtocol ? 1000 : 1500);
2352
2353//
2354// Watch mode
2355//
2356less.watch   = function () { return this.watchMode = true };
2357less.unwatch = function () { return this.watchMode = false };
2358
2359if (less.env === 'development') {
2360    less.optimization = 0;
2361
2362    if (/!watch/.test(location.hash)) {
2363        less.watch();
2364    }
2365    less.watchTimer = setInterval(function () {
2366        if (less.watchMode) {
2367            loadStyleSheets(function (root, sheet, env) {
2368                if (root) {
2369                    createCSS(root.toCSS(), sheet, env.lastModified);
2370                }
2371            });
2372        }
2373    }, less.poll);
2374} else {
2375    less.optimization = 3;
2376}
2377
2378var cache;
2379
2380try {
2381    cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage;
2382} catch (_) {
2383    cache = null;
2384}
2385
2386//
2387// Get all <link> tags with the 'rel' attribute set to "stylesheet/less"
2388//
2389var links = document.getElementsByTagName('link');
2390var typePattern = /^text\/(x-)?less$/;
2391
2392less.sheets = [];
2393
2394for (var i = 0; i < links.length; i++) {
2395    if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) &&
2396       (links[i].type.match(typePattern)))) {
2397        less.sheets.push(links[i]);
2398    }
2399}
2400
2401
2402less.refresh = function (reload) {
2403    var startTime, endTime;
2404    startTime = endTime = new(Date);
2405
2406    loadStyleSheets(function (root, sheet, env) {
2407        if (env.local) {
2408            log("loading " + sheet.href + " from cache.");
2409        } else {
2410            log("parsed " + sheet.href + " successfully.");
2411            createCSS(root.toCSS(), sheet, env.lastModified);
2412        }
2413        log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms');
2414        (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms');
2415        endTime = new(Date);
2416    }, reload);
2417
2418    loadStyles();
2419};
2420less.refreshStyles = loadStyles;
2421
2422less.refresh(less.env === 'development');
2423
2424function loadStyles() {
2425    var styles = document.getElementsByTagName('style');
2426    for (var i = 0; i < styles.length; i++) {
2427        if (styles[i].type.match(typePattern)) {
2428            new(less.Parser)().parse(styles[i].innerHTML || '', function (e, tree) {
2429                styles[i].type      = 'text/css';
2430                styles[i].innerHTML = tree.toCSS();
2431            });
2432        }
2433    }
2434}
2435
2436function loadStyleSheets(callback, reload) {
2437    for (var i = 0; i < less.sheets.length; i++) {
2438        loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1));
2439    }
2440}
2441
2442function loadStyleSheet(sheet, callback, reload, remaining) {
2443    var url       = window.location.href.replace(/[#?].*$/, '');
2444    var href      = sheet.href.replace(/\?.*$/, '');
2445    var css       = cache && cache.getItem(href);
2446    var timestamp = cache && cache.getItem(href + ':timestamp');
2447    var styles    = { css: css, timestamp: timestamp };
2448
2449    // Stylesheets in IE don't always return the full path
2450    if (! /^(https?|file):/.test(href)) {
2451        if (href.charAt(0) == "/") {
2452            href = window.location.protocol + "//" + window.location.host + href;
2453        } else {
2454            href = url.slice(0, url.lastIndexOf('/') + 1) + href;
2455        }
2456    }
2457
2458    xhr(sheet.href, sheet.type, function (data, lastModified) {
2459        if (!reload && styles && lastModified &&
2460           (new(Date)(lastModified).valueOf() ===
2461            new(Date)(styles.timestamp).valueOf())) {
2462            // Use local copy
2463            createCSS(styles.css, sheet);
2464            callback(null, sheet, { local: true, remaining: remaining });
2465        } else {
2466            // Use remote copy (re-parse)
2467            try {
2468                new(less.Parser)({
2469                    optimization: less.optimization,
2470                    paths: [href.replace(/[\w\.-]+$/, '')],
2471                    mime: sheet.type
2472                }).parse(data, function (e, root) {
2473                    if (e) { return error(e, href) }
2474                    try {
2475                        callback(root, sheet, { local: false, lastModified: lastModified, remaining: remaining });
2476                        removeNode(document.getElementById('less-error-message:' + extractId(href)));
2477                    } catch (e) {
2478                        error(e, href);
2479                    }
2480                });
2481            } catch (e) {
2482                error(e, href);
2483            }
2484        }
2485    }, function (status, url) {
2486        throw new(Error)("Couldn't load " + url + " (" + status + ")");
2487    });
2488}
2489
2490function extractId(href) {
2491    return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' )  // Remove protocol & domain
2492               .replace(/^\//,                 '' )  // Remove root /
2493               .replace(/\?.*$/,               '' )  // Remove query
2494               .replace(/\.[^\.\/]+$/,         '' )  // Remove file extension
2495               .replace(/[^\.\w-]+/g,          '-')  // Replace illegal characters
2496               .replace(/\./g,                 ':'); // Replace dots with colons(for valid id)
2497}
2498
2499function createCSS(styles, sheet, lastModified) {
2500    var css;
2501
2502    // Strip the query-string
2503    var href = sheet.href ? sheet.href.replace(/\?.*$/, '') : '';
2504
2505    // If there is no title set, use the filename, minus the extension
2506    var id = 'less:' + (sheet.title || extractId(href));
2507
2508    // If the stylesheet doesn't exist, create a new node
2509    if ((css = document.getElementById(id)) === null) {
2510        css = document.createElement('style');
2511        css.type = 'text/css';
2512        css.media = sheet.media || 'screen';
2513        css.id = id;
2514        document.getElementsByTagName('head')[0].appendChild(css);
2515    }
2516
2517    if (css.styleSheet) { // IE
2518        try {
2519            css.styleSheet.cssText = styles;
2520        } catch (e) {
2521            throw new(Error)("Couldn't reassign styleSheet.cssText.");
2522        }
2523    } else {
2524        (function (node) {
2525            if (css.childNodes.length > 0) {
2526                if (css.firstChild.nodeValue !== node.nodeValue) {
2527                    css.replaceChild(node, css.firstChild);
2528                }
2529            } else {
2530                css.appendChild(node);
2531            }
2532        })(document.createTextNode(styles));
2533    }
2534
2535    // Don't update the local store if the file wasn't modified
2536    if (lastModified && cache) {
2537        log('saving ' + href + ' to cache.');
2538        cache.setItem(href, styles);
2539        cache.setItem(href + ':timestamp', lastModified);
2540    }
2541}
2542
2543function xhr(url, type, callback, errback) {
2544    var xhr = getXMLHttpRequest();
2545    var async = isFileProtocol ? false : less.async;
2546
2547    if (typeof(xhr.overrideMimeType) === 'function') {
2548        xhr.overrideMimeType('text/css');
2549    }
2550    xhr.open('GET', url, async);
2551    xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5');
2552    xhr.send(null);
2553
2554    if (isFileProtocol) {
2555        if (xhr.status === 0) {
2556            callback(xhr.responseText);
2557        } else {
2558            errback(xhr.status, url);
2559        }
2560    } else if (async) {
2561        xhr.onreadystatechange = function () {
2562            if (xhr.readyState == 4) {
2563                handleResponse(xhr, callback, errback);
2564            }
2565        };
2566    } else {
2567        handleResponse(xhr, callback, errback);
2568    }
2569
2570    function handleResponse(xhr, callback, errback) {
2571        if (xhr.status >= 200 && xhr.status < 300) {
2572            callback(xhr.responseText,
2573                     xhr.getResponseHeader("Last-Modified"));
2574        } else if (typeof(errback) === 'function') {
2575            errback(xhr.status, url);
2576        }
2577    }
2578}
2579
2580function getXMLHttpRequest() {
2581    if (window.XMLHttpRequest) {
2582        return new(XMLHttpRequest);
2583    } else {
2584        try {
2585            return new(ActiveXObject)("MSXML2.XMLHTTP.3.0");
2586        } catch (e) {
2587            log("browser doesn't support AJAX.");
2588            return null;
2589        }
2590    }
2591}
2592
2593function removeNode(node) {
2594    return node && node.parentNode.removeChild(node);
2595}
2596
2597function log(str) {
2598    if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) }
2599}
2600
2601function error(e, href) {
2602    var id = 'less-error-message:' + extractId(href);
2603
2604    var template = ['<ul>',
2605                        '<li><label>[-1]</label><pre class="ctx">{0}</pre></li>',
2606                        '<li><label>[0]</label><pre>{current}</pre></li>',
2607                        '<li><label>[1]</label><pre class="ctx">{2}</pre></li>',
2608                    '</ul>'].join('\n');
2609
2610    var elem = document.createElement('div'), timer, content;
2611
2612    elem.id        = id;
2613    elem.className = "less-error-message";
2614
2615    content = '<h3>'  + (e.message || 'There is an error in your .less file') +
2616              '</h3>' + '<p><a href="' + href   + '">' + href + "</a> ";
2617
2618    if (e.extract) {
2619        content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':</p>' +
2620            template.replace(/\[(-?\d)\]/g, function (_, i) {
2621                return (parseInt(e.line) + parseInt(i)) || '';
2622            }).replace(/\{(\d)\}/g, function (_, i) {
2623                return e.extract[parseInt(i)] || '';
2624            }).replace(/\{current\}/, e.extract[1].slice(0, e.column) + '<span class="error">' +
2625                                      e.extract[1].slice(e.column)    + '</span>');
2626    }
2627    elem.innerHTML = content;
2628
2629    // CSS for error messages
2630    createCSS([
2631        '.less-error-message ul, .less-error-message li {',
2632            'list-style-type: none;',
2633            'margin-right: 15px;',
2634            'padding: 4px 0;',
2635            'margin: 0;',
2636        '}',
2637        '.less-error-message label {',
2638            'font-size: 12px;',
2639            'margin-right: 15px;',
2640            'padding: 4px 0;',
2641            'color: #cc7777;',
2642        '}',
2643        '.less-error-message pre {',
2644            'color: #ee4444;',
2645            'padding: 4px 0;',
2646            'margin: 0;',
2647            'display: inline-block;',
2648        '}',
2649        '.less-error-message pre.ctx {',
2650            'color: #dd4444;',
2651        '}',
2652        '.less-error-message h3 {',
2653            'font-size: 20px;',
2654            'font-weight: bold;',
2655            'padding: 15px 0 5px 0;',
2656            'margin: 0;',
2657        '}',
2658        '.less-error-message a {',
2659            'color: #10a',
2660        '}',
2661        '.less-error-message .error {',
2662            'color: red;',
2663            'font-weight: bold;',
2664            'padding-bottom: 2px;',
2665            'border-bottom: 1px dashed red;',
2666        '}'
2667    ].join('\n'), { title: 'error-message' });
2668
2669    elem.style.cssText = [
2670        "font-family: Arial, sans-serif",
2671        "border: 1px solid #e00",
2672        "background-color: #eee",
2673        "border-radius: 5px",
2674        "-webkit-border-radius: 5px",
2675        "-moz-border-radius: 5px",
2676        "color: #e00",
2677        "padding: 15px",
2678        "margin-bottom: 15px"
2679    ].join(';');
2680
2681    if (less.env == 'development') {
2682        timer = setInterval(function () {
2683            if (document.body) {
2684                if (document.getElementById(id)) {
2685                    document.body.replaceChild(elem, document.getElementById(id));
2686                } else {
2687                    document.body.insertBefore(elem, document.body.firstChild);
2688                }
2689                clearInterval(timer);
2690            }
2691        }, 10);
2692    }
2693}
2694
2695})(window);
Note: See TracBrowser for help on using the repository browser.