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