www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

less-1.0.35.js (84928B)


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