~george-edison55/less/trunk

« back to all changes in this revision

Viewing changes to dist/less-1.3.0.js

  • Committer: Nathan Osman
  • Date: 2013-04-16 22:43:51 UTC
  • Revision ID: admin@quickmediasolutions.com-20130416224351-5juqujuu4itkwpat
Initial commit.

Show diffs side-by-side

added added

removed removed

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