Projekt

Obecné

Profil

Stáhnout (291 KB) Statistiky
| Větev: | Tag: | Revize:
1
"no use strict";
2
!(function(window) {
3
if (typeof window.window != "undefined" && window.document)
4
    return;
5
if (window.require && window.define)
6
    return;
7

    
8
if (!window.console) {
9
    window.console = function() {
10
        var msgs = Array.prototype.slice.call(arguments, 0);
11
        postMessage({type: "log", data: msgs});
12
    };
13
    window.console.error =
14
    window.console.warn = 
15
    window.console.log =
16
    window.console.trace = window.console;
17
}
18
window.window = window;
19
window.ace = window;
20

    
21
window.onerror = function(message, file, line, col, err) {
22
    postMessage({type: "error", data: {
23
        message: message,
24
        data: err.data,
25
        file: file,
26
        line: line, 
27
        col: col,
28
        stack: err.stack
29
    }});
30
};
31

    
32
window.normalizeModule = function(parentId, moduleName) {
33
    // normalize plugin requires
34
    if (moduleName.indexOf("!") !== -1) {
35
        var chunks = moduleName.split("!");
36
        return window.normalizeModule(parentId, chunks[0]) + "!" + window.normalizeModule(parentId, chunks[1]);
37
    }
38
    // normalize relative requires
39
    if (moduleName.charAt(0) == ".") {
40
        var base = parentId.split("/").slice(0, -1).join("/");
41
        moduleName = (base ? base + "/" : "") + moduleName;
42
        
43
        while (moduleName.indexOf(".") !== -1 && previous != moduleName) {
44
            var previous = moduleName;
45
            moduleName = moduleName.replace(/^\.\//, "").replace(/\/\.\//, "/").replace(/[^\/]+\/\.\.\//, "");
46
        }
47
    }
48
    
49
    return moduleName;
50
};
51

    
52
window.require = function require(parentId, id) {
53
    if (!id) {
54
        id = parentId;
55
        parentId = null;
56
    }
57
    if (!id.charAt)
58
        throw new Error("worker.js require() accepts only (parentId, id) as arguments");
59

    
60
    id = window.normalizeModule(parentId, id);
61

    
62
    var module = window.require.modules[id];
63
    if (module) {
64
        if (!module.initialized) {
65
            module.initialized = true;
66
            module.exports = module.factory().exports;
67
        }
68
        return module.exports;
69
    }
70
   
71
    if (!window.require.tlns)
72
        return console.log("unable to load " + id);
73
    
74
    var path = resolveModuleId(id, window.require.tlns);
75
    if (path.slice(-3) != ".js") path += ".js";
76
    
77
    window.require.id = id;
78
    window.require.modules[id] = {}; // prevent infinite loop on broken modules
79
    importScripts(path);
80
    return window.require(parentId, id);
81
};
82
function resolveModuleId(id, paths) {
83
    var testPath = id, tail = "";
84
    while (testPath) {
85
        var alias = paths[testPath];
86
        if (typeof alias == "string") {
87
            return alias + tail;
88
        } else if (alias) {
89
            return  alias.location.replace(/\/*$/, "/") + (tail || alias.main || alias.name);
90
        } else if (alias === false) {
91
            return "";
92
        }
93
        var i = testPath.lastIndexOf("/");
94
        if (i === -1) break;
95
        tail = testPath.substr(i) + tail;
96
        testPath = testPath.slice(0, i);
97
    }
98
    return id;
99
}
100
window.require.modules = {};
101
window.require.tlns = {};
102

    
103
window.define = function(id, deps, factory) {
104
    if (arguments.length == 2) {
105
        factory = deps;
106
        if (typeof id != "string") {
107
            deps = id;
108
            id = window.require.id;
109
        }
110
    } else if (arguments.length == 1) {
111
        factory = id;
112
        deps = [];
113
        id = window.require.id;
114
    }
115
    
116
    if (typeof factory != "function") {
117
        window.require.modules[id] = {
118
            exports: factory,
119
            initialized: true
120
        };
121
        return;
122
    }
123

    
124
    if (!deps.length)
125
        // If there is no dependencies, we inject "require", "exports" and
126
        // "module" as dependencies, to provide CommonJS compatibility.
127
        deps = ["require", "exports", "module"];
128

    
129
    var req = function(childId) {
130
        return window.require(id, childId);
131
    };
132

    
133
    window.require.modules[id] = {
134
        exports: {},
135
        factory: function() {
136
            var module = this;
137
            var returnExports = factory.apply(this, deps.slice(0, factory.length).map(function(dep) {
138
                switch (dep) {
139
                    // Because "require", "exports" and "module" aren't actual
140
                    // dependencies, we must handle them seperately.
141
                    case "require": return req;
142
                    case "exports": return module.exports;
143
                    case "module":  return module;
144
                    // But for all other dependencies, we can just go ahead and
145
                    // require them.
146
                    default:        return req(dep);
147
                }
148
            }));
149
            if (returnExports)
150
                module.exports = returnExports;
151
            return module;
152
        }
153
    };
154
};
155
window.define.amd = {};
156
require.tlns = {};
157
window.initBaseUrls  = function initBaseUrls(topLevelNamespaces) {
158
    for (var i in topLevelNamespaces)
159
        require.tlns[i] = topLevelNamespaces[i];
160
};
161

    
162
window.initSender = function initSender() {
163

    
164
    var EventEmitter = window.require("ace/lib/event_emitter").EventEmitter;
165
    var oop = window.require("ace/lib/oop");
166
    
167
    var Sender = function() {};
168
    
169
    (function() {
170
        
171
        oop.implement(this, EventEmitter);
172
                
173
        this.callback = function(data, callbackId) {
174
            postMessage({
175
                type: "call",
176
                id: callbackId,
177
                data: data
178
            });
179
        };
180
    
181
        this.emit = function(name, data) {
182
            postMessage({
183
                type: "event",
184
                name: name,
185
                data: data
186
            });
187
        };
188
        
189
    }).call(Sender.prototype);
190
    
191
    return new Sender();
192
};
193

    
194
var main = window.main = null;
195
var sender = window.sender = null;
196

    
197
window.onmessage = function(e) {
198
    var msg = e.data;
199
    if (msg.event && sender) {
200
        sender._signal(msg.event, msg.data);
201
    }
202
    else if (msg.command) {
203
        if (main[msg.command])
204
            main[msg.command].apply(main, msg.args);
205
        else if (window[msg.command])
206
            window[msg.command].apply(window, msg.args);
207
        else
208
            throw new Error("Unknown command:" + msg.command);
209
    }
210
    else if (msg.init) {
211
        window.initBaseUrls(msg.tlns);
212
        require("ace/lib/es5-shim");
213
        sender = window.sender = window.initSender();
214
        var clazz = require(msg.module)[msg.classname];
215
        main = window.main = new clazz(sender);
216
    }
217
};
218
})(this);
219

    
220
ace.define("ace/lib/oop",[], function(require, exports, module) {
221
"use strict";
222

    
223
exports.inherits = function(ctor, superCtor) {
224
    ctor.super_ = superCtor;
225
    ctor.prototype = Object.create(superCtor.prototype, {
226
        constructor: {
227
            value: ctor,
228
            enumerable: false,
229
            writable: true,
230
            configurable: true
231
        }
232
    });
233
};
234

    
235
exports.mixin = function(obj, mixin) {
236
    for (var key in mixin) {
237
        obj[key] = mixin[key];
238
    }
239
    return obj;
240
};
241

    
242
exports.implement = function(proto, mixin) {
243
    exports.mixin(proto, mixin);
244
};
245

    
246
});
247

    
248
ace.define("ace/lib/lang",[], function(require, exports, module) {
249
"use strict";
250

    
251
exports.last = function(a) {
252
    return a[a.length - 1];
253
};
254

    
255
exports.stringReverse = function(string) {
256
    return string.split("").reverse().join("");
257
};
258

    
259
exports.stringRepeat = function (string, count) {
260
    var result = '';
261
    while (count > 0) {
262
        if (count & 1)
263
            result += string;
264

    
265
        if (count >>= 1)
266
            string += string;
267
    }
268
    return result;
269
};
270

    
271
var trimBeginRegexp = /^\s\s*/;
272
var trimEndRegexp = /\s\s*$/;
273

    
274
exports.stringTrimLeft = function (string) {
275
    return string.replace(trimBeginRegexp, '');
276
};
277

    
278
exports.stringTrimRight = function (string) {
279
    return string.replace(trimEndRegexp, '');
280
};
281

    
282
exports.copyObject = function(obj) {
283
    var copy = {};
284
    for (var key in obj) {
285
        copy[key] = obj[key];
286
    }
287
    return copy;
288
};
289

    
290
exports.copyArray = function(array){
291
    var copy = [];
292
    for (var i=0, l=array.length; i<l; i++) {
293
        if (array[i] && typeof array[i] == "object")
294
            copy[i] = this.copyObject(array[i]);
295
        else 
296
            copy[i] = array[i];
297
    }
298
    return copy;
299
};
300

    
301
exports.deepCopy = function deepCopy(obj) {
302
    if (typeof obj !== "object" || !obj)
303
        return obj;
304
    var copy;
305
    if (Array.isArray(obj)) {
306
        copy = [];
307
        for (var key = 0; key < obj.length; key++) {
308
            copy[key] = deepCopy(obj[key]);
309
        }
310
        return copy;
311
    }
312
    if (Object.prototype.toString.call(obj) !== "[object Object]")
313
        return obj;
314
    
315
    copy = {};
316
    for (var key in obj)
317
        copy[key] = deepCopy(obj[key]);
318
    return copy;
319
};
320

    
321
exports.arrayToMap = function(arr) {
322
    var map = {};
323
    for (var i=0; i<arr.length; i++) {
324
        map[arr[i]] = 1;
325
    }
326
    return map;
327

    
328
};
329

    
330
exports.createMap = function(props) {
331
    var map = Object.create(null);
332
    for (var i in props) {
333
        map[i] = props[i];
334
    }
335
    return map;
336
};
337
exports.arrayRemove = function(array, value) {
338
  for (var i = 0; i <= array.length; i++) {
339
    if (value === array[i]) {
340
      array.splice(i, 1);
341
    }
342
  }
343
};
344

    
345
exports.escapeRegExp = function(str) {
346
    return str.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1');
347
};
348

    
349
exports.escapeHTML = function(str) {
350
    return ("" + str).replace(/&/g, "&#38;").replace(/"/g, "&#34;").replace(/'/g, "&#39;").replace(/</g, "&#60;");
351
};
352

    
353
exports.getMatchOffsets = function(string, regExp) {
354
    var matches = [];
355

    
356
    string.replace(regExp, function(str) {
357
        matches.push({
358
            offset: arguments[arguments.length-2],
359
            length: str.length
360
        });
361
    });
362

    
363
    return matches;
364
};
365
exports.deferredCall = function(fcn) {
366
    var timer = null;
367
    var callback = function() {
368
        timer = null;
369
        fcn();
370
    };
371

    
372
    var deferred = function(timeout) {
373
        deferred.cancel();
374
        timer = setTimeout(callback, timeout || 0);
375
        return deferred;
376
    };
377

    
378
    deferred.schedule = deferred;
379

    
380
    deferred.call = function() {
381
        this.cancel();
382
        fcn();
383
        return deferred;
384
    };
385

    
386
    deferred.cancel = function() {
387
        clearTimeout(timer);
388
        timer = null;
389
        return deferred;
390
    };
391
    
392
    deferred.isPending = function() {
393
        return timer;
394
    };
395

    
396
    return deferred;
397
};
398

    
399

    
400
exports.delayedCall = function(fcn, defaultTimeout) {
401
    var timer = null;
402
    var callback = function() {
403
        timer = null;
404
        fcn();
405
    };
406

    
407
    var _self = function(timeout) {
408
        if (timer == null)
409
            timer = setTimeout(callback, timeout || defaultTimeout);
410
    };
411

    
412
    _self.delay = function(timeout) {
413
        timer && clearTimeout(timer);
414
        timer = setTimeout(callback, timeout || defaultTimeout);
415
    };
416
    _self.schedule = _self;
417

    
418
    _self.call = function() {
419
        this.cancel();
420
        fcn();
421
    };
422

    
423
    _self.cancel = function() {
424
        timer && clearTimeout(timer);
425
        timer = null;
426
    };
427

    
428
    _self.isPending = function() {
429
        return timer;
430
    };
431

    
432
    return _self;
433
};
434
});
435

    
436
ace.define("ace/range",[], function(require, exports, module) {
437
"use strict";
438
var comparePoints = function(p1, p2) {
439
    return p1.row - p2.row || p1.column - p2.column;
440
};
441
var Range = function(startRow, startColumn, endRow, endColumn) {
442
    this.start = {
443
        row: startRow,
444
        column: startColumn
445
    };
446

    
447
    this.end = {
448
        row: endRow,
449
        column: endColumn
450
    };
451
};
452

    
453
(function() {
454
    this.isEqual = function(range) {
455
        return this.start.row === range.start.row &&
456
            this.end.row === range.end.row &&
457
            this.start.column === range.start.column &&
458
            this.end.column === range.end.column;
459
    };
460
    this.toString = function() {
461
        return ("Range: [" + this.start.row + "/" + this.start.column +
462
            "] -> [" + this.end.row + "/" + this.end.column + "]");
463
    };
464

    
465
    this.contains = function(row, column) {
466
        return this.compare(row, column) == 0;
467
    };
468
    this.compareRange = function(range) {
469
        var cmp,
470
            end = range.end,
471
            start = range.start;
472

    
473
        cmp = this.compare(end.row, end.column);
474
        if (cmp == 1) {
475
            cmp = this.compare(start.row, start.column);
476
            if (cmp == 1) {
477
                return 2;
478
            } else if (cmp == 0) {
479
                return 1;
480
            } else {
481
                return 0;
482
            }
483
        } else if (cmp == -1) {
484
            return -2;
485
        } else {
486
            cmp = this.compare(start.row, start.column);
487
            if (cmp == -1) {
488
                return -1;
489
            } else if (cmp == 1) {
490
                return 42;
491
            } else {
492
                return 0;
493
            }
494
        }
495
    };
496
    this.comparePoint = function(p) {
497
        return this.compare(p.row, p.column);
498
    };
499
    this.containsRange = function(range) {
500
        return this.comparePoint(range.start) == 0 && this.comparePoint(range.end) == 0;
501
    };
502
    this.intersects = function(range) {
503
        var cmp = this.compareRange(range);
504
        return (cmp == -1 || cmp == 0 || cmp == 1);
505
    };
506
    this.isEnd = function(row, column) {
507
        return this.end.row == row && this.end.column == column;
508
    };
509
    this.isStart = function(row, column) {
510
        return this.start.row == row && this.start.column == column;
511
    };
512
    this.setStart = function(row, column) {
513
        if (typeof row == "object") {
514
            this.start.column = row.column;
515
            this.start.row = row.row;
516
        } else {
517
            this.start.row = row;
518
            this.start.column = column;
519
        }
520
    };
521
    this.setEnd = function(row, column) {
522
        if (typeof row == "object") {
523
            this.end.column = row.column;
524
            this.end.row = row.row;
525
        } else {
526
            this.end.row = row;
527
            this.end.column = column;
528
        }
529
    };
530
    this.inside = function(row, column) {
531
        if (this.compare(row, column) == 0) {
532
            if (this.isEnd(row, column) || this.isStart(row, column)) {
533
                return false;
534
            } else {
535
                return true;
536
            }
537
        }
538
        return false;
539
    };
540
    this.insideStart = function(row, column) {
541
        if (this.compare(row, column) == 0) {
542
            if (this.isEnd(row, column)) {
543
                return false;
544
            } else {
545
                return true;
546
            }
547
        }
548
        return false;
549
    };
550
    this.insideEnd = function(row, column) {
551
        if (this.compare(row, column) == 0) {
552
            if (this.isStart(row, column)) {
553
                return false;
554
            } else {
555
                return true;
556
            }
557
        }
558
        return false;
559
    };
560
    this.compare = function(row, column) {
561
        if (!this.isMultiLine()) {
562
            if (row === this.start.row) {
563
                return column < this.start.column ? -1 : (column > this.end.column ? 1 : 0);
564
            }
565
        }
566

    
567
        if (row < this.start.row)
568
            return -1;
569

    
570
        if (row > this.end.row)
571
            return 1;
572

    
573
        if (this.start.row === row)
574
            return column >= this.start.column ? 0 : -1;
575

    
576
        if (this.end.row === row)
577
            return column <= this.end.column ? 0 : 1;
578

    
579
        return 0;
580
    };
581
    this.compareStart = function(row, column) {
582
        if (this.start.row == row && this.start.column == column) {
583
            return -1;
584
        } else {
585
            return this.compare(row, column);
586
        }
587
    };
588
    this.compareEnd = function(row, column) {
589
        if (this.end.row == row && this.end.column == column) {
590
            return 1;
591
        } else {
592
            return this.compare(row, column);
593
        }
594
    };
595
    this.compareInside = function(row, column) {
596
        if (this.end.row == row && this.end.column == column) {
597
            return 1;
598
        } else if (this.start.row == row && this.start.column == column) {
599
            return -1;
600
        } else {
601
            return this.compare(row, column);
602
        }
603
    };
604
    this.clipRows = function(firstRow, lastRow) {
605
        if (this.end.row > lastRow)
606
            var end = {row: lastRow + 1, column: 0};
607
        else if (this.end.row < firstRow)
608
            var end = {row: firstRow, column: 0};
609

    
610
        if (this.start.row > lastRow)
611
            var start = {row: lastRow + 1, column: 0};
612
        else if (this.start.row < firstRow)
613
            var start = {row: firstRow, column: 0};
614

    
615
        return Range.fromPoints(start || this.start, end || this.end);
616
    };
617
    this.extend = function(row, column) {
618
        var cmp = this.compare(row, column);
619

    
620
        if (cmp == 0)
621
            return this;
622
        else if (cmp == -1)
623
            var start = {row: row, column: column};
624
        else
625
            var end = {row: row, column: column};
626

    
627
        return Range.fromPoints(start || this.start, end || this.end);
628
    };
629

    
630
    this.isEmpty = function() {
631
        return (this.start.row === this.end.row && this.start.column === this.end.column);
632
    };
633
    this.isMultiLine = function() {
634
        return (this.start.row !== this.end.row);
635
    };
636
    this.clone = function() {
637
        return Range.fromPoints(this.start, this.end);
638
    };
639
    this.collapseRows = function() {
640
        if (this.end.column == 0)
641
            return new Range(this.start.row, 0, Math.max(this.start.row, this.end.row-1), 0);
642
        else
643
            return new Range(this.start.row, 0, this.end.row, 0);
644
    };
645
    this.toScreenRange = function(session) {
646
        var screenPosStart = session.documentToScreenPosition(this.start);
647
        var screenPosEnd = session.documentToScreenPosition(this.end);
648

    
649
        return new Range(
650
            screenPosStart.row, screenPosStart.column,
651
            screenPosEnd.row, screenPosEnd.column
652
        );
653
    };
654
    this.moveBy = function(row, column) {
655
        this.start.row += row;
656
        this.start.column += column;
657
        this.end.row += row;
658
        this.end.column += column;
659
    };
660

    
661
}).call(Range.prototype);
662
Range.fromPoints = function(start, end) {
663
    return new Range(start.row, start.column, end.row, end.column);
664
};
665
Range.comparePoints = comparePoints;
666

    
667
Range.comparePoints = function(p1, p2) {
668
    return p1.row - p2.row || p1.column - p2.column;
669
};
670

    
671

    
672
exports.Range = Range;
673
});
674

    
675
ace.define("ace/apply_delta",[], function(require, exports, module) {
676
"use strict";
677

    
678
function throwDeltaError(delta, errorText){
679
    console.log("Invalid Delta:", delta);
680
    throw "Invalid Delta: " + errorText;
681
}
682

    
683
function positionInDocument(docLines, position) {
684
    return position.row    >= 0 && position.row    <  docLines.length &&
685
           position.column >= 0 && position.column <= docLines[position.row].length;
686
}
687

    
688
function validateDelta(docLines, delta) {
689
    if (delta.action != "insert" && delta.action != "remove")
690
        throwDeltaError(delta, "delta.action must be 'insert' or 'remove'");
691
    if (!(delta.lines instanceof Array))
692
        throwDeltaError(delta, "delta.lines must be an Array");
693
    if (!delta.start || !delta.end)
694
       throwDeltaError(delta, "delta.start/end must be an present");
695
    var start = delta.start;
696
    if (!positionInDocument(docLines, delta.start))
697
        throwDeltaError(delta, "delta.start must be contained in document");
698
    var end = delta.end;
699
    if (delta.action == "remove" && !positionInDocument(docLines, end))
700
        throwDeltaError(delta, "delta.end must contained in document for 'remove' actions");
701
    var numRangeRows = end.row - start.row;
702
    var numRangeLastLineChars = (end.column - (numRangeRows == 0 ? start.column : 0));
703
    if (numRangeRows != delta.lines.length - 1 || delta.lines[numRangeRows].length != numRangeLastLineChars)
704
        throwDeltaError(delta, "delta.range must match delta lines");
705
}
706

    
707
exports.applyDelta = function(docLines, delta, doNotValidate) {
708
    
709
    var row = delta.start.row;
710
    var startColumn = delta.start.column;
711
    var line = docLines[row] || "";
712
    switch (delta.action) {
713
        case "insert":
714
            var lines = delta.lines;
715
            if (lines.length === 1) {
716
                docLines[row] = line.substring(0, startColumn) + delta.lines[0] + line.substring(startColumn);
717
            } else {
718
                var args = [row, 1].concat(delta.lines);
719
                docLines.splice.apply(docLines, args);
720
                docLines[row] = line.substring(0, startColumn) + docLines[row];
721
                docLines[row + delta.lines.length - 1] += line.substring(startColumn);
722
            }
723
            break;
724
        case "remove":
725
            var endColumn = delta.end.column;
726
            var endRow = delta.end.row;
727
            if (row === endRow) {
728
                docLines[row] = line.substring(0, startColumn) + line.substring(endColumn);
729
            } else {
730
                docLines.splice(
731
                    row, endRow - row + 1,
732
                    line.substring(0, startColumn) + docLines[endRow].substring(endColumn)
733
                );
734
            }
735
            break;
736
    }
737
};
738
});
739

    
740
ace.define("ace/lib/event_emitter",[], function(require, exports, module) {
741
"use strict";
742

    
743
var EventEmitter = {};
744
var stopPropagation = function() { this.propagationStopped = true; };
745
var preventDefault = function() { this.defaultPrevented = true; };
746

    
747
EventEmitter._emit =
748
EventEmitter._dispatchEvent = function(eventName, e) {
749
    this._eventRegistry || (this._eventRegistry = {});
750
    this._defaultHandlers || (this._defaultHandlers = {});
751

    
752
    var listeners = this._eventRegistry[eventName] || [];
753
    var defaultHandler = this._defaultHandlers[eventName];
754
    if (!listeners.length && !defaultHandler)
755
        return;
756

    
757
    if (typeof e != "object" || !e)
758
        e = {};
759

    
760
    if (!e.type)
761
        e.type = eventName;
762
    if (!e.stopPropagation)
763
        e.stopPropagation = stopPropagation;
764
    if (!e.preventDefault)
765
        e.preventDefault = preventDefault;
766

    
767
    listeners = listeners.slice();
768
    for (var i=0; i<listeners.length; i++) {
769
        listeners[i](e, this);
770
        if (e.propagationStopped)
771
            break;
772
    }
773
    
774
    if (defaultHandler && !e.defaultPrevented)
775
        return defaultHandler(e, this);
776
};
777

    
778

    
779
EventEmitter._signal = function(eventName, e) {
780
    var listeners = (this._eventRegistry || {})[eventName];
781
    if (!listeners)
782
        return;
783
    listeners = listeners.slice();
784
    for (var i=0; i<listeners.length; i++)
785
        listeners[i](e, this);
786
};
787

    
788
EventEmitter.once = function(eventName, callback) {
789
    var _self = this;
790
    this.on(eventName, function newCallback() {
791
        _self.off(eventName, newCallback);
792
        callback.apply(null, arguments);
793
    });
794
    if (!callback) {
795
        return new Promise(function(resolve) {
796
            callback = resolve;
797
        });
798
    }
799
};
800

    
801

    
802
EventEmitter.setDefaultHandler = function(eventName, callback) {
803
    var handlers = this._defaultHandlers;
804
    if (!handlers)
805
        handlers = this._defaultHandlers = {_disabled_: {}};
806
    
807
    if (handlers[eventName]) {
808
        var old = handlers[eventName];
809
        var disabled = handlers._disabled_[eventName];
810
        if (!disabled)
811
            handlers._disabled_[eventName] = disabled = [];
812
        disabled.push(old);
813
        var i = disabled.indexOf(callback);
814
        if (i != -1) 
815
            disabled.splice(i, 1);
816
    }
817
    handlers[eventName] = callback;
818
};
819
EventEmitter.removeDefaultHandler = function(eventName, callback) {
820
    var handlers = this._defaultHandlers;
821
    if (!handlers)
822
        return;
823
    var disabled = handlers._disabled_[eventName];
824
    
825
    if (handlers[eventName] == callback) {
826
        if (disabled)
827
            this.setDefaultHandler(eventName, disabled.pop());
828
    } else if (disabled) {
829
        var i = disabled.indexOf(callback);
830
        if (i != -1)
831
            disabled.splice(i, 1);
832
    }
833
};
834

    
835
EventEmitter.on =
836
EventEmitter.addEventListener = function(eventName, callback, capturing) {
837
    this._eventRegistry = this._eventRegistry || {};
838

    
839
    var listeners = this._eventRegistry[eventName];
840
    if (!listeners)
841
        listeners = this._eventRegistry[eventName] = [];
842

    
843
    if (listeners.indexOf(callback) == -1)
844
        listeners[capturing ? "unshift" : "push"](callback);
845
    return callback;
846
};
847

    
848
EventEmitter.off =
849
EventEmitter.removeListener =
850
EventEmitter.removeEventListener = function(eventName, callback) {
851
    this._eventRegistry = this._eventRegistry || {};
852

    
853
    var listeners = this._eventRegistry[eventName];
854
    if (!listeners)
855
        return;
856

    
857
    var index = listeners.indexOf(callback);
858
    if (index !== -1)
859
        listeners.splice(index, 1);
860
};
861

    
862
EventEmitter.removeAllListeners = function(eventName) {
863
    if (!eventName) this._eventRegistry = this._defaultHandlers = undefined;
864
    if (this._eventRegistry) this._eventRegistry[eventName] = undefined;
865
    if (this._defaultHandlers) this._defaultHandlers[eventName] = undefined;
866
};
867

    
868
exports.EventEmitter = EventEmitter;
869

    
870
});
871

    
872
ace.define("ace/anchor",[], function(require, exports, module) {
873
"use strict";
874

    
875
var oop = require("./lib/oop");
876
var EventEmitter = require("./lib/event_emitter").EventEmitter;
877

    
878
var Anchor = exports.Anchor = function(doc, row, column) {
879
    this.$onChange = this.onChange.bind(this);
880
    this.attach(doc);
881
    
882
    if (typeof column == "undefined")
883
        this.setPosition(row.row, row.column);
884
    else
885
        this.setPosition(row, column);
886
};
887

    
888
(function() {
889

    
890
    oop.implement(this, EventEmitter);
891
    this.getPosition = function() {
892
        return this.$clipPositionToDocument(this.row, this.column);
893
    };
894
    this.getDocument = function() {
895
        return this.document;
896
    };
897
    this.$insertRight = false;
898
    this.onChange = function(delta) {
899
        if (delta.start.row == delta.end.row && delta.start.row != this.row)
900
            return;
901

    
902
        if (delta.start.row > this.row)
903
            return;
904
            
905
        var point = $getTransformedPoint(delta, {row: this.row, column: this.column}, this.$insertRight);
906
        this.setPosition(point.row, point.column, true);
907
    };
908
    
909
    function $pointsInOrder(point1, point2, equalPointsInOrder) {
910
        var bColIsAfter = equalPointsInOrder ? point1.column <= point2.column : point1.column < point2.column;
911
        return (point1.row < point2.row) || (point1.row == point2.row && bColIsAfter);
912
    }
913
            
914
    function $getTransformedPoint(delta, point, moveIfEqual) {
915
        var deltaIsInsert = delta.action == "insert";
916
        var deltaRowShift = (deltaIsInsert ? 1 : -1) * (delta.end.row    - delta.start.row);
917
        var deltaColShift = (deltaIsInsert ? 1 : -1) * (delta.end.column - delta.start.column);
918
        var deltaStart = delta.start;
919
        var deltaEnd = deltaIsInsert ? deltaStart : delta.end; // Collapse insert range.
920
        if ($pointsInOrder(point, deltaStart, moveIfEqual)) {
921
            return {
922
                row: point.row,
923
                column: point.column
924
            };
925
        }
926
        if ($pointsInOrder(deltaEnd, point, !moveIfEqual)) {
927
            return {
928
                row: point.row + deltaRowShift,
929
                column: point.column + (point.row == deltaEnd.row ? deltaColShift : 0)
930
            };
931
        }
932
        
933
        return {
934
            row: deltaStart.row,
935
            column: deltaStart.column
936
        };
937
    }
938
    this.setPosition = function(row, column, noClip) {
939
        var pos;
940
        if (noClip) {
941
            pos = {
942
                row: row,
943
                column: column
944
            };
945
        } else {
946
            pos = this.$clipPositionToDocument(row, column);
947
        }
948

    
949
        if (this.row == pos.row && this.column == pos.column)
950
            return;
951

    
952
        var old = {
953
            row: this.row,
954
            column: this.column
955
        };
956

    
957
        this.row = pos.row;
958
        this.column = pos.column;
959
        this._signal("change", {
960
            old: old,
961
            value: pos
962
        });
963
    };
964
    this.detach = function() {
965
        this.document.off("change", this.$onChange);
966
    };
967
    this.attach = function(doc) {
968
        this.document = doc || this.document;
969
        this.document.on("change", this.$onChange);
970
    };
971
    this.$clipPositionToDocument = function(row, column) {
972
        var pos = {};
973

    
974
        if (row >= this.document.getLength()) {
975
            pos.row = Math.max(0, this.document.getLength() - 1);
976
            pos.column = this.document.getLine(pos.row).length;
977
        }
978
        else if (row < 0) {
979
            pos.row = 0;
980
            pos.column = 0;
981
        }
982
        else {
983
            pos.row = row;
984
            pos.column = Math.min(this.document.getLine(pos.row).length, Math.max(0, column));
985
        }
986

    
987
        if (column < 0)
988
            pos.column = 0;
989

    
990
        return pos;
991
    };
992

    
993
}).call(Anchor.prototype);
994

    
995
});
996

    
997
ace.define("ace/document",[], function(require, exports, module) {
998
"use strict";
999

    
1000
var oop = require("./lib/oop");
1001
var applyDelta = require("./apply_delta").applyDelta;
1002
var EventEmitter = require("./lib/event_emitter").EventEmitter;
1003
var Range = require("./range").Range;
1004
var Anchor = require("./anchor").Anchor;
1005

    
1006
var Document = function(textOrLines) {
1007
    this.$lines = [""];
1008
    if (textOrLines.length === 0) {
1009
        this.$lines = [""];
1010
    } else if (Array.isArray(textOrLines)) {
1011
        this.insertMergedLines({row: 0, column: 0}, textOrLines);
1012
    } else {
1013
        this.insert({row: 0, column:0}, textOrLines);
1014
    }
1015
};
1016

    
1017
(function() {
1018

    
1019
    oop.implement(this, EventEmitter);
1020
    this.setValue = function(text) {
1021
        var len = this.getLength() - 1;
1022
        this.remove(new Range(0, 0, len, this.getLine(len).length));
1023
        this.insert({row: 0, column: 0}, text);
1024
    };
1025
    this.getValue = function() {
1026
        return this.getAllLines().join(this.getNewLineCharacter());
1027
    };
1028
    this.createAnchor = function(row, column) {
1029
        return new Anchor(this, row, column);
1030
    };
1031
    if ("aaa".split(/a/).length === 0) {
1032
        this.$split = function(text) {
1033
            return text.replace(/\r\n|\r/g, "\n").split("\n");
1034
        };
1035
    } else {
1036
        this.$split = function(text) {
1037
            return text.split(/\r\n|\r|\n/);
1038
        };
1039
    }
1040

    
1041

    
1042
    this.$detectNewLine = function(text) {
1043
        var match = text.match(/^.*?(\r\n|\r|\n)/m);
1044
        this.$autoNewLine = match ? match[1] : "\n";
1045
        this._signal("changeNewLineMode");
1046
    };
1047
    this.getNewLineCharacter = function() {
1048
        switch (this.$newLineMode) {
1049
          case "windows":
1050
            return "\r\n";
1051
          case "unix":
1052
            return "\n";
1053
          default:
1054
            return this.$autoNewLine || "\n";
1055
        }
1056
    };
1057

    
1058
    this.$autoNewLine = "";
1059
    this.$newLineMode = "auto";
1060
    this.setNewLineMode = function(newLineMode) {
1061
        if (this.$newLineMode === newLineMode)
1062
            return;
1063

    
1064
        this.$newLineMode = newLineMode;
1065
        this._signal("changeNewLineMode");
1066
    };
1067
    this.getNewLineMode = function() {
1068
        return this.$newLineMode;
1069
    };
1070
    this.isNewLine = function(text) {
1071
        return (text == "\r\n" || text == "\r" || text == "\n");
1072
    };
1073
    this.getLine = function(row) {
1074
        return this.$lines[row] || "";
1075
    };
1076
    this.getLines = function(firstRow, lastRow) {
1077
        return this.$lines.slice(firstRow, lastRow + 1);
1078
    };
1079
    this.getAllLines = function() {
1080
        return this.getLines(0, this.getLength());
1081
    };
1082
    this.getLength = function() {
1083
        return this.$lines.length;
1084
    };
1085
    this.getTextRange = function(range) {
1086
        return this.getLinesForRange(range).join(this.getNewLineCharacter());
1087
    };
1088
    this.getLinesForRange = function(range) {
1089
        var lines;
1090
        if (range.start.row === range.end.row) {
1091
            lines = [this.getLine(range.start.row).substring(range.start.column, range.end.column)];
1092
        } else {
1093
            lines = this.getLines(range.start.row, range.end.row);
1094
            lines[0] = (lines[0] || "").substring(range.start.column);
1095
            var l = lines.length - 1;
1096
            if (range.end.row - range.start.row == l)
1097
                lines[l] = lines[l].substring(0, range.end.column);
1098
        }
1099
        return lines;
1100
    };
1101
    this.insertLines = function(row, lines) {
1102
        console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead.");
1103
        return this.insertFullLines(row, lines);
1104
    };
1105
    this.removeLines = function(firstRow, lastRow) {
1106
        console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead.");
1107
        return this.removeFullLines(firstRow, lastRow);
1108
    };
1109
    this.insertNewLine = function(position) {
1110
        console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead.");
1111
        return this.insertMergedLines(position, ["", ""]);
1112
    };
1113
    this.insert = function(position, text) {
1114
        if (this.getLength() <= 1)
1115
            this.$detectNewLine(text);
1116
        
1117
        return this.insertMergedLines(position, this.$split(text));
1118
    };
1119
    this.insertInLine = function(position, text) {
1120
        var start = this.clippedPos(position.row, position.column);
1121
        var end = this.pos(position.row, position.column + text.length);
1122
        
1123
        this.applyDelta({
1124
            start: start,
1125
            end: end,
1126
            action: "insert",
1127
            lines: [text]
1128
        }, true);
1129
        
1130
        return this.clonePos(end);
1131
    };
1132
    
1133
    this.clippedPos = function(row, column) {
1134
        var length = this.getLength();
1135
        if (row === undefined) {
1136
            row = length;
1137
        } else if (row < 0) {
1138
            row = 0;
1139
        } else if (row >= length) {
1140
            row = length - 1;
1141
            column = undefined;
1142
        }
1143
        var line = this.getLine(row);
1144
        if (column == undefined)
1145
            column = line.length;
1146
        column = Math.min(Math.max(column, 0), line.length);
1147
        return {row: row, column: column};
1148
    };
1149
    
1150
    this.clonePos = function(pos) {
1151
        return {row: pos.row, column: pos.column};
1152
    };
1153
    
1154
    this.pos = function(row, column) {
1155
        return {row: row, column: column};
1156
    };
1157
    
1158
    this.$clipPosition = function(position) {
1159
        var length = this.getLength();
1160
        if (position.row >= length) {
1161
            position.row = Math.max(0, length - 1);
1162
            position.column = this.getLine(length - 1).length;
1163
        } else {
1164
            position.row = Math.max(0, position.row);
1165
            position.column = Math.min(Math.max(position.column, 0), this.getLine(position.row).length);
1166
        }
1167
        return position;
1168
    };
1169
    this.insertFullLines = function(row, lines) {
1170
        row = Math.min(Math.max(row, 0), this.getLength());
1171
        var column = 0;
1172
        if (row < this.getLength()) {
1173
            lines = lines.concat([""]);
1174
            column = 0;
1175
        } else {
1176
            lines = [""].concat(lines);
1177
            row--;
1178
            column = this.$lines[row].length;
1179
        }
1180
        this.insertMergedLines({row: row, column: column}, lines);
1181
    };    
1182
    this.insertMergedLines = function(position, lines) {
1183
        var start = this.clippedPos(position.row, position.column);
1184
        var end = {
1185
            row: start.row + lines.length - 1,
1186
            column: (lines.length == 1 ? start.column : 0) + lines[lines.length - 1].length
1187
        };
1188
        
1189
        this.applyDelta({
1190
            start: start,
1191
            end: end,
1192
            action: "insert",
1193
            lines: lines
1194
        });
1195
        
1196
        return this.clonePos(end);
1197
    };
1198
    this.remove = function(range) {
1199
        var start = this.clippedPos(range.start.row, range.start.column);
1200
        var end = this.clippedPos(range.end.row, range.end.column);
1201
        this.applyDelta({
1202
            start: start,
1203
            end: end,
1204
            action: "remove",
1205
            lines: this.getLinesForRange({start: start, end: end})
1206
        });
1207
        return this.clonePos(start);
1208
    };
1209
    this.removeInLine = function(row, startColumn, endColumn) {
1210
        var start = this.clippedPos(row, startColumn);
1211
        var end = this.clippedPos(row, endColumn);
1212
        
1213
        this.applyDelta({
1214
            start: start,
1215
            end: end,
1216
            action: "remove",
1217
            lines: this.getLinesForRange({start: start, end: end})
1218
        }, true);
1219
        
1220
        return this.clonePos(start);
1221
    };
1222
    this.removeFullLines = function(firstRow, lastRow) {
1223
        firstRow = Math.min(Math.max(0, firstRow), this.getLength() - 1);
1224
        lastRow  = Math.min(Math.max(0, lastRow ), this.getLength() - 1);
1225
        var deleteFirstNewLine = lastRow == this.getLength() - 1 && firstRow > 0;
1226
        var deleteLastNewLine  = lastRow  < this.getLength() - 1;
1227
        var startRow = ( deleteFirstNewLine ? firstRow - 1                  : firstRow                    );
1228
        var startCol = ( deleteFirstNewLine ? this.getLine(startRow).length : 0                           );
1229
        var endRow   = ( deleteLastNewLine  ? lastRow + 1                   : lastRow                     );
1230
        var endCol   = ( deleteLastNewLine  ? 0                             : this.getLine(endRow).length ); 
1231
        var range = new Range(startRow, startCol, endRow, endCol);
1232
        var deletedLines = this.$lines.slice(firstRow, lastRow + 1);
1233
        
1234
        this.applyDelta({
1235
            start: range.start,
1236
            end: range.end,
1237
            action: "remove",
1238
            lines: this.getLinesForRange(range)
1239
        });
1240
        return deletedLines;
1241
    };
1242
    this.removeNewLine = function(row) {
1243
        if (row < this.getLength() - 1 && row >= 0) {
1244
            this.applyDelta({
1245
                start: this.pos(row, this.getLine(row).length),
1246
                end: this.pos(row + 1, 0),
1247
                action: "remove",
1248
                lines: ["", ""]
1249
            });
1250
        }
1251
    };
1252
    this.replace = function(range, text) {
1253
        if (!(range instanceof Range))
1254
            range = Range.fromPoints(range.start, range.end);
1255
        if (text.length === 0 && range.isEmpty())
1256
            return range.start;
1257
        if (text == this.getTextRange(range))
1258
            return range.end;
1259

    
1260
        this.remove(range);
1261
        var end;
1262
        if (text) {
1263
            end = this.insert(range.start, text);
1264
        }
1265
        else {
1266
            end = range.start;
1267
        }
1268
        
1269
        return end;
1270
    };
1271
    this.applyDeltas = function(deltas) {
1272
        for (var i=0; i<deltas.length; i++) {
1273
            this.applyDelta(deltas[i]);
1274
        }
1275
    };
1276
    this.revertDeltas = function(deltas) {
1277
        for (var i=deltas.length-1; i>=0; i--) {
1278
            this.revertDelta(deltas[i]);
1279
        }
1280
    };
1281
    this.applyDelta = function(delta, doNotValidate) {
1282
        var isInsert = delta.action == "insert";
1283
        if (isInsert ? delta.lines.length <= 1 && !delta.lines[0]
1284
            : !Range.comparePoints(delta.start, delta.end)) {
1285
            return;
1286
        }
1287
        
1288
        if (isInsert && delta.lines.length > 20000) {
1289
            this.$splitAndapplyLargeDelta(delta, 20000);
1290
        }
1291
        else {
1292
            applyDelta(this.$lines, delta, doNotValidate);
1293
            this._signal("change", delta);
1294
        }
1295
    };
1296
    
1297
    this.$safeApplyDelta = function(delta) {
1298
        var docLength = this.$lines.length;
1299
        if (
1300
            delta.action == "remove" && delta.start.row < docLength && delta.end.row < docLength
1301
            || delta.action == "insert" && delta.start.row <= docLength
1302
        ) {
1303
            this.applyDelta(delta);
1304
        }
1305
    };
1306
    
1307
    this.$splitAndapplyLargeDelta = function(delta, MAX) {
1308
        var lines = delta.lines;
1309
        var l = lines.length - MAX + 1;
1310
        var row = delta.start.row; 
1311
        var column = delta.start.column;
1312
        for (var from = 0, to = 0; from < l; from = to) {
1313
            to += MAX - 1;
1314
            var chunk = lines.slice(from, to);
1315
            chunk.push("");
1316
            this.applyDelta({
1317
                start: this.pos(row + from, column),
1318
                end: this.pos(row + to, column = 0),
1319
                action: delta.action,
1320
                lines: chunk
1321
            }, true);
1322
        }
1323
        delta.lines = lines.slice(from);
1324
        delta.start.row = row + from;
1325
        delta.start.column = column;
1326
        this.applyDelta(delta, true);
1327
    };
1328
    this.revertDelta = function(delta) {
1329
        this.$safeApplyDelta({
1330
            start: this.clonePos(delta.start),
1331
            end: this.clonePos(delta.end),
1332
            action: (delta.action == "insert" ? "remove" : "insert"),
1333
            lines: delta.lines.slice()
1334
        });
1335
    };
1336
    this.indexToPosition = function(index, startRow) {
1337
        var lines = this.$lines || this.getAllLines();
1338
        var newlineLength = this.getNewLineCharacter().length;
1339
        for (var i = startRow || 0, l = lines.length; i < l; i++) {
1340
            index -= lines[i].length + newlineLength;
1341
            if (index < 0)
1342
                return {row: i, column: index + lines[i].length + newlineLength};
1343
        }
1344
        return {row: l-1, column: index + lines[l-1].length + newlineLength};
1345
    };
1346
    this.positionToIndex = function(pos, startRow) {
1347
        var lines = this.$lines || this.getAllLines();
1348
        var newlineLength = this.getNewLineCharacter().length;
1349
        var index = 0;
1350
        var row = Math.min(pos.row, lines.length);
1351
        for (var i = startRow || 0; i < row; ++i)
1352
            index += lines[i].length + newlineLength;
1353

    
1354
        return index + pos.column;
1355
    };
1356

    
1357
}).call(Document.prototype);
1358

    
1359
exports.Document = Document;
1360
});
1361

    
1362
ace.define("ace/worker/mirror",[], function(require, exports, module) {
1363
"use strict";
1364

    
1365
var Range = require("../range").Range;
1366
var Document = require("../document").Document;
1367
var lang = require("../lib/lang");
1368
    
1369
var Mirror = exports.Mirror = function(sender) {
1370
    this.sender = sender;
1371
    var doc = this.doc = new Document("");
1372
    
1373
    var deferredUpdate = this.deferredUpdate = lang.delayedCall(this.onUpdate.bind(this));
1374
    
1375
    var _self = this;
1376
    sender.on("change", function(e) {
1377
        var data = e.data;
1378
        if (data[0].start) {
1379
            doc.applyDeltas(data);
1380
        } else {
1381
            for (var i = 0; i < data.length; i += 2) {
1382
                if (Array.isArray(data[i+1])) {
1383
                    var d = {action: "insert", start: data[i], lines: data[i+1]};
1384
                } else {
1385
                    var d = {action: "remove", start: data[i], end: data[i+1]};
1386
                }
1387
                doc.applyDelta(d, true);
1388
            }
1389
        }
1390
        if (_self.$timeout)
1391
            return deferredUpdate.schedule(_self.$timeout);
1392
        _self.onUpdate();
1393
    });
1394
};
1395

    
1396
(function() {
1397
    
1398
    this.$timeout = 500;
1399
    
1400
    this.setTimeout = function(timeout) {
1401
        this.$timeout = timeout;
1402
    };
1403
    
1404
    this.setValue = function(value) {
1405
        this.doc.setValue(value);
1406
        this.deferredUpdate.schedule(this.$timeout);
1407
    };
1408
    
1409
    this.getValue = function(callbackId) {
1410
        this.sender.callback(this.doc.getValue(), callbackId);
1411
    };
1412
    
1413
    this.onUpdate = function() {
1414
    };
1415
    
1416
    this.isPending = function() {
1417
        return this.deferredUpdate.isPending();
1418
    };
1419
    
1420
}).call(Mirror.prototype);
1421

    
1422
});
1423

    
1424
ace.define("ace/mode/css/csslint",[], function(require, exports, module) {
1425
var parserlib = {};
1426
(function(){
1427
function EventTarget(){
1428
    this._listeners = {};
1429
}
1430

    
1431
EventTarget.prototype = {
1432
    constructor: EventTarget,
1433
    addListener: function(type, listener){
1434
        if (!this._listeners[type]){
1435
            this._listeners[type] = [];
1436
        }
1437

    
1438
        this._listeners[type].push(listener);
1439
    },
1440
    fire: function(event){
1441
        if (typeof event == "string"){
1442
            event = { type: event };
1443
        }
1444
        if (typeof event.target != "undefined"){
1445
            event.target = this;
1446
        }
1447

    
1448
        if (typeof event.type == "undefined"){
1449
            throw new Error("Event object missing 'type' property.");
1450
        }
1451

    
1452
        if (this._listeners[event.type]){
1453
            var listeners = this._listeners[event.type].concat();
1454
            for (var i=0, len=listeners.length; i < len; i++){
1455
                listeners[i].call(this, event);
1456
            }
1457
        }
1458
    },
1459
    removeListener: function(type, listener){
1460
        if (this._listeners[type]){
1461
            var listeners = this._listeners[type];
1462
            for (var i=0, len=listeners.length; i < len; i++){
1463
                if (listeners[i] === listener){
1464
                    listeners.splice(i, 1);
1465
                    break;
1466
                }
1467
            }
1468

    
1469

    
1470
        }
1471
    }
1472
};
1473
function StringReader(text){
1474
    this._input = text.replace(/\n\r?/g, "\n");
1475
    this._line = 1;
1476
    this._col = 1;
1477
    this._cursor = 0;
1478
}
1479

    
1480
StringReader.prototype = {
1481
    constructor: StringReader,
1482
    getCol: function(){
1483
        return this._col;
1484
    },
1485
    getLine: function(){
1486
        return this._line ;
1487
    },
1488
    eof: function(){
1489
        return (this._cursor == this._input.length);
1490
    },
1491
    peek: function(count){
1492
        var c = null;
1493
        count = (typeof count == "undefined" ? 1 : count);
1494
        if (this._cursor < this._input.length){
1495
            c = this._input.charAt(this._cursor + count - 1);
1496
        }
1497

    
1498
        return c;
1499
    },
1500
    read: function(){
1501
        var c = null;
1502
        if (this._cursor < this._input.length){
1503
            if (this._input.charAt(this._cursor) == "\n"){
1504
                this._line++;
1505
                this._col=1;
1506
            } else {
1507
                this._col++;
1508
            }
1509
            c = this._input.charAt(this._cursor++);
1510
        }
1511

    
1512
        return c;
1513
    },
1514
    mark: function(){
1515
        this._bookmark = {
1516
            cursor: this._cursor,
1517
            line:   this._line,
1518
            col:    this._col
1519
        };
1520
    },
1521

    
1522
    reset: function(){
1523
        if (this._bookmark){
1524
            this._cursor = this._bookmark.cursor;
1525
            this._line = this._bookmark.line;
1526
            this._col = this._bookmark.col;
1527
            delete this._bookmark;
1528
        }
1529
    },
1530
    readTo: function(pattern){
1531

    
1532
        var buffer = "",
1533
            c;
1534
        while (buffer.length < pattern.length || buffer.lastIndexOf(pattern) != buffer.length - pattern.length){
1535
            c = this.read();
1536
            if (c){
1537
                buffer += c;
1538
            } else {
1539
                throw new Error("Expected \"" + pattern + "\" at line " + this._line  + ", col " + this._col + ".");
1540
            }
1541
        }
1542

    
1543
        return buffer;
1544

    
1545
    },
1546
    readWhile: function(filter){
1547

    
1548
        var buffer = "",
1549
            c = this.read();
1550

    
1551
        while(c !== null && filter(c)){
1552
            buffer += c;
1553
            c = this.read();
1554
        }
1555

    
1556
        return buffer;
1557

    
1558
    },
1559
    readMatch: function(matcher){
1560

    
1561
        var source = this._input.substring(this._cursor),
1562
            value = null;
1563
        if (typeof matcher == "string"){
1564
            if (source.indexOf(matcher) === 0){
1565
                value = this.readCount(matcher.length);
1566
            }
1567
        } else if (matcher instanceof RegExp){
1568
            if (matcher.test(source)){
1569
                value = this.readCount(RegExp.lastMatch.length);
1570
            }
1571
        }
1572

    
1573
        return value;
1574
    },
1575
    readCount: function(count){
1576
        var buffer = "";
1577

    
1578
        while(count--){
1579
            buffer += this.read();
1580
        }
1581

    
1582
        return buffer;
1583
    }
1584

    
1585
};
1586
function SyntaxError(message, line, col){
1587
    this.col = col;
1588
    this.line = line;
1589
    this.message = message;
1590

    
1591
}
1592
SyntaxError.prototype = new Error();
1593
function SyntaxUnit(text, line, col, type){
1594
    this.col = col;
1595
    this.line = line;
1596
    this.text = text;
1597
    this.type = type;
1598
}
1599
SyntaxUnit.fromToken = function(token){
1600
    return new SyntaxUnit(token.value, token.startLine, token.startCol);
1601
};
1602

    
1603
SyntaxUnit.prototype = {
1604
    constructor: SyntaxUnit,
1605
    valueOf: function(){
1606
        return this.text;
1607
    },
1608
    toString: function(){
1609
        return this.text;
1610
    }
1611

    
1612
};
1613
function TokenStreamBase(input, tokenData){
1614
    this._reader = input ? new StringReader(input.toString()) : null;
1615
    this._token = null;
1616
    this._tokenData = tokenData;
1617
    this._lt = [];
1618
    this._ltIndex = 0;
1619

    
1620
    this._ltIndexCache = [];
1621
}
1622
TokenStreamBase.createTokenData = function(tokens){
1623

    
1624
    var nameMap     = [],
1625
        typeMap     = {},
1626
        tokenData     = tokens.concat([]),
1627
        i            = 0,
1628
        len            = tokenData.length+1;
1629

    
1630
    tokenData.UNKNOWN = -1;
1631
    tokenData.unshift({name:"EOF"});
1632

    
1633
    for (; i < len; i++){
1634
        nameMap.push(tokenData[i].name);
1635
        tokenData[tokenData[i].name] = i;
1636
        if (tokenData[i].text){
1637
            typeMap[tokenData[i].text] = i;
1638
        }
1639
    }
1640

    
1641
    tokenData.name = function(tt){
1642
        return nameMap[tt];
1643
    };
1644

    
1645
    tokenData.type = function(c){
1646
        return typeMap[c];
1647
    };
1648

    
1649
    return tokenData;
1650
};
1651

    
1652
TokenStreamBase.prototype = {
1653
    constructor: TokenStreamBase,
1654
    match: function(tokenTypes, channel){
1655
        if (!(tokenTypes instanceof Array)){
1656
            tokenTypes = [tokenTypes];
1657
        }
1658

    
1659
        var tt  = this.get(channel),
1660
            i   = 0,
1661
            len = tokenTypes.length;
1662

    
1663
        while(i < len){
1664
            if (tt == tokenTypes[i++]){
1665
                return true;
1666
            }
1667
        }
1668
        this.unget();
1669
        return false;
1670
    },
1671
    mustMatch: function(tokenTypes, channel){
1672

    
1673
        var token;
1674
        if (!(tokenTypes instanceof Array)){
1675
            tokenTypes = [tokenTypes];
1676
        }
1677

    
1678
        if (!this.match.apply(this, arguments)){
1679
            token = this.LT(1);
1680
            throw new SyntaxError("Expected " + this._tokenData[tokenTypes[0]].name +
1681
                " at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
1682
        }
1683
    },
1684
    advance: function(tokenTypes, channel){
1685

    
1686
        while(this.LA(0) !== 0 && !this.match(tokenTypes, channel)){
1687
            this.get();
1688
        }
1689

    
1690
        return this.LA(0);
1691
    },
1692
    get: function(channel){
1693

    
1694
        var tokenInfo   = this._tokenData,
1695
            reader      = this._reader,
1696
            value,
1697
            i           =0,
1698
            len         = tokenInfo.length,
1699
            found       = false,
1700
            token,
1701
            info;
1702
        if (this._lt.length && this._ltIndex >= 0 && this._ltIndex < this._lt.length){
1703

    
1704
            i++;
1705
            this._token = this._lt[this._ltIndex++];
1706
            info = tokenInfo[this._token.type];
1707
            while((info.channel !== undefined && channel !== info.channel) &&
1708
                    this._ltIndex < this._lt.length){
1709
                this._token = this._lt[this._ltIndex++];
1710
                info = tokenInfo[this._token.type];
1711
                i++;
1712
            }
1713
            if ((info.channel === undefined || channel === info.channel) &&
1714
                    this._ltIndex <= this._lt.length){
1715
                this._ltIndexCache.push(i);
1716
                return this._token.type;
1717
            }
1718
        }
1719
        token = this._getToken();
1720
        if (token.type > -1 && !tokenInfo[token.type].hide){
1721
            token.channel = tokenInfo[token.type].channel;
1722
            this._token = token;
1723
            this._lt.push(token);
1724
            this._ltIndexCache.push(this._lt.length - this._ltIndex + i);
1725
            if (this._lt.length > 5){
1726
                this._lt.shift();
1727
            }
1728
            if (this._ltIndexCache.length > 5){
1729
                this._ltIndexCache.shift();
1730
            }
1731
            this._ltIndex = this._lt.length;
1732
        }
1733
        info = tokenInfo[token.type];
1734
        if (info &&
1735
                (info.hide ||
1736
                (info.channel !== undefined && channel !== info.channel))){
1737
            return this.get(channel);
1738
        } else {
1739
            return token.type;
1740
        }
1741
    },
1742
    LA: function(index){
1743
        var total = index,
1744
            tt;
1745
        if (index > 0){
1746
            if (index > 5){
1747
                throw new Error("Too much lookahead.");
1748
            }
1749
            while(total){
1750
                tt = this.get();
1751
                total--;
1752
            }
1753
            while(total < index){
1754
                this.unget();
1755
                total++;
1756
            }
1757
        } else if (index < 0){
1758

    
1759
            if(this._lt[this._ltIndex+index]){
1760
                tt = this._lt[this._ltIndex+index].type;
1761
            } else {
1762
                throw new Error("Too much lookbehind.");
1763
            }
1764

    
1765
        } else {
1766
            tt = this._token.type;
1767
        }
1768

    
1769
        return tt;
1770

    
1771
    },
1772
    LT: function(index){
1773
        this.LA(index);
1774
        return this._lt[this._ltIndex+index-1];
1775
    },
1776
    peek: function(){
1777
        return this.LA(1);
1778
    },
1779
    token: function(){
1780
        return this._token;
1781
    },
1782
    tokenName: function(tokenType){
1783
        if (tokenType < 0 || tokenType > this._tokenData.length){
1784
            return "UNKNOWN_TOKEN";
1785
        } else {
1786
            return this._tokenData[tokenType].name;
1787
        }
1788
    },
1789
    tokenType: function(tokenName){
1790
        return this._tokenData[tokenName] || -1;
1791
    },
1792
    unget: function(){
1793
        if (this._ltIndexCache.length){
1794
            this._ltIndex -= this._ltIndexCache.pop();//--;
1795
            this._token = this._lt[this._ltIndex - 1];
1796
        } else {
1797
            throw new Error("Too much lookahead.");
1798
        }
1799
    }
1800

    
1801
};
1802

    
1803

    
1804
parserlib.util = {
1805
StringReader: StringReader,
1806
SyntaxError : SyntaxError,
1807
SyntaxUnit  : SyntaxUnit,
1808
EventTarget : EventTarget,
1809
TokenStreamBase : TokenStreamBase
1810
};
1811
})();
1812
(function(){
1813
var EventTarget = parserlib.util.EventTarget,
1814
TokenStreamBase = parserlib.util.TokenStreamBase,
1815
StringReader = parserlib.util.StringReader,
1816
SyntaxError = parserlib.util.SyntaxError,
1817
SyntaxUnit  = parserlib.util.SyntaxUnit;
1818

    
1819
var Colors = {
1820
    aliceblue       :"#f0f8ff",
1821
    antiquewhite    :"#faebd7",
1822
    aqua            :"#00ffff",
1823
    aquamarine      :"#7fffd4",
1824
    azure           :"#f0ffff",
1825
    beige           :"#f5f5dc",
1826
    bisque          :"#ffe4c4",
1827
    black           :"#000000",
1828
    blanchedalmond  :"#ffebcd",
1829
    blue            :"#0000ff",
1830
    blueviolet      :"#8a2be2",
1831
    brown           :"#a52a2a",
1832
    burlywood       :"#deb887",
1833
    cadetblue       :"#5f9ea0",
1834
    chartreuse      :"#7fff00",
1835
    chocolate       :"#d2691e",
1836
    coral           :"#ff7f50",
1837
    cornflowerblue  :"#6495ed",
1838
    cornsilk        :"#fff8dc",
1839
    crimson         :"#dc143c",
1840
    cyan            :"#00ffff",
1841
    darkblue        :"#00008b",
1842
    darkcyan        :"#008b8b",
1843
    darkgoldenrod   :"#b8860b",
1844
    darkgray        :"#a9a9a9",
1845
    darkgrey        :"#a9a9a9",
1846
    darkgreen       :"#006400",
1847
    darkkhaki       :"#bdb76b",
1848
    darkmagenta     :"#8b008b",
1849
    darkolivegreen  :"#556b2f",
1850
    darkorange      :"#ff8c00",
1851
    darkorchid      :"#9932cc",
1852
    darkred         :"#8b0000",
1853
    darksalmon      :"#e9967a",
1854
    darkseagreen    :"#8fbc8f",
1855
    darkslateblue   :"#483d8b",
1856
    darkslategray   :"#2f4f4f",
1857
    darkslategrey   :"#2f4f4f",
1858
    darkturquoise   :"#00ced1",
1859
    darkviolet      :"#9400d3",
1860
    deeppink        :"#ff1493",
1861
    deepskyblue     :"#00bfff",
1862
    dimgray         :"#696969",
1863
    dimgrey         :"#696969",
1864
    dodgerblue      :"#1e90ff",
1865
    firebrick       :"#b22222",
1866
    floralwhite     :"#fffaf0",
1867
    forestgreen     :"#228b22",
1868
    fuchsia         :"#ff00ff",
1869
    gainsboro       :"#dcdcdc",
1870
    ghostwhite      :"#f8f8ff",
1871
    gold            :"#ffd700",
1872
    goldenrod       :"#daa520",
1873
    gray            :"#808080",
1874
    grey            :"#808080",
1875
    green           :"#008000",
1876
    greenyellow     :"#adff2f",
1877
    honeydew        :"#f0fff0",
1878
    hotpink         :"#ff69b4",
1879
    indianred       :"#cd5c5c",
1880
    indigo          :"#4b0082",
1881
    ivory           :"#fffff0",
1882
    khaki           :"#f0e68c",
1883
    lavender        :"#e6e6fa",
1884
    lavenderblush   :"#fff0f5",
1885
    lawngreen       :"#7cfc00",
1886
    lemonchiffon    :"#fffacd",
1887
    lightblue       :"#add8e6",
1888
    lightcoral      :"#f08080",
1889
    lightcyan       :"#e0ffff",
1890
    lightgoldenrodyellow  :"#fafad2",
1891
    lightgray       :"#d3d3d3",
1892
    lightgrey       :"#d3d3d3",
1893
    lightgreen      :"#90ee90",
1894
    lightpink       :"#ffb6c1",
1895
    lightsalmon     :"#ffa07a",
1896
    lightseagreen   :"#20b2aa",
1897
    lightskyblue    :"#87cefa",
1898
    lightslategray  :"#778899",
1899
    lightslategrey  :"#778899",
1900
    lightsteelblue  :"#b0c4de",
1901
    lightyellow     :"#ffffe0",
1902
    lime            :"#00ff00",
1903
    limegreen       :"#32cd32",
1904
    linen           :"#faf0e6",
1905
    magenta         :"#ff00ff",
1906
    maroon          :"#800000",
1907
    mediumaquamarine:"#66cdaa",
1908
    mediumblue      :"#0000cd",
1909
    mediumorchid    :"#ba55d3",
1910
    mediumpurple    :"#9370d8",
1911
    mediumseagreen  :"#3cb371",
1912
    mediumslateblue :"#7b68ee",
1913
    mediumspringgreen   :"#00fa9a",
1914
    mediumturquoise :"#48d1cc",
1915
    mediumvioletred :"#c71585",
1916
    midnightblue    :"#191970",
1917
    mintcream       :"#f5fffa",
1918
    mistyrose       :"#ffe4e1",
1919
    moccasin        :"#ffe4b5",
1920
    navajowhite     :"#ffdead",
1921
    navy            :"#000080",
1922
    oldlace         :"#fdf5e6",
1923
    olive           :"#808000",
1924
    olivedrab       :"#6b8e23",
1925
    orange          :"#ffa500",
1926
    orangered       :"#ff4500",
1927
    orchid          :"#da70d6",
1928
    palegoldenrod   :"#eee8aa",
1929
    palegreen       :"#98fb98",
1930
    paleturquoise   :"#afeeee",
1931
    palevioletred   :"#d87093",
1932
    papayawhip      :"#ffefd5",
1933
    peachpuff       :"#ffdab9",
1934
    peru            :"#cd853f",
1935
    pink            :"#ffc0cb",
1936
    plum            :"#dda0dd",
1937
    powderblue      :"#b0e0e6",
1938
    purple          :"#800080",
1939
    red             :"#ff0000",
1940
    rosybrown       :"#bc8f8f",
1941
    royalblue       :"#4169e1",
1942
    saddlebrown     :"#8b4513",
1943
    salmon          :"#fa8072",
1944
    sandybrown      :"#f4a460",
1945
    seagreen        :"#2e8b57",
1946
    seashell        :"#fff5ee",
1947
    sienna          :"#a0522d",
1948
    silver          :"#c0c0c0",
1949
    skyblue         :"#87ceeb",
1950
    slateblue       :"#6a5acd",
1951
    slategray       :"#708090",
1952
    slategrey       :"#708090",
1953
    snow            :"#fffafa",
1954
    springgreen     :"#00ff7f",
1955
    steelblue       :"#4682b4",
1956
    tan             :"#d2b48c",
1957
    teal            :"#008080",
1958
    thistle         :"#d8bfd8",
1959
    tomato          :"#ff6347",
1960
    turquoise       :"#40e0d0",
1961
    violet          :"#ee82ee",
1962
    wheat           :"#f5deb3",
1963
    white           :"#ffffff",
1964
    whitesmoke      :"#f5f5f5",
1965
    yellow          :"#ffff00",
1966
    yellowgreen     :"#9acd32",
1967
    activeBorder        :"Active window border.",
1968
    activecaption       :"Active window caption.",
1969
    appworkspace        :"Background color of multiple document interface.",
1970
    background          :"Desktop background.",
1971
    buttonface          :"The face background color for 3-D elements that appear 3-D due to one layer of surrounding border.",
1972
    buttonhighlight     :"The color of the border facing the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",
1973
    buttonshadow        :"The color of the border away from the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",
1974
    buttontext          :"Text on push buttons.",
1975
    captiontext         :"Text in caption, size box, and scrollbar arrow box.",
1976
    graytext            :"Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.",
1977
    greytext            :"Greyed (disabled) text. This color is set to #000 if the current display driver does not support a solid grey color.",
1978
    highlight           :"Item(s) selected in a control.",
1979
    highlighttext       :"Text of item(s) selected in a control.",
1980
    inactiveborder      :"Inactive window border.",
1981
    inactivecaption     :"Inactive window caption.",
1982
    inactivecaptiontext :"Color of text in an inactive caption.",
1983
    infobackground      :"Background color for tooltip controls.",
1984
    infotext            :"Text color for tooltip controls.",
1985
    menu                :"Menu background.",
1986
    menutext            :"Text in menus.",
1987
    scrollbar           :"Scroll bar gray area.",
1988
    threeddarkshadow    :"The color of the darker (generally outer) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
1989
    threedface          :"The face background color for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
1990
    threedhighlight     :"The color of the lighter (generally outer) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
1991
    threedlightshadow   :"The color of the darker (generally inner) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
1992
    threedshadow        :"The color of the lighter (generally inner) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
1993
    window              :"Window background.",
1994
    windowframe         :"Window frame.",
1995
    windowtext          :"Text in windows."
1996
};
1997
function Combinator(text, line, col){
1998

    
1999
    SyntaxUnit.call(this, text, line, col, Parser.COMBINATOR_TYPE);
2000
    this.type = "unknown";
2001
    if (/^\s+$/.test(text)){
2002
        this.type = "descendant";
2003
    } else if (text == ">"){
2004
        this.type = "child";
2005
    } else if (text == "+"){
2006
        this.type = "adjacent-sibling";
2007
    } else if (text == "~"){
2008
        this.type = "sibling";
2009
    }
2010

    
2011
}
2012

    
2013
Combinator.prototype = new SyntaxUnit();
2014
Combinator.prototype.constructor = Combinator;
2015
function MediaFeature(name, value){
2016

    
2017
    SyntaxUnit.call(this, "(" + name + (value !== null ? ":" + value : "") + ")", name.startLine, name.startCol, Parser.MEDIA_FEATURE_TYPE);
2018
    this.name = name;
2019
    this.value = value;
2020
}
2021

    
2022
MediaFeature.prototype = new SyntaxUnit();
2023
MediaFeature.prototype.constructor = MediaFeature;
2024
function MediaQuery(modifier, mediaType, features, line, col){
2025

    
2026
    SyntaxUnit.call(this, (modifier ? modifier + " ": "") + (mediaType ? mediaType : "") + (mediaType && features.length > 0 ? " and " : "") + features.join(" and "), line, col, Parser.MEDIA_QUERY_TYPE);
2027
    this.modifier = modifier;
2028
    this.mediaType = mediaType;
2029
    this.features = features;
2030

    
2031
}
2032

    
2033
MediaQuery.prototype = new SyntaxUnit();
2034
MediaQuery.prototype.constructor = MediaQuery;
2035
function Parser(options){
2036
    EventTarget.call(this);
2037

    
2038

    
2039
    this.options = options || {};
2040

    
2041
    this._tokenStream = null;
2042
}
2043
Parser.DEFAULT_TYPE = 0;
2044
Parser.COMBINATOR_TYPE = 1;
2045
Parser.MEDIA_FEATURE_TYPE = 2;
2046
Parser.MEDIA_QUERY_TYPE = 3;
2047
Parser.PROPERTY_NAME_TYPE = 4;
2048
Parser.PROPERTY_VALUE_TYPE = 5;
2049
Parser.PROPERTY_VALUE_PART_TYPE = 6;
2050
Parser.SELECTOR_TYPE = 7;
2051
Parser.SELECTOR_PART_TYPE = 8;
2052
Parser.SELECTOR_SUB_PART_TYPE = 9;
2053

    
2054
Parser.prototype = function(){
2055

    
2056
    var proto = new EventTarget(),  //new prototype
2057
        prop,
2058
        additions =  {
2059
            constructor: Parser,
2060
            DEFAULT_TYPE : 0,
2061
            COMBINATOR_TYPE : 1,
2062
            MEDIA_FEATURE_TYPE : 2,
2063
            MEDIA_QUERY_TYPE : 3,
2064
            PROPERTY_NAME_TYPE : 4,
2065
            PROPERTY_VALUE_TYPE : 5,
2066
            PROPERTY_VALUE_PART_TYPE : 6,
2067
            SELECTOR_TYPE : 7,
2068
            SELECTOR_PART_TYPE : 8,
2069
            SELECTOR_SUB_PART_TYPE : 9,
2070

    
2071
            _stylesheet: function(){
2072

    
2073
                var tokenStream = this._tokenStream,
2074
                    charset     = null,
2075
                    count,
2076
                    token,
2077
                    tt;
2078

    
2079
                this.fire("startstylesheet");
2080
                this._charset();
2081

    
2082
                this._skipCruft();
2083
                while (tokenStream.peek() == Tokens.IMPORT_SYM){
2084
                    this._import();
2085
                    this._skipCruft();
2086
                }
2087
                while (tokenStream.peek() == Tokens.NAMESPACE_SYM){
2088
                    this._namespace();
2089
                    this._skipCruft();
2090
                }
2091
                tt = tokenStream.peek();
2092
                while(tt > Tokens.EOF){
2093

    
2094
                    try {
2095

    
2096
                        switch(tt){
2097
                            case Tokens.MEDIA_SYM:
2098
                                this._media();
2099
                                this._skipCruft();
2100
                                break;
2101
                            case Tokens.PAGE_SYM:
2102
                                this._page();
2103
                                this._skipCruft();
2104
                                break;
2105
                            case Tokens.FONT_FACE_SYM:
2106
                                this._font_face();
2107
                                this._skipCruft();
2108
                                break;
2109
                            case Tokens.KEYFRAMES_SYM:
2110
                                this._keyframes();
2111
                                this._skipCruft();
2112
                                break;
2113
                            case Tokens.VIEWPORT_SYM:
2114
                                this._viewport();
2115
                                this._skipCruft();
2116
                                break;
2117
                            case Tokens.UNKNOWN_SYM:  //unknown @ rule
2118
                                tokenStream.get();
2119
                                if (!this.options.strict){
2120
                                    this.fire({
2121
                                        type:       "error",
2122
                                        error:      null,
2123
                                        message:    "Unknown @ rule: " + tokenStream.LT(0).value + ".",
2124
                                        line:       tokenStream.LT(0).startLine,
2125
                                        col:        tokenStream.LT(0).startCol
2126
                                    });
2127
                                    count=0;
2128
                                    while (tokenStream.advance([Tokens.LBRACE, Tokens.RBRACE]) == Tokens.LBRACE){
2129
                                        count++;    //keep track of nesting depth
2130
                                    }
2131

    
2132
                                    while(count){
2133
                                        tokenStream.advance([Tokens.RBRACE]);
2134
                                        count--;
2135
                                    }
2136

    
2137
                                } else {
2138
                                    throw new SyntaxError("Unknown @ rule.", tokenStream.LT(0).startLine, tokenStream.LT(0).startCol);
2139
                                }
2140
                                break;
2141
                            case Tokens.S:
2142
                                this._readWhitespace();
2143
                                break;
2144
                            default:
2145
                                if(!this._ruleset()){
2146
                                    switch(tt){
2147
                                        case Tokens.CHARSET_SYM:
2148
                                            token = tokenStream.LT(1);
2149
                                            this._charset(false);
2150
                                            throw new SyntaxError("@charset not allowed here.", token.startLine, token.startCol);
2151
                                        case Tokens.IMPORT_SYM:
2152
                                            token = tokenStream.LT(1);
2153
                                            this._import(false);
2154
                                            throw new SyntaxError("@import not allowed here.", token.startLine, token.startCol);
2155
                                        case Tokens.NAMESPACE_SYM:
2156
                                            token = tokenStream.LT(1);
2157
                                            this._namespace(false);
2158
                                            throw new SyntaxError("@namespace not allowed here.", token.startLine, token.startCol);
2159
                                        default:
2160
                                            tokenStream.get();  //get the last token
2161
                                            this._unexpectedToken(tokenStream.token());
2162
                                    }
2163

    
2164
                                }
2165
                        }
2166
                    } catch(ex) {
2167
                        if (ex instanceof SyntaxError && !this.options.strict){
2168
                            this.fire({
2169
                                type:       "error",
2170
                                error:      ex,
2171
                                message:    ex.message,
2172
                                line:       ex.line,
2173
                                col:        ex.col
2174
                            });
2175
                        } else {
2176
                            throw ex;
2177
                        }
2178
                    }
2179

    
2180
                    tt = tokenStream.peek();
2181
                }
2182

    
2183
                if (tt != Tokens.EOF){
2184
                    this._unexpectedToken(tokenStream.token());
2185
                }
2186

    
2187
                this.fire("endstylesheet");
2188
            },
2189

    
2190
            _charset: function(emit){
2191
                var tokenStream = this._tokenStream,
2192
                    charset,
2193
                    token,
2194
                    line,
2195
                    col;
2196

    
2197
                if (tokenStream.match(Tokens.CHARSET_SYM)){
2198
                    line = tokenStream.token().startLine;
2199
                    col = tokenStream.token().startCol;
2200

    
2201
                    this._readWhitespace();
2202
                    tokenStream.mustMatch(Tokens.STRING);
2203

    
2204
                    token = tokenStream.token();
2205
                    charset = token.value;
2206

    
2207
                    this._readWhitespace();
2208
                    tokenStream.mustMatch(Tokens.SEMICOLON);
2209

    
2210
                    if (emit !== false){
2211
                        this.fire({
2212
                            type:   "charset",
2213
                            charset:charset,
2214
                            line:   line,
2215
                            col:    col
2216
                        });
2217
                    }
2218
                }
2219
            },
2220

    
2221
            _import: function(emit){
2222

    
2223
                var tokenStream = this._tokenStream,
2224
                    tt,
2225
                    uri,
2226
                    importToken,
2227
                    mediaList   = [];
2228
                tokenStream.mustMatch(Tokens.IMPORT_SYM);
2229
                importToken = tokenStream.token();
2230
                this._readWhitespace();
2231

    
2232
                tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
2233
                uri = tokenStream.token().value.replace(/^(?:url\()?["']?([^"']+?)["']?\)?$/, "$1");
2234

    
2235
                this._readWhitespace();
2236

    
2237
                mediaList = this._media_query_list();
2238
                tokenStream.mustMatch(Tokens.SEMICOLON);
2239
                this._readWhitespace();
2240

    
2241
                if (emit !== false){
2242
                    this.fire({
2243
                        type:   "import",
2244
                        uri:    uri,
2245
                        media:  mediaList,
2246
                        line:   importToken.startLine,
2247
                        col:    importToken.startCol
2248
                    });
2249
                }
2250

    
2251
            },
2252

    
2253
            _namespace: function(emit){
2254

    
2255
                var tokenStream = this._tokenStream,
2256
                    line,
2257
                    col,
2258
                    prefix,
2259
                    uri;
2260
                tokenStream.mustMatch(Tokens.NAMESPACE_SYM);
2261
                line = tokenStream.token().startLine;
2262
                col = tokenStream.token().startCol;
2263
                this._readWhitespace();
2264
                if (tokenStream.match(Tokens.IDENT)){
2265
                    prefix = tokenStream.token().value;
2266
                    this._readWhitespace();
2267
                }
2268

    
2269
                tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
2270
                uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1");
2271

    
2272
                this._readWhitespace();
2273
                tokenStream.mustMatch(Tokens.SEMICOLON);
2274
                this._readWhitespace();
2275

    
2276
                if (emit !== false){
2277
                    this.fire({
2278
                        type:   "namespace",
2279
                        prefix: prefix,
2280
                        uri:    uri,
2281
                        line:   line,
2282
                        col:    col
2283
                    });
2284
                }
2285

    
2286
            },
2287

    
2288
            _media: function(){
2289
                var tokenStream     = this._tokenStream,
2290
                    line,
2291
                    col,
2292
                    mediaList;//       = [];
2293
                tokenStream.mustMatch(Tokens.MEDIA_SYM);
2294
                line = tokenStream.token().startLine;
2295
                col = tokenStream.token().startCol;
2296

    
2297
                this._readWhitespace();
2298

    
2299
                mediaList = this._media_query_list();
2300

    
2301
                tokenStream.mustMatch(Tokens.LBRACE);
2302
                this._readWhitespace();
2303

    
2304
                this.fire({
2305
                    type:   "startmedia",
2306
                    media:  mediaList,
2307
                    line:   line,
2308
                    col:    col
2309
                });
2310

    
2311
                while(true) {
2312
                    if (tokenStream.peek() == Tokens.PAGE_SYM){
2313
                        this._page();
2314
                    } else if (tokenStream.peek() == Tokens.FONT_FACE_SYM){
2315
                        this._font_face();
2316
                    } else if (tokenStream.peek() == Tokens.VIEWPORT_SYM){
2317
                        this._viewport();
2318
                    } else if (!this._ruleset()){
2319
                        break;
2320
                    }
2321
                }
2322

    
2323
                tokenStream.mustMatch(Tokens.RBRACE);
2324
                this._readWhitespace();
2325

    
2326
                this.fire({
2327
                    type:   "endmedia",
2328
                    media:  mediaList,
2329
                    line:   line,
2330
                    col:    col
2331
                });
2332
            },
2333
            _media_query_list: function(){
2334
                var tokenStream = this._tokenStream,
2335
                    mediaList   = [];
2336

    
2337

    
2338
                this._readWhitespace();
2339

    
2340
                if (tokenStream.peek() == Tokens.IDENT || tokenStream.peek() == Tokens.LPAREN){
2341
                    mediaList.push(this._media_query());
2342
                }
2343

    
2344
                while(tokenStream.match(Tokens.COMMA)){
2345
                    this._readWhitespace();
2346
                    mediaList.push(this._media_query());
2347
                }
2348

    
2349
                return mediaList;
2350
            },
2351
            _media_query: function(){
2352
                var tokenStream = this._tokenStream,
2353
                    type        = null,
2354
                    ident       = null,
2355
                    token       = null,
2356
                    expressions = [];
2357

    
2358
                if (tokenStream.match(Tokens.IDENT)){
2359
                    ident = tokenStream.token().value.toLowerCase();
2360
                    if (ident != "only" && ident != "not"){
2361
                        tokenStream.unget();
2362
                        ident = null;
2363
                    } else {
2364
                        token = tokenStream.token();
2365
                    }
2366
                }
2367

    
2368
                this._readWhitespace();
2369

    
2370
                if (tokenStream.peek() == Tokens.IDENT){
2371
                    type = this._media_type();
2372
                    if (token === null){
2373
                        token = tokenStream.token();
2374
                    }
2375
                } else if (tokenStream.peek() == Tokens.LPAREN){
2376
                    if (token === null){
2377
                        token = tokenStream.LT(1);
2378
                    }
2379
                    expressions.push(this._media_expression());
2380
                }
2381

    
2382
                if (type === null && expressions.length === 0){
2383
                    return null;
2384
                } else {
2385
                    this._readWhitespace();
2386
                    while (tokenStream.match(Tokens.IDENT)){
2387
                        if (tokenStream.token().value.toLowerCase() != "and"){
2388
                            this._unexpectedToken(tokenStream.token());
2389
                        }
2390

    
2391
                        this._readWhitespace();
2392
                        expressions.push(this._media_expression());
2393
                    }
2394
                }
2395

    
2396
                return new MediaQuery(ident, type, expressions, token.startLine, token.startCol);
2397
            },
2398
            _media_type: function(){
2399
                return this._media_feature();
2400
            },
2401
            _media_expression: function(){
2402
                var tokenStream = this._tokenStream,
2403
                    feature     = null,
2404
                    token,
2405
                    expression  = null;
2406

    
2407
                tokenStream.mustMatch(Tokens.LPAREN);
2408
                this._readWhitespace();
2409

    
2410
                feature = this._media_feature();
2411
                this._readWhitespace();
2412

    
2413
                if (tokenStream.match(Tokens.COLON)){
2414
                    this._readWhitespace();
2415
                    token = tokenStream.LT(1);
2416
                    expression = this._expression();
2417
                }
2418

    
2419
                tokenStream.mustMatch(Tokens.RPAREN);
2420
                this._readWhitespace();
2421

    
2422
                return new MediaFeature(feature, (expression ? new SyntaxUnit(expression, token.startLine, token.startCol) : null));
2423
            },
2424
            _media_feature: function(){
2425
                var tokenStream = this._tokenStream;
2426

    
2427
                tokenStream.mustMatch(Tokens.IDENT);
2428

    
2429
                return SyntaxUnit.fromToken(tokenStream.token());
2430
            },
2431
            _page: function(){
2432
                var tokenStream = this._tokenStream,
2433
                    line,
2434
                    col,
2435
                    identifier  = null,
2436
                    pseudoPage  = null;
2437
                tokenStream.mustMatch(Tokens.PAGE_SYM);
2438
                line = tokenStream.token().startLine;
2439
                col = tokenStream.token().startCol;
2440

    
2441
                this._readWhitespace();
2442

    
2443
                if (tokenStream.match(Tokens.IDENT)){
2444
                    identifier = tokenStream.token().value;
2445
                    if (identifier.toLowerCase() === "auto"){
2446
                        this._unexpectedToken(tokenStream.token());
2447
                    }
2448
                }
2449
                if (tokenStream.peek() == Tokens.COLON){
2450
                    pseudoPage = this._pseudo_page();
2451
                }
2452

    
2453
                this._readWhitespace();
2454

    
2455
                this.fire({
2456
                    type:   "startpage",
2457
                    id:     identifier,
2458
                    pseudo: pseudoPage,
2459
                    line:   line,
2460
                    col:    col
2461
                });
2462

    
2463
                this._readDeclarations(true, true);
2464

    
2465
                this.fire({
2466
                    type:   "endpage",
2467
                    id:     identifier,
2468
                    pseudo: pseudoPage,
2469
                    line:   line,
2470
                    col:    col
2471
                });
2472

    
2473
            },
2474
            _margin: function(){
2475
                var tokenStream = this._tokenStream,
2476
                    line,
2477
                    col,
2478
                    marginSym   = this._margin_sym();
2479

    
2480
                if (marginSym){
2481
                    line = tokenStream.token().startLine;
2482
                    col = tokenStream.token().startCol;
2483

    
2484
                    this.fire({
2485
                        type: "startpagemargin",
2486
                        margin: marginSym,
2487
                        line:   line,
2488
                        col:    col
2489
                    });
2490

    
2491
                    this._readDeclarations(true);
2492

    
2493
                    this.fire({
2494
                        type: "endpagemargin",
2495
                        margin: marginSym,
2496
                        line:   line,
2497
                        col:    col
2498
                    });
2499
                    return true;
2500
                } else {
2501
                    return false;
2502
                }
2503
            },
2504
            _margin_sym: function(){
2505

    
2506
                var tokenStream = this._tokenStream;
2507

    
2508
                if(tokenStream.match([Tokens.TOPLEFTCORNER_SYM, Tokens.TOPLEFT_SYM,
2509
                        Tokens.TOPCENTER_SYM, Tokens.TOPRIGHT_SYM, Tokens.TOPRIGHTCORNER_SYM,
2510
                        Tokens.BOTTOMLEFTCORNER_SYM, Tokens.BOTTOMLEFT_SYM,
2511
                        Tokens.BOTTOMCENTER_SYM, Tokens.BOTTOMRIGHT_SYM,
2512
                        Tokens.BOTTOMRIGHTCORNER_SYM, Tokens.LEFTTOP_SYM,
2513
                        Tokens.LEFTMIDDLE_SYM, Tokens.LEFTBOTTOM_SYM, Tokens.RIGHTTOP_SYM,
2514
                        Tokens.RIGHTMIDDLE_SYM, Tokens.RIGHTBOTTOM_SYM]))
2515
                {
2516
                    return SyntaxUnit.fromToken(tokenStream.token());
2517
                } else {
2518
                    return null;
2519
                }
2520

    
2521
            },
2522

    
2523
            _pseudo_page: function(){
2524

    
2525
                var tokenStream = this._tokenStream;
2526

    
2527
                tokenStream.mustMatch(Tokens.COLON);
2528
                tokenStream.mustMatch(Tokens.IDENT);
2529

    
2530
                return tokenStream.token().value;
2531
            },
2532

    
2533
            _font_face: function(){
2534
                var tokenStream = this._tokenStream,
2535
                    line,
2536
                    col;
2537
                tokenStream.mustMatch(Tokens.FONT_FACE_SYM);
2538
                line = tokenStream.token().startLine;
2539
                col = tokenStream.token().startCol;
2540

    
2541
                this._readWhitespace();
2542

    
2543
                this.fire({
2544
                    type:   "startfontface",
2545
                    line:   line,
2546
                    col:    col
2547
                });
2548

    
2549
                this._readDeclarations(true);
2550

    
2551
                this.fire({
2552
                    type:   "endfontface",
2553
                    line:   line,
2554
                    col:    col
2555
                });
2556
            },
2557

    
2558
            _viewport: function(){
2559
                 var tokenStream = this._tokenStream,
2560
                    line,
2561
                    col;
2562

    
2563
                    tokenStream.mustMatch(Tokens.VIEWPORT_SYM);
2564
                    line = tokenStream.token().startLine;
2565
                    col = tokenStream.token().startCol;
2566

    
2567
                    this._readWhitespace();
2568

    
2569
                    this.fire({
2570
                        type:   "startviewport",
2571
                        line:   line,
2572
                        col:    col
2573
                    });
2574

    
2575
                    this._readDeclarations(true);
2576

    
2577
                    this.fire({
2578
                        type:   "endviewport",
2579
                        line:   line,
2580
                        col:    col
2581
                    });
2582

    
2583
            },
2584

    
2585
            _operator: function(inFunction){
2586

    
2587
                var tokenStream = this._tokenStream,
2588
                    token       = null;
2589

    
2590
                if (tokenStream.match([Tokens.SLASH, Tokens.COMMA]) ||
2591
                    (inFunction && tokenStream.match([Tokens.PLUS, Tokens.STAR, Tokens.MINUS]))){
2592
                    token =  tokenStream.token();
2593
                    this._readWhitespace();
2594
                }
2595
                return token ? PropertyValuePart.fromToken(token) : null;
2596

    
2597
            },
2598

    
2599
            _combinator: function(){
2600

    
2601
                var tokenStream = this._tokenStream,
2602
                    value       = null,
2603
                    token;
2604

    
2605
                if(tokenStream.match([Tokens.PLUS, Tokens.GREATER, Tokens.TILDE])){
2606
                    token = tokenStream.token();
2607
                    value = new Combinator(token.value, token.startLine, token.startCol);
2608
                    this._readWhitespace();
2609
                }
2610

    
2611
                return value;
2612
            },
2613

    
2614
            _unary_operator: function(){
2615

    
2616
                var tokenStream = this._tokenStream;
2617

    
2618
                if (tokenStream.match([Tokens.MINUS, Tokens.PLUS])){
2619
                    return tokenStream.token().value;
2620
                } else {
2621
                    return null;
2622
                }
2623
            },
2624

    
2625
            _property: function(){
2626

    
2627
                var tokenStream = this._tokenStream,
2628
                    value       = null,
2629
                    hack        = null,
2630
                    tokenValue,
2631
                    token,
2632
                    line,
2633
                    col;
2634
                if (tokenStream.peek() == Tokens.STAR && this.options.starHack){
2635
                    tokenStream.get();
2636
                    token = tokenStream.token();
2637
                    hack = token.value;
2638
                    line = token.startLine;
2639
                    col = token.startCol;
2640
                }
2641

    
2642
                if(tokenStream.match(Tokens.IDENT)){
2643
                    token = tokenStream.token();
2644
                    tokenValue = token.value;
2645
                    if (tokenValue.charAt(0) == "_" && this.options.underscoreHack){
2646
                        hack = "_";
2647
                        tokenValue = tokenValue.substring(1);
2648
                    }
2649

    
2650
                    value = new PropertyName(tokenValue, hack, (line||token.startLine), (col||token.startCol));
2651
                    this._readWhitespace();
2652
                }
2653

    
2654
                return value;
2655
            },
2656
            _ruleset: function(){
2657

    
2658
                var tokenStream = this._tokenStream,
2659
                    tt,
2660
                    selectors;
2661
                try {
2662
                    selectors = this._selectors_group();
2663
                } catch (ex){
2664
                    if (ex instanceof SyntaxError && !this.options.strict){
2665
                        this.fire({
2666
                            type:       "error",
2667
                            error:      ex,
2668
                            message:    ex.message,
2669
                            line:       ex.line,
2670
                            col:        ex.col
2671
                        });
2672
                        tt = tokenStream.advance([Tokens.RBRACE]);
2673
                        if (tt == Tokens.RBRACE){
2674
                        } else {
2675
                            throw ex;
2676
                        }
2677

    
2678
                    } else {
2679
                        throw ex;
2680
                    }
2681
                    return true;
2682
                }
2683
                if (selectors){
2684

    
2685
                    this.fire({
2686
                        type:       "startrule",
2687
                        selectors:  selectors,
2688
                        line:       selectors[0].line,
2689
                        col:        selectors[0].col
2690
                    });
2691

    
2692
                    this._readDeclarations(true);
2693

    
2694
                    this.fire({
2695
                        type:       "endrule",
2696
                        selectors:  selectors,
2697
                        line:       selectors[0].line,
2698
                        col:        selectors[0].col
2699
                    });
2700

    
2701
                }
2702

    
2703
                return selectors;
2704

    
2705
            },
2706
            _selectors_group: function(){
2707
                var tokenStream = this._tokenStream,
2708
                    selectors   = [],
2709
                    selector;
2710

    
2711
                selector = this._selector();
2712
                if (selector !== null){
2713

    
2714
                    selectors.push(selector);
2715
                    while(tokenStream.match(Tokens.COMMA)){
2716
                        this._readWhitespace();
2717
                        selector = this._selector();
2718
                        if (selector !== null){
2719
                            selectors.push(selector);
2720
                        } else {
2721
                            this._unexpectedToken(tokenStream.LT(1));
2722
                        }
2723
                    }
2724
                }
2725

    
2726
                return selectors.length ? selectors : null;
2727
            },
2728
            _selector: function(){
2729

    
2730
                var tokenStream = this._tokenStream,
2731
                    selector    = [],
2732
                    nextSelector = null,
2733
                    combinator  = null,
2734
                    ws          = null;
2735
                nextSelector = this._simple_selector_sequence();
2736
                if (nextSelector === null){
2737
                    return null;
2738
                }
2739

    
2740
                selector.push(nextSelector);
2741

    
2742
                do {
2743
                    combinator = this._combinator();
2744

    
2745
                    if (combinator !== null){
2746
                        selector.push(combinator);
2747
                        nextSelector = this._simple_selector_sequence();
2748
                        if (nextSelector === null){
2749
                            this._unexpectedToken(tokenStream.LT(1));
2750
                        } else {
2751
                            selector.push(nextSelector);
2752
                        }
2753
                    } else {
2754
                        if (this._readWhitespace()){
2755
                            ws = new Combinator(tokenStream.token().value, tokenStream.token().startLine, tokenStream.token().startCol);
2756
                            combinator = this._combinator();
2757
                            nextSelector = this._simple_selector_sequence();
2758
                            if (nextSelector === null){
2759
                                if (combinator !== null){
2760
                                    this._unexpectedToken(tokenStream.LT(1));
2761
                                }
2762
                            } else {
2763

    
2764
                                if (combinator !== null){
2765
                                    selector.push(combinator);
2766
                                } else {
2767
                                    selector.push(ws);
2768
                                }
2769

    
2770
                                selector.push(nextSelector);
2771
                            }
2772
                        } else {
2773
                            break;
2774
                        }
2775

    
2776
                    }
2777
                } while(true);
2778

    
2779
                return new Selector(selector, selector[0].line, selector[0].col);
2780
            },
2781
            _simple_selector_sequence: function(){
2782

    
2783
                var tokenStream = this._tokenStream,
2784
                    elementName = null,
2785
                    modifiers   = [],
2786
                    selectorText= "",
2787
                    components  = [
2788
                        function(){
2789
                            return tokenStream.match(Tokens.HASH) ?
2790
                                    new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
2791
                                    null;
2792
                        },
2793
                        this._class,
2794
                        this._attrib,
2795
                        this._pseudo,
2796
                        this._negation
2797
                    ],
2798
                    i           = 0,
2799
                    len         = components.length,
2800
                    component   = null,
2801
                    found       = false,
2802
                    line,
2803
                    col;
2804
                line = tokenStream.LT(1).startLine;
2805
                col = tokenStream.LT(1).startCol;
2806

    
2807
                elementName = this._type_selector();
2808
                if (!elementName){
2809
                    elementName = this._universal();
2810
                }
2811

    
2812
                if (elementName !== null){
2813
                    selectorText += elementName;
2814
                }
2815

    
2816
                while(true){
2817
                    if (tokenStream.peek() === Tokens.S){
2818
                        break;
2819
                    }
2820
                    while(i < len && component === null){
2821
                        component = components[i++].call(this);
2822
                    }
2823

    
2824
                    if (component === null){
2825
                        if (selectorText === ""){
2826
                            return null;
2827
                        } else {
2828
                            break;
2829
                        }
2830
                    } else {
2831
                        i = 0;
2832
                        modifiers.push(component);
2833
                        selectorText += component.toString();
2834
                        component = null;
2835
                    }
2836
                }
2837

    
2838

    
2839
                return selectorText !== "" ?
2840
                        new SelectorPart(elementName, modifiers, selectorText, line, col) :
2841
                        null;
2842
            },
2843
            _type_selector: function(){
2844

    
2845
                var tokenStream = this._tokenStream,
2846
                    ns          = this._namespace_prefix(),
2847
                    elementName = this._element_name();
2848

    
2849
                if (!elementName){
2850
                    if (ns){
2851
                        tokenStream.unget();
2852
                        if (ns.length > 1){
2853
                            tokenStream.unget();
2854
                        }
2855
                    }
2856

    
2857
                    return null;
2858
                } else {
2859
                    if (ns){
2860
                        elementName.text = ns + elementName.text;
2861
                        elementName.col -= ns.length;
2862
                    }
2863
                    return elementName;
2864
                }
2865
            },
2866
            _class: function(){
2867

    
2868
                var tokenStream = this._tokenStream,
2869
                    token;
2870

    
2871
                if (tokenStream.match(Tokens.DOT)){
2872
                    tokenStream.mustMatch(Tokens.IDENT);
2873
                    token = tokenStream.token();
2874
                    return new SelectorSubPart("." + token.value, "class", token.startLine, token.startCol - 1);
2875
                } else {
2876
                    return null;
2877
                }
2878

    
2879
            },
2880
            _element_name: function(){
2881

    
2882
                var tokenStream = this._tokenStream,
2883
                    token;
2884

    
2885
                if (tokenStream.match(Tokens.IDENT)){
2886
                    token = tokenStream.token();
2887
                    return new SelectorSubPart(token.value, "elementName", token.startLine, token.startCol);
2888

    
2889
                } else {
2890
                    return null;
2891
                }
2892
            },
2893
            _namespace_prefix: function(){
2894
                var tokenStream = this._tokenStream,
2895
                    value       = "";
2896
                if (tokenStream.LA(1) === Tokens.PIPE || tokenStream.LA(2) === Tokens.PIPE){
2897

    
2898
                    if(tokenStream.match([Tokens.IDENT, Tokens.STAR])){
2899
                        value += tokenStream.token().value;
2900
                    }
2901

    
2902
                    tokenStream.mustMatch(Tokens.PIPE);
2903
                    value += "|";
2904

    
2905
                }
2906

    
2907
                return value.length ? value : null;
2908
            },
2909
            _universal: function(){
2910
                var tokenStream = this._tokenStream,
2911
                    value       = "",
2912
                    ns;
2913

    
2914
                ns = this._namespace_prefix();
2915
                if(ns){
2916
                    value += ns;
2917
                }
2918

    
2919
                if(tokenStream.match(Tokens.STAR)){
2920
                    value += "*";
2921
                }
2922

    
2923
                return value.length ? value : null;
2924

    
2925
           },
2926
            _attrib: function(){
2927

    
2928
                var tokenStream = this._tokenStream,
2929
                    value       = null,
2930
                    ns,
2931
                    token;
2932

    
2933
                if (tokenStream.match(Tokens.LBRACKET)){
2934
                    token = tokenStream.token();
2935
                    value = token.value;
2936
                    value += this._readWhitespace();
2937

    
2938
                    ns = this._namespace_prefix();
2939

    
2940
                    if (ns){
2941
                        value += ns;
2942
                    }
2943

    
2944
                    tokenStream.mustMatch(Tokens.IDENT);
2945
                    value += tokenStream.token().value;
2946
                    value += this._readWhitespace();
2947

    
2948
                    if(tokenStream.match([Tokens.PREFIXMATCH, Tokens.SUFFIXMATCH, Tokens.SUBSTRINGMATCH,
2949
                            Tokens.EQUALS, Tokens.INCLUDES, Tokens.DASHMATCH])){
2950

    
2951
                        value += tokenStream.token().value;
2952
                        value += this._readWhitespace();
2953

    
2954
                        tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
2955
                        value += tokenStream.token().value;
2956
                        value += this._readWhitespace();
2957
                    }
2958

    
2959
                    tokenStream.mustMatch(Tokens.RBRACKET);
2960

    
2961
                    return new SelectorSubPart(value + "]", "attribute", token.startLine, token.startCol);
2962
                } else {
2963
                    return null;
2964
                }
2965
            },
2966
            _pseudo: function(){
2967

    
2968
                var tokenStream = this._tokenStream,
2969
                    pseudo      = null,
2970
                    colons      = ":",
2971
                    line,
2972
                    col;
2973

    
2974
                if (tokenStream.match(Tokens.COLON)){
2975

    
2976
                    if (tokenStream.match(Tokens.COLON)){
2977
                        colons += ":";
2978
                    }
2979

    
2980
                    if (tokenStream.match(Tokens.IDENT)){
2981
                        pseudo = tokenStream.token().value;
2982
                        line = tokenStream.token().startLine;
2983
                        col = tokenStream.token().startCol - colons.length;
2984
                    } else if (tokenStream.peek() == Tokens.FUNCTION){
2985
                        line = tokenStream.LT(1).startLine;
2986
                        col = tokenStream.LT(1).startCol - colons.length;
2987
                        pseudo = this._functional_pseudo();
2988
                    }
2989

    
2990
                    if (pseudo){
2991
                        pseudo = new SelectorSubPart(colons + pseudo, "pseudo", line, col);
2992
                    }
2993
                }
2994

    
2995
                return pseudo;
2996
            },
2997
            _functional_pseudo: function(){
2998

    
2999
                var tokenStream = this._tokenStream,
3000
                    value = null;
3001

    
3002
                if(tokenStream.match(Tokens.FUNCTION)){
3003
                    value = tokenStream.token().value;
3004
                    value += this._readWhitespace();
3005
                    value += this._expression();
3006
                    tokenStream.mustMatch(Tokens.RPAREN);
3007
                    value += ")";
3008
                }
3009

    
3010
                return value;
3011
            },
3012
            _expression: function(){
3013

    
3014
                var tokenStream = this._tokenStream,
3015
                    value       = "";
3016

    
3017
                while(tokenStream.match([Tokens.PLUS, Tokens.MINUS, Tokens.DIMENSION,
3018
                        Tokens.NUMBER, Tokens.STRING, Tokens.IDENT, Tokens.LENGTH,
3019
                        Tokens.FREQ, Tokens.ANGLE, Tokens.TIME,
3020
                        Tokens.RESOLUTION, Tokens.SLASH])){
3021

    
3022
                    value += tokenStream.token().value;
3023
                    value += this._readWhitespace();
3024
                }
3025

    
3026
                return value.length ? value : null;
3027

    
3028
            },
3029
            _negation: function(){
3030

    
3031
                var tokenStream = this._tokenStream,
3032
                    line,
3033
                    col,
3034
                    value       = "",
3035
                    arg,
3036
                    subpart     = null;
3037

    
3038
                if (tokenStream.match(Tokens.NOT)){
3039
                    value = tokenStream.token().value;
3040
                    line = tokenStream.token().startLine;
3041
                    col = tokenStream.token().startCol;
3042
                    value += this._readWhitespace();
3043
                    arg = this._negation_arg();
3044
                    value += arg;
3045
                    value += this._readWhitespace();
3046
                    tokenStream.match(Tokens.RPAREN);
3047
                    value += tokenStream.token().value;
3048

    
3049
                    subpart = new SelectorSubPart(value, "not", line, col);
3050
                    subpart.args.push(arg);
3051
                }
3052

    
3053
                return subpart;
3054
            },
3055
            _negation_arg: function(){
3056

    
3057
                var tokenStream = this._tokenStream,
3058
                    args        = [
3059
                        this._type_selector,
3060
                        this._universal,
3061
                        function(){
3062
                            return tokenStream.match(Tokens.HASH) ?
3063
                                    new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
3064
                                    null;
3065
                        },
3066
                        this._class,
3067
                        this._attrib,
3068
                        this._pseudo
3069
                    ],
3070
                    arg         = null,
3071
                    i           = 0,
3072
                    len         = args.length,
3073
                    elementName,
3074
                    line,
3075
                    col,
3076
                    part;
3077

    
3078
                line = tokenStream.LT(1).startLine;
3079
                col = tokenStream.LT(1).startCol;
3080

    
3081
                while(i < len && arg === null){
3082

    
3083
                    arg = args[i].call(this);
3084
                    i++;
3085
                }
3086
                if (arg === null){
3087
                    this._unexpectedToken(tokenStream.LT(1));
3088
                }
3089
                if (arg.type == "elementName"){
3090
                    part = new SelectorPart(arg, [], arg.toString(), line, col);
3091
                } else {
3092
                    part = new SelectorPart(null, [arg], arg.toString(), line, col);
3093
                }
3094

    
3095
                return part;
3096
            },
3097

    
3098
            _declaration: function(){
3099

    
3100
                var tokenStream = this._tokenStream,
3101
                    property    = null,
3102
                    expr        = null,
3103
                    prio        = null,
3104
                    error       = null,
3105
                    invalid     = null,
3106
                    propertyName= "";
3107

    
3108
                property = this._property();
3109
                if (property !== null){
3110

    
3111
                    tokenStream.mustMatch(Tokens.COLON);
3112
                    this._readWhitespace();
3113

    
3114
                    expr = this._expr();
3115
                    if (!expr || expr.length === 0){
3116
                        this._unexpectedToken(tokenStream.LT(1));
3117
                    }
3118

    
3119
                    prio = this._prio();
3120
                    propertyName = property.toString();
3121
                    if (this.options.starHack && property.hack == "*" ||
3122
                            this.options.underscoreHack && property.hack == "_") {
3123

    
3124
                        propertyName = property.text;
3125
                    }
3126

    
3127
                    try {
3128
                        this._validateProperty(propertyName, expr);
3129
                    } catch (ex) {
3130
                        invalid = ex;
3131
                    }
3132

    
3133
                    this.fire({
3134
                        type:       "property",
3135
                        property:   property,
3136
                        value:      expr,
3137
                        important:  prio,
3138
                        line:       property.line,
3139
                        col:        property.col,
3140
                        invalid:    invalid
3141
                    });
3142

    
3143
                    return true;
3144
                } else {
3145
                    return false;
3146
                }
3147
            },
3148

    
3149
            _prio: function(){
3150

    
3151
                var tokenStream = this._tokenStream,
3152
                    result      = tokenStream.match(Tokens.IMPORTANT_SYM);
3153

    
3154
                this._readWhitespace();
3155
                return result;
3156
            },
3157

    
3158
            _expr: function(inFunction){
3159

    
3160
                var tokenStream = this._tokenStream,
3161
                    values      = [],
3162
                    value       = null,
3163
                    operator    = null;
3164

    
3165
                value = this._term(inFunction);
3166
                if (value !== null){
3167

    
3168
                    values.push(value);
3169

    
3170
                    do {
3171
                        operator = this._operator(inFunction);
3172
                        if (operator){
3173
                            values.push(operator);
3174
                        } /*else {
3175
                            values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
3176
                            valueParts = [];
3177
                        }*/
3178

    
3179
                        value = this._term(inFunction);
3180

    
3181
                        if (value === null){
3182
                            break;
3183
                        } else {
3184
                            values.push(value);
3185
                        }
3186
                    } while(true);
3187
                }
3188

    
3189
                return values.length > 0 ? new PropertyValue(values, values[0].line, values[0].col) : null;
3190
            },
3191

    
3192
            _term: function(inFunction){
3193

    
3194
                var tokenStream = this._tokenStream,
3195
                    unary       = null,
3196
                    value       = null,
3197
                    endChar     = null,
3198
                    token,
3199
                    line,
3200
                    col;
3201
                unary = this._unary_operator();
3202
                if (unary !== null){
3203
                    line = tokenStream.token().startLine;
3204
                    col = tokenStream.token().startCol;
3205
                }
3206
                if (tokenStream.peek() == Tokens.IE_FUNCTION && this.options.ieFilters){
3207

    
3208
                    value = this._ie_function();
3209
                    if (unary === null){
3210
                        line = tokenStream.token().startLine;
3211
                        col = tokenStream.token().startCol;
3212
                    }
3213
                } else if (inFunction && tokenStream.match([Tokens.LPAREN, Tokens.LBRACE, Tokens.LBRACKET])){
3214

    
3215
                    token = tokenStream.token();
3216
                    endChar = token.endChar;
3217
                    value = token.value + this._expr(inFunction).text;
3218
                    if (unary === null){
3219
                        line = tokenStream.token().startLine;
3220
                        col = tokenStream.token().startCol;
3221
                    }
3222
                    tokenStream.mustMatch(Tokens.type(endChar));
3223
                    value += endChar;
3224
                    this._readWhitespace();
3225
                } else if (tokenStream.match([Tokens.NUMBER, Tokens.PERCENTAGE, Tokens.LENGTH,
3226
                        Tokens.ANGLE, Tokens.TIME,
3227
                        Tokens.FREQ, Tokens.STRING, Tokens.IDENT, Tokens.URI, Tokens.UNICODE_RANGE])){
3228

    
3229
                    value = tokenStream.token().value;
3230
                    if (unary === null){
3231
                        line = tokenStream.token().startLine;
3232
                        col = tokenStream.token().startCol;
3233
                    }
3234
                    this._readWhitespace();
3235
                } else {
3236
                    token = this._hexcolor();
3237
                    if (token === null){
3238
                        if (unary === null){
3239
                            line = tokenStream.LT(1).startLine;
3240
                            col = tokenStream.LT(1).startCol;
3241
                        }
3242
                        if (value === null){
3243
                            if (tokenStream.LA(3) == Tokens.EQUALS && this.options.ieFilters){
3244
                                value = this._ie_function();
3245
                            } else {
3246
                                value = this._function();
3247
                            }
3248
                        }
3249

    
3250
                    } else {
3251
                        value = token.value;
3252
                        if (unary === null){
3253
                            line = token.startLine;
3254
                            col = token.startCol;
3255
                        }
3256
                    }
3257

    
3258
                }
3259

    
3260
                return value !== null ?
3261
                        new PropertyValuePart(unary !== null ? unary + value : value, line, col) :
3262
                        null;
3263

    
3264
            },
3265

    
3266
            _function: function(){
3267

    
3268
                var tokenStream = this._tokenStream,
3269
                    functionText = null,
3270
                    expr        = null,
3271
                    lt;
3272

    
3273
                if (tokenStream.match(Tokens.FUNCTION)){
3274
                    functionText = tokenStream.token().value;
3275
                    this._readWhitespace();
3276
                    expr = this._expr(true);
3277
                    functionText += expr;
3278
                    if (this.options.ieFilters && tokenStream.peek() == Tokens.EQUALS){
3279
                        do {
3280

    
3281
                            if (this._readWhitespace()){
3282
                                functionText += tokenStream.token().value;
3283
                            }
3284
                            if (tokenStream.LA(0) == Tokens.COMMA){
3285
                                functionText += tokenStream.token().value;
3286
                            }
3287

    
3288
                            tokenStream.match(Tokens.IDENT);
3289
                            functionText += tokenStream.token().value;
3290

    
3291
                            tokenStream.match(Tokens.EQUALS);
3292
                            functionText += tokenStream.token().value;
3293
                            lt = tokenStream.peek();
3294
                            while(lt != Tokens.COMMA && lt != Tokens.S && lt != Tokens.RPAREN){
3295
                                tokenStream.get();
3296
                                functionText += tokenStream.token().value;
3297
                                lt = tokenStream.peek();
3298
                            }
3299
                        } while(tokenStream.match([Tokens.COMMA, Tokens.S]));
3300
                    }
3301

    
3302
                    tokenStream.match(Tokens.RPAREN);
3303
                    functionText += ")";
3304
                    this._readWhitespace();
3305
                }
3306

    
3307
                return functionText;
3308
            },
3309

    
3310
            _ie_function: function(){
3311

    
3312
                var tokenStream = this._tokenStream,
3313
                    functionText = null,
3314
                    expr        = null,
3315
                    lt;
3316
                if (tokenStream.match([Tokens.IE_FUNCTION, Tokens.FUNCTION])){
3317
                    functionText = tokenStream.token().value;
3318

    
3319
                    do {
3320

    
3321
                        if (this._readWhitespace()){
3322
                            functionText += tokenStream.token().value;
3323
                        }
3324
                        if (tokenStream.LA(0) == Tokens.COMMA){
3325
                            functionText += tokenStream.token().value;
3326
                        }
3327

    
3328
                        tokenStream.match(Tokens.IDENT);
3329
                        functionText += tokenStream.token().value;
3330

    
3331
                        tokenStream.match(Tokens.EQUALS);
3332
                        functionText += tokenStream.token().value;
3333
                        lt = tokenStream.peek();
3334
                        while(lt != Tokens.COMMA && lt != Tokens.S && lt != Tokens.RPAREN){
3335
                            tokenStream.get();
3336
                            functionText += tokenStream.token().value;
3337
                            lt = tokenStream.peek();
3338
                        }
3339
                    } while(tokenStream.match([Tokens.COMMA, Tokens.S]));
3340

    
3341
                    tokenStream.match(Tokens.RPAREN);
3342
                    functionText += ")";
3343
                    this._readWhitespace();
3344
                }
3345

    
3346
                return functionText;
3347
            },
3348

    
3349
            _hexcolor: function(){
3350

    
3351
                var tokenStream = this._tokenStream,
3352
                    token = null,
3353
                    color;
3354

    
3355
                if(tokenStream.match(Tokens.HASH)){
3356

    
3357
                    token = tokenStream.token();
3358
                    color = token.value;
3359
                    if (!/#[a-f0-9]{3,6}/i.test(color)){
3360
                        throw new SyntaxError("Expected a hex color but found '" + color + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
3361
                    }
3362
                    this._readWhitespace();
3363
                }
3364

    
3365
                return token;
3366
            },
3367

    
3368
            _keyframes: function(){
3369
                var tokenStream = this._tokenStream,
3370
                    token,
3371
                    tt,
3372
                    name,
3373
                    prefix = "";
3374

    
3375
                tokenStream.mustMatch(Tokens.KEYFRAMES_SYM);
3376
                token = tokenStream.token();
3377
                if (/^@\-([^\-]+)\-/.test(token.value)) {
3378
                    prefix = RegExp.$1;
3379
                }
3380

    
3381
                this._readWhitespace();
3382
                name = this._keyframe_name();
3383

    
3384
                this._readWhitespace();
3385
                tokenStream.mustMatch(Tokens.LBRACE);
3386

    
3387
                this.fire({
3388
                    type:   "startkeyframes",
3389
                    name:   name,
3390
                    prefix: prefix,
3391
                    line:   token.startLine,
3392
                    col:    token.startCol
3393
                });
3394

    
3395
                this._readWhitespace();
3396
                tt = tokenStream.peek();
3397
                while(tt == Tokens.IDENT || tt == Tokens.PERCENTAGE) {
3398
                    this._keyframe_rule();
3399
                    this._readWhitespace();
3400
                    tt = tokenStream.peek();
3401
                }
3402

    
3403
                this.fire({
3404
                    type:   "endkeyframes",
3405
                    name:   name,
3406
                    prefix: prefix,
3407
                    line:   token.startLine,
3408
                    col:    token.startCol
3409
                });
3410

    
3411
                this._readWhitespace();
3412
                tokenStream.mustMatch(Tokens.RBRACE);
3413

    
3414
            },
3415

    
3416
            _keyframe_name: function(){
3417
                var tokenStream = this._tokenStream,
3418
                    token;
3419

    
3420
                tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
3421
                return SyntaxUnit.fromToken(tokenStream.token());
3422
            },
3423

    
3424
            _keyframe_rule: function(){
3425
                var tokenStream = this._tokenStream,
3426
                    token,
3427
                    keyList = this._key_list();
3428

    
3429
                this.fire({
3430
                    type:   "startkeyframerule",
3431
                    keys:   keyList,
3432
                    line:   keyList[0].line,
3433
                    col:    keyList[0].col
3434
                });
3435

    
3436
                this._readDeclarations(true);
3437

    
3438
                this.fire({
3439
                    type:   "endkeyframerule",
3440
                    keys:   keyList,
3441
                    line:   keyList[0].line,
3442
                    col:    keyList[0].col
3443
                });
3444

    
3445
            },
3446

    
3447
            _key_list: function(){
3448
                var tokenStream = this._tokenStream,
3449
                    token,
3450
                    key,
3451
                    keyList = [];
3452
                keyList.push(this._key());
3453

    
3454
                this._readWhitespace();
3455

    
3456
                while(tokenStream.match(Tokens.COMMA)){
3457
                    this._readWhitespace();
3458
                    keyList.push(this._key());
3459
                    this._readWhitespace();
3460
                }
3461

    
3462
                return keyList;
3463
            },
3464

    
3465
            _key: function(){
3466

    
3467
                var tokenStream = this._tokenStream,
3468
                    token;
3469

    
3470
                if (tokenStream.match(Tokens.PERCENTAGE)){
3471
                    return SyntaxUnit.fromToken(tokenStream.token());
3472
                } else if (tokenStream.match(Tokens.IDENT)){
3473
                    token = tokenStream.token();
3474

    
3475
                    if (/from|to/i.test(token.value)){
3476
                        return SyntaxUnit.fromToken(token);
3477
                    }
3478

    
3479
                    tokenStream.unget();
3480
                }
3481
                this._unexpectedToken(tokenStream.LT(1));
3482
            },
3483
            _skipCruft: function(){
3484
                while(this._tokenStream.match([Tokens.S, Tokens.CDO, Tokens.CDC])){
3485
                }
3486
            },
3487
            _readDeclarations: function(checkStart, readMargins){
3488
                var tokenStream = this._tokenStream,
3489
                    tt;
3490

    
3491

    
3492
                this._readWhitespace();
3493

    
3494
                if (checkStart){
3495
                    tokenStream.mustMatch(Tokens.LBRACE);
3496
                }
3497

    
3498
                this._readWhitespace();
3499

    
3500
                try {
3501

    
3502
                    while(true){
3503

    
3504
                        if (tokenStream.match(Tokens.SEMICOLON) || (readMargins && this._margin())){
3505
                        } else if (this._declaration()){
3506
                            if (!tokenStream.match(Tokens.SEMICOLON)){
3507
                                break;
3508
                            }
3509
                        } else {
3510
                            break;
3511
                        }
3512
                        this._readWhitespace();
3513
                    }
3514

    
3515
                    tokenStream.mustMatch(Tokens.RBRACE);
3516
                    this._readWhitespace();
3517

    
3518
                } catch (ex) {
3519
                    if (ex instanceof SyntaxError && !this.options.strict){
3520
                        this.fire({
3521
                            type:       "error",
3522
                            error:      ex,
3523
                            message:    ex.message,
3524
                            line:       ex.line,
3525
                            col:        ex.col
3526
                        });
3527
                        tt = tokenStream.advance([Tokens.SEMICOLON, Tokens.RBRACE]);
3528
                        if (tt == Tokens.SEMICOLON){
3529
                            this._readDeclarations(false, readMargins);
3530
                        } else if (tt != Tokens.RBRACE){
3531
                            throw ex;
3532
                        }
3533

    
3534
                    } else {
3535
                        throw ex;
3536
                    }
3537
                }
3538

    
3539
            },
3540
            _readWhitespace: function(){
3541

    
3542
                var tokenStream = this._tokenStream,
3543
                    ws = "";
3544

    
3545
                while(tokenStream.match(Tokens.S)){
3546
                    ws += tokenStream.token().value;
3547
                }
3548

    
3549
                return ws;
3550
            },
3551
            _unexpectedToken: function(token){
3552
                throw new SyntaxError("Unexpected token '" + token.value + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
3553
            },
3554
            _verifyEnd: function(){
3555
                if (this._tokenStream.LA(1) != Tokens.EOF){
3556
                    this._unexpectedToken(this._tokenStream.LT(1));
3557
                }
3558
            },
3559
            _validateProperty: function(property, value){
3560
                Validation.validate(property, value);
3561
            },
3562

    
3563
            parse: function(input){
3564
                this._tokenStream = new TokenStream(input, Tokens);
3565
                this._stylesheet();
3566
            },
3567

    
3568
            parseStyleSheet: function(input){
3569
                return this.parse(input);
3570
            },
3571

    
3572
            parseMediaQuery: function(input){
3573
                this._tokenStream = new TokenStream(input, Tokens);
3574
                var result = this._media_query();
3575
                this._verifyEnd();
3576
                return result;
3577
            },
3578
            parsePropertyValue: function(input){
3579

    
3580
                this._tokenStream = new TokenStream(input, Tokens);
3581
                this._readWhitespace();
3582

    
3583
                var result = this._expr();
3584
                this._readWhitespace();
3585
                this._verifyEnd();
3586
                return result;
3587
            },
3588
            parseRule: function(input){
3589
                this._tokenStream = new TokenStream(input, Tokens);
3590
                this._readWhitespace();
3591

    
3592
                var result = this._ruleset();
3593
                this._readWhitespace();
3594
                this._verifyEnd();
3595
                return result;
3596
            },
3597
            parseSelector: function(input){
3598

    
3599
                this._tokenStream = new TokenStream(input, Tokens);
3600
                this._readWhitespace();
3601

    
3602
                var result = this._selector();
3603
                this._readWhitespace();
3604
                this._verifyEnd();
3605
                return result;
3606
            },
3607
            parseStyleAttribute: function(input){
3608
                input += "}"; // for error recovery in _readDeclarations()
3609
                this._tokenStream = new TokenStream(input, Tokens);
3610
                this._readDeclarations();
3611
            }
3612
        };
3613
    for (prop in additions){
3614
        if (additions.hasOwnProperty(prop)){
3615
            proto[prop] = additions[prop];
3616
        }
3617
    }
3618

    
3619
    return proto;
3620
}();
3621
var Properties = {
3622
    "align-items"                   : "flex-start | flex-end | center | baseline | stretch",
3623
    "align-content"                 : "flex-start | flex-end | center | space-between | space-around | stretch",
3624
    "align-self"                    : "auto | flex-start | flex-end | center | baseline | stretch",
3625
    "-webkit-align-items"           : "flex-start | flex-end | center | baseline | stretch",
3626
    "-webkit-align-content"         : "flex-start | flex-end | center | space-between | space-around | stretch",
3627
    "-webkit-align-self"            : "auto | flex-start | flex-end | center | baseline | stretch",
3628
    "alignment-adjust"              : "auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | <percentage> | <length>",
3629
    "alignment-baseline"            : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
3630
    "animation"                     : 1,
3631
    "animation-delay"               : { multi: "<time>", comma: true },
3632
    "animation-direction"           : { multi: "normal | reverse | alternate | alternate-reverse", comma: true },
3633
    "animation-duration"            : { multi: "<time>", comma: true },
3634
    "animation-fill-mode"           : { multi: "none | forwards | backwards | both", comma: true },
3635
    "animation-iteration-count"     : { multi: "<number> | infinite", comma: true },
3636
    "animation-name"                : { multi: "none | <ident>", comma: true },
3637
    "animation-play-state"          : { multi: "running | paused", comma: true },
3638
    "animation-timing-function"     : 1,
3639
    "-moz-animation-delay"               : { multi: "<time>", comma: true },
3640
    "-moz-animation-direction"           : { multi: "normal | reverse | alternate | alternate-reverse", comma: true },
3641
    "-moz-animation-duration"            : { multi: "<time>", comma: true },
3642
    "-moz-animation-iteration-count"     : { multi: "<number> | infinite", comma: true },
3643
    "-moz-animation-name"                : { multi: "none | <ident>", comma: true },
3644
    "-moz-animation-play-state"          : { multi: "running | paused", comma: true },
3645

    
3646
    "-ms-animation-delay"               : { multi: "<time>", comma: true },
3647
    "-ms-animation-direction"           : { multi: "normal | reverse | alternate | alternate-reverse", comma: true },
3648
    "-ms-animation-duration"            : { multi: "<time>", comma: true },
3649
    "-ms-animation-iteration-count"     : { multi: "<number> | infinite", comma: true },
3650
    "-ms-animation-name"                : { multi: "none | <ident>", comma: true },
3651
    "-ms-animation-play-state"          : { multi: "running | paused", comma: true },
3652

    
3653
    "-webkit-animation-delay"               : { multi: "<time>", comma: true },
3654
    "-webkit-animation-direction"           : { multi: "normal | reverse | alternate | alternate-reverse", comma: true },
3655
    "-webkit-animation-duration"            : { multi: "<time>", comma: true },
3656
    "-webkit-animation-fill-mode"           : { multi: "none | forwards | backwards | both", comma: true },
3657
    "-webkit-animation-iteration-count"     : { multi: "<number> | infinite", comma: true },
3658
    "-webkit-animation-name"                : { multi: "none | <ident>", comma: true },
3659
    "-webkit-animation-play-state"          : { multi: "running | paused", comma: true },
3660

    
3661
    "-o-animation-delay"               : { multi: "<time>", comma: true },
3662
    "-o-animation-direction"           : { multi: "normal | reverse | alternate | alternate-reverse", comma: true },
3663
    "-o-animation-duration"            : { multi: "<time>", comma: true },
3664
    "-o-animation-iteration-count"     : { multi: "<number> | infinite", comma: true },
3665
    "-o-animation-name"                : { multi: "none | <ident>", comma: true },
3666
    "-o-animation-play-state"          : { multi: "running | paused", comma: true },
3667

    
3668
    "appearance"                    : "icon | window | desktop | workspace | document | tooltip | dialog | button | push-button | hyperlink | radio-button | checkbox | menu-item | tab | menu | menubar | pull-down-menu | pop-up-menu | list-menu | radio-group | checkbox-group | outline-tree | range | field | combo-box | signature | password | normal | none | inherit",
3669
    "azimuth"                       : function (expression) {
3670
        var simple      = "<angle> | leftwards | rightwards | inherit",
3671
            direction   = "left-side | far-left | left | center-left | center | center-right | right | far-right | right-side",
3672
            behind      = false,
3673
            valid       = false,
3674
            part;
3675

    
3676
        if (!ValidationTypes.isAny(expression, simple)) {
3677
            if (ValidationTypes.isAny(expression, "behind")) {
3678
                behind = true;
3679
                valid = true;
3680
            }
3681

    
3682
            if (ValidationTypes.isAny(expression, direction)) {
3683
                valid = true;
3684
                if (!behind) {
3685
                    ValidationTypes.isAny(expression, "behind");
3686
                }
3687
            }
3688
        }
3689

    
3690
        if (expression.hasNext()) {
3691
            part = expression.next();
3692
            if (valid) {
3693
                throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
3694
            } else {
3695
                throw new ValidationError("Expected (<'azimuth'>) but found '" + part + "'.", part.line, part.col);
3696
            }
3697
        }
3698
    },
3699
    "backface-visibility"           : "visible | hidden",
3700
    "background"                    : 1,
3701
    "background-attachment"         : { multi: "<attachment>", comma: true },
3702
    "background-clip"               : { multi: "<box>", comma: true },
3703
    "background-color"              : "<color> | inherit",
3704
    "background-image"              : { multi: "<bg-image>", comma: true },
3705
    "background-origin"             : { multi: "<box>", comma: true },
3706
    "background-position"           : { multi: "<bg-position>", comma: true },
3707
    "background-repeat"             : { multi: "<repeat-style>" },
3708
    "background-size"               : { multi: "<bg-size>", comma: true },
3709
    "baseline-shift"                : "baseline | sub | super | <percentage> | <length>",
3710
    "behavior"                      : 1,
3711
    "binding"                       : 1,
3712
    "bleed"                         : "<length>",
3713
    "bookmark-label"                : "<content> | <attr> | <string>",
3714
    "bookmark-level"                : "none | <integer>",
3715
    "bookmark-state"                : "open | closed",
3716
    "bookmark-target"               : "none | <uri> | <attr>",
3717
    "border"                        : "<border-width> || <border-style> || <color>",
3718
    "border-bottom"                 : "<border-width> || <border-style> || <color>",
3719
    "border-bottom-color"           : "<color> | inherit",
3720
    "border-bottom-left-radius"     :  "<x-one-radius>",
3721
    "border-bottom-right-radius"    :  "<x-one-radius>",
3722
    "border-bottom-style"           : "<border-style>",
3723
    "border-bottom-width"           : "<border-width>",
3724
    "border-collapse"               : "collapse | separate | inherit",
3725
    "border-color"                  : { multi: "<color> | inherit", max: 4 },
3726
    "border-image"                  : 1,
3727
    "border-image-outset"           : { multi: "<length> | <number>", max: 4 },
3728
    "border-image-repeat"           : { multi: "stretch | repeat | round", max: 2 },
3729
    "border-image-slice"            : function(expression) {
3730

    
3731
        var valid   = false,
3732
            numeric = "<number> | <percentage>",
3733
            fill    = false,
3734
            count   = 0,
3735
            max     = 4,
3736
            part;
3737

    
3738
        if (ValidationTypes.isAny(expression, "fill")) {
3739
            fill = true;
3740
            valid = true;
3741
        }
3742

    
3743
        while (expression.hasNext() && count < max) {
3744
            valid = ValidationTypes.isAny(expression, numeric);
3745
            if (!valid) {
3746
                break;
3747
            }
3748
            count++;
3749
        }
3750

    
3751

    
3752
        if (!fill) {
3753
            ValidationTypes.isAny(expression, "fill");
3754
        } else {
3755
            valid = true;
3756
        }
3757

    
3758
        if (expression.hasNext()) {
3759
            part = expression.next();
3760
            if (valid) {
3761
                throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
3762
            } else {
3763
                throw new ValidationError("Expected ([<number> | <percentage>]{1,4} && fill?) but found '" + part + "'.", part.line, part.col);
3764
            }
3765
        }
3766
    },
3767
    "border-image-source"           : "<image> | none",
3768
    "border-image-width"            : { multi: "<length> | <percentage> | <number> | auto", max: 4 },
3769
    "border-left"                   : "<border-width> || <border-style> || <color>",
3770
    "border-left-color"             : "<color> | inherit",
3771
    "border-left-style"             : "<border-style>",
3772
    "border-left-width"             : "<border-width>",
3773
    "border-radius"                 : function(expression) {
3774

    
3775
        var valid   = false,
3776
            simple = "<length> | <percentage> | inherit",
3777
            slash   = false,
3778
            fill    = false,
3779
            count   = 0,
3780
            max     = 8,
3781
            part;
3782

    
3783
        while (expression.hasNext() && count < max) {
3784
            valid = ValidationTypes.isAny(expression, simple);
3785
            if (!valid) {
3786

    
3787
                if (expression.peek() == "/" && count > 0 && !slash) {
3788
                    slash = true;
3789
                    max = count + 5;
3790
                    expression.next();
3791
                } else {
3792
                    break;
3793
                }
3794
            }
3795
            count++;
3796
        }
3797

    
3798
        if (expression.hasNext()) {
3799
            part = expression.next();
3800
            if (valid) {
3801
                throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
3802
            } else {
3803
                throw new ValidationError("Expected (<'border-radius'>) but found '" + part + "'.", part.line, part.col);
3804
            }
3805
        }
3806
    },
3807
    "border-right"                  : "<border-width> || <border-style> || <color>",
3808
    "border-right-color"            : "<color> | inherit",
3809
    "border-right-style"            : "<border-style>",
3810
    "border-right-width"            : "<border-width>",
3811
    "border-spacing"                : { multi: "<length> | inherit", max: 2 },
3812
    "border-style"                  : { multi: "<border-style>", max: 4 },
3813
    "border-top"                    : "<border-width> || <border-style> || <color>",
3814
    "border-top-color"              : "<color> | inherit",
3815
    "border-top-left-radius"        : "<x-one-radius>",
3816
    "border-top-right-radius"       : "<x-one-radius>",
3817
    "border-top-style"              : "<border-style>",
3818
    "border-top-width"              : "<border-width>",
3819
    "border-width"                  : { multi: "<border-width>", max: 4 },
3820
    "bottom"                        : "<margin-width> | inherit",
3821
    "-moz-box-align"                : "start | end | center | baseline | stretch",
3822
    "-moz-box-decoration-break"     : "slice |clone",
3823
    "-moz-box-direction"            : "normal | reverse | inherit",
3824
    "-moz-box-flex"                 : "<number>",
3825
    "-moz-box-flex-group"           : "<integer>",
3826
    "-moz-box-lines"                : "single | multiple",
3827
    "-moz-box-ordinal-group"        : "<integer>",
3828
    "-moz-box-orient"               : "horizontal | vertical | inline-axis | block-axis | inherit",
3829
    "-moz-box-pack"                 : "start | end | center | justify",
3830
    "-webkit-box-align"             : "start | end | center | baseline | stretch",
3831
    "-webkit-box-decoration-break"  : "slice |clone",
3832
    "-webkit-box-direction"         : "normal | reverse | inherit",
3833
    "-webkit-box-flex"              : "<number>",
3834
    "-webkit-box-flex-group"        : "<integer>",
3835
    "-webkit-box-lines"             : "single | multiple",
3836
    "-webkit-box-ordinal-group"     : "<integer>",
3837
    "-webkit-box-orient"            : "horizontal | vertical | inline-axis | block-axis | inherit",
3838
    "-webkit-box-pack"              : "start | end | center | justify",
3839
    "box-shadow"                    : function (expression) {
3840
        var result      = false,
3841
            part;
3842

    
3843
        if (!ValidationTypes.isAny(expression, "none")) {
3844
            Validation.multiProperty("<shadow>", expression, true, Infinity);
3845
        } else {
3846
            if (expression.hasNext()) {
3847
                part = expression.next();
3848
                throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
3849
            }
3850
        }
3851
    },
3852
    "box-sizing"                    : "content-box | border-box | inherit",
3853
    "break-after"                   : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
3854
    "break-before"                  : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
3855
    "break-inside"                  : "auto | avoid | avoid-page | avoid-column",
3856
    "caption-side"                  : "top | bottom | inherit",
3857
    "clear"                         : "none | right | left | both | inherit",
3858
    "clip"                          : 1,
3859
    "color"                         : "<color> | inherit",
3860
    "color-profile"                 : 1,
3861
    "column-count"                  : "<integer> | auto",                      //http://www.w3.org/TR/css3-multicol/
3862
    "column-fill"                   : "auto | balance",
3863
    "column-gap"                    : "<length> | normal",
3864
    "column-rule"                   : "<border-width> || <border-style> || <color>",
3865
    "column-rule-color"             : "<color>",
3866
    "column-rule-style"             : "<border-style>",
3867
    "column-rule-width"             : "<border-width>",
3868
    "column-span"                   : "none | all",
3869
    "column-width"                  : "<length> | auto",
3870
    "columns"                       : 1,
3871
    "content"                       : 1,
3872
    "counter-increment"             : 1,
3873
    "counter-reset"                 : 1,
3874
    "crop"                          : "<shape> | auto",
3875
    "cue"                           : "cue-after | cue-before | inherit",
3876
    "cue-after"                     : 1,
3877
    "cue-before"                    : 1,
3878
    "cursor"                        : 1,
3879
    "direction"                     : "ltr | rtl | inherit",
3880
    "display"                       : "inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | grid | inline-grid | none | inherit | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker | -webkit-box | -webkit-inline-box | -ms-flexbox | -ms-inline-flexbox | flex | -webkit-flex | inline-flex | -webkit-inline-flex",
3881
    "dominant-baseline"             : 1,
3882
    "drop-initial-after-adjust"     : "central | middle | after-edge | text-after-edge | ideographic | alphabetic | mathematical | <percentage> | <length>",
3883
    "drop-initial-after-align"      : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
3884
    "drop-initial-before-adjust"    : "before-edge | text-before-edge | central | middle | hanging | mathematical | <percentage> | <length>",
3885
    "drop-initial-before-align"     : "caps-height | baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
3886
    "drop-initial-size"             : "auto | line | <length> | <percentage>",
3887
    "drop-initial-value"            : "initial | <integer>",
3888
    "elevation"                     : "<angle> | below | level | above | higher | lower | inherit",
3889
    "empty-cells"                   : "show | hide | inherit",
3890
    "filter"                        : 1,
3891
    "fit"                           : "fill | hidden | meet | slice",
3892
    "fit-position"                  : 1,
3893
    "flex"                          : "<flex>",
3894
    "flex-basis"                    : "<width>",
3895
    "flex-direction"                : "row | row-reverse | column | column-reverse",
3896
    "flex-flow"                     : "<flex-direction> || <flex-wrap>",
3897
    "flex-grow"                     : "<number>",
3898
    "flex-shrink"                   : "<number>",
3899
    "flex-wrap"                     : "nowrap | wrap | wrap-reverse",
3900
    "-webkit-flex"                  : "<flex>",
3901
    "-webkit-flex-basis"            : "<width>",
3902
    "-webkit-flex-direction"        : "row | row-reverse | column | column-reverse",
3903
    "-webkit-flex-flow"             : "<flex-direction> || <flex-wrap>",
3904
    "-webkit-flex-grow"             : "<number>",
3905
    "-webkit-flex-shrink"           : "<number>",
3906
    "-webkit-flex-wrap"             : "nowrap | wrap | wrap-reverse",
3907
    "-ms-flex"                      : "<flex>",
3908
    "-ms-flex-align"                : "start | end | center | stretch | baseline",
3909
    "-ms-flex-direction"            : "row | row-reverse | column | column-reverse | inherit",
3910
    "-ms-flex-order"                : "<number>",
3911
    "-ms-flex-pack"                 : "start | end | center | justify",
3912
    "-ms-flex-wrap"                 : "nowrap | wrap | wrap-reverse",
3913
    "float"                         : "left | right | none | inherit",
3914
    "float-offset"                  : 1,
3915
    "font"                          : 1,
3916
    "font-family"                   : 1,
3917
    "font-size"                     : "<absolute-size> | <relative-size> | <length> | <percentage> | inherit",
3918
    "font-size-adjust"              : "<number> | none | inherit",
3919
    "font-stretch"                  : "normal | ultra-condensed | extra-condensed | condensed | semi-condensed | semi-expanded | expanded | extra-expanded | ultra-expanded | inherit",
3920
    "font-style"                    : "normal | italic | oblique | inherit",
3921
    "font-variant"                  : "normal | small-caps | inherit",
3922
    "font-weight"                   : "normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | inherit",
3923
    "grid-cell-stacking"            : "columns | rows | layer",
3924
    "grid-column"                   : 1,
3925
    "grid-columns"                  : 1,
3926
    "grid-column-align"             : "start | end | center | stretch",
3927
    "grid-column-sizing"            : 1,
3928
    "grid-column-span"              : "<integer>",
3929
    "grid-flow"                     : "none | rows | columns",
3930
    "grid-layer"                    : "<integer>",
3931
    "grid-row"                      : 1,
3932
    "grid-rows"                     : 1,
3933
    "grid-row-align"                : "start | end | center | stretch",
3934
    "grid-row-gap"                  : 1,
3935
    "grid-row-span"                 : "<integer>",
3936
    "grid-row-sizing"               : 1,
3937
    "grid-template"                 : 1,
3938
    "grid-template-areas"           : 1,
3939
    "grid-template-columns"         : 1,
3940
    "grid-template-rows"            : 1,
3941
    "hanging-punctuation"           : 1,
3942
    "height"                        : "<margin-width> | <content-sizing> | inherit",
3943
    "hyphenate-after"               : "<integer> | auto",
3944
    "hyphenate-before"              : "<integer> | auto",
3945
    "hyphenate-character"           : "<string> | auto",
3946
    "hyphenate-lines"               : "no-limit | <integer>",
3947
    "hyphenate-resource"            : 1,
3948
    "hyphens"                       : "none | manual | auto",
3949
    "icon"                          : 1,
3950
    "image-orientation"             : "angle | auto",
3951
    "image-rendering"               : 1,
3952
    "image-resolution"              : 1,
3953
    "inline-box-align"              : "initial | last | <integer>",
3954
    "justify-content"               : "flex-start | flex-end | center | space-between | space-around",
3955
    "-webkit-justify-content"       : "flex-start | flex-end | center | space-between | space-around",
3956
    "left"                          : "<margin-width> | inherit",
3957
    "letter-spacing"                : "<length> | normal | inherit",
3958
    "line-height"                   : "<number> | <length> | <percentage> | normal | inherit",
3959
    "line-break"                    : "auto | loose | normal | strict",
3960
    "line-stacking"                 : 1,
3961
    "line-stacking-ruby"            : "exclude-ruby | include-ruby",
3962
    "line-stacking-shift"           : "consider-shifts | disregard-shifts",
3963
    "line-stacking-strategy"        : "inline-line-height | block-line-height | max-height | grid-height",
3964
    "list-style"                    : 1,
3965
    "list-style-image"              : "<uri> | none | inherit",
3966
    "list-style-position"           : "inside | outside | inherit",
3967
    "list-style-type"               : "disc | circle | square | decimal | decimal-leading-zero | lower-roman | upper-roman | lower-greek | lower-latin | upper-latin | armenian | georgian | lower-alpha | upper-alpha | none | inherit",
3968
    "margin"                        : { multi: "<margin-width> | inherit", max: 4 },
3969
    "margin-bottom"                 : "<margin-width> | inherit",
3970
    "margin-left"                   : "<margin-width> | inherit",
3971
    "margin-right"                  : "<margin-width> | inherit",
3972
    "margin-top"                    : "<margin-width> | inherit",
3973
    "mark"                          : 1,
3974
    "mark-after"                    : 1,
3975
    "mark-before"                   : 1,
3976
    "marks"                         : 1,
3977
    "marquee-direction"             : 1,
3978
    "marquee-play-count"            : 1,
3979
    "marquee-speed"                 : 1,
3980
    "marquee-style"                 : 1,
3981
    "max-height"                    : "<length> | <percentage> | <content-sizing> | none | inherit",
3982
    "max-width"                     : "<length> | <percentage> | <content-sizing> | none | inherit",
3983
    "max-zoom"                      : "<number> | <percentage> | auto",
3984
    "min-height"                    : "<length> | <percentage> | <content-sizing> | contain-floats | -moz-contain-floats | -webkit-contain-floats | inherit",
3985
    "min-width"                     : "<length> | <percentage> | <content-sizing> | contain-floats | -moz-contain-floats | -webkit-contain-floats | inherit",
3986
    "min-zoom"                      : "<number> | <percentage> | auto",
3987
    "move-to"                       : 1,
3988
    "nav-down"                      : 1,
3989
    "nav-index"                     : 1,
3990
    "nav-left"                      : 1,
3991
    "nav-right"                     : 1,
3992
    "nav-up"                        : 1,
3993
    "opacity"                       : "<number> | inherit",
3994
    "order"                         : "<integer>",
3995
    "-webkit-order"                 : "<integer>",
3996
    "orphans"                       : "<integer> | inherit",
3997
    "outline"                       : 1,
3998
    "outline-color"                 : "<color> | invert | inherit",
3999
    "outline-offset"                : 1,
4000
    "outline-style"                 : "<border-style> | inherit",
4001
    "outline-width"                 : "<border-width> | inherit",
4002
    "overflow"                      : "visible | hidden | scroll | auto | inherit",
4003
    "overflow-style"                : 1,
4004
    "overflow-wrap"                 : "normal | break-word",
4005
    "overflow-x"                    : 1,
4006
    "overflow-y"                    : 1,
4007
    "padding"                       : { multi: "<padding-width> | inherit", max: 4 },
4008
    "padding-bottom"                : "<padding-width> | inherit",
4009
    "padding-left"                  : "<padding-width> | inherit",
4010
    "padding-right"                 : "<padding-width> | inherit",
4011
    "padding-top"                   : "<padding-width> | inherit",
4012
    "page"                          : 1,
4013
    "page-break-after"              : "auto | always | avoid | left | right | inherit",
4014
    "page-break-before"             : "auto | always | avoid | left | right | inherit",
4015
    "page-break-inside"             : "auto | avoid | inherit",
4016
    "page-policy"                   : 1,
4017
    "pause"                         : 1,
4018
    "pause-after"                   : 1,
4019
    "pause-before"                  : 1,
4020
    "perspective"                   : 1,
4021
    "perspective-origin"            : 1,
4022
    "phonemes"                      : 1,
4023
    "pitch"                         : 1,
4024
    "pitch-range"                   : 1,
4025
    "play-during"                   : 1,
4026
    "pointer-events"                : "auto | none | visiblePainted | visibleFill | visibleStroke | visible | painted | fill | stroke | all | inherit",
4027
    "position"                      : "static | relative | absolute | fixed | inherit",
4028
    "presentation-level"            : 1,
4029
    "punctuation-trim"              : 1,
4030
    "quotes"                        : 1,
4031
    "rendering-intent"              : 1,
4032
    "resize"                        : 1,
4033
    "rest"                          : 1,
4034
    "rest-after"                    : 1,
4035
    "rest-before"                   : 1,
4036
    "richness"                      : 1,
4037
    "right"                         : "<margin-width> | inherit",
4038
    "rotation"                      : 1,
4039
    "rotation-point"                : 1,
4040
    "ruby-align"                    : 1,
4041
    "ruby-overhang"                 : 1,
4042
    "ruby-position"                 : 1,
4043
    "ruby-span"                     : 1,
4044
    "size"                          : 1,
4045
    "speak"                         : "normal | none | spell-out | inherit",
4046
    "speak-header"                  : "once | always | inherit",
4047
    "speak-numeral"                 : "digits | continuous | inherit",
4048
    "speak-punctuation"             : "code | none | inherit",
4049
    "speech-rate"                   : 1,
4050
    "src"                           : 1,
4051
    "stress"                        : 1,
4052
    "string-set"                    : 1,
4053

    
4054
    "table-layout"                  : "auto | fixed | inherit",
4055
    "tab-size"                      : "<integer> | <length>",
4056
    "target"                        : 1,
4057
    "target-name"                   : 1,
4058
    "target-new"                    : 1,
4059
    "target-position"               : 1,
4060
    "text-align"                    : "left | right | center | justify | inherit" ,
4061
    "text-align-last"               : 1,
4062
    "text-decoration"               : 1,
4063
    "text-emphasis"                 : 1,
4064
    "text-height"                   : 1,
4065
    "text-indent"                   : "<length> | <percentage> | inherit",
4066
    "text-justify"                  : "auto | none | inter-word | inter-ideograph | inter-cluster | distribute | kashida",
4067
    "text-outline"                  : 1,
4068
    "text-overflow"                 : 1,
4069
    "text-rendering"                : "auto | optimizeSpeed | optimizeLegibility | geometricPrecision | inherit",
4070
    "text-shadow"                   : 1,
4071
    "text-transform"                : "capitalize | uppercase | lowercase | none | inherit",
4072
    "text-wrap"                     : "normal | none | avoid",
4073
    "top"                           : "<margin-width> | inherit",
4074
    "-ms-touch-action"              : "auto | none | pan-x | pan-y",
4075
    "touch-action"                  : "auto | none | pan-x | pan-y",
4076
    "transform"                     : 1,
4077
    "transform-origin"              : 1,
4078
    "transform-style"               : 1,
4079
    "transition"                    : 1,
4080
    "transition-delay"              : 1,
4081
    "transition-duration"           : 1,
4082
    "transition-property"           : 1,
4083
    "transition-timing-function"    : 1,
4084
    "unicode-bidi"                  : "normal | embed | isolate | bidi-override | isolate-override | plaintext | inherit",
4085
    "user-modify"                   : "read-only | read-write | write-only | inherit",
4086
    "user-select"                   : "none | text | toggle | element | elements | all | inherit",
4087
    "user-zoom"                     : "zoom | fixed",
4088
    "vertical-align"                : "auto | use-script | baseline | sub | super | top | text-top | central | middle | bottom | text-bottom | <percentage> | <length>",
4089
    "visibility"                    : "visible | hidden | collapse | inherit",
4090
    "voice-balance"                 : 1,
4091
    "voice-duration"                : 1,
4092
    "voice-family"                  : 1,
4093
    "voice-pitch"                   : 1,
4094
    "voice-pitch-range"             : 1,
4095
    "voice-rate"                    : 1,
4096
    "voice-stress"                  : 1,
4097
    "voice-volume"                  : 1,
4098
    "volume"                        : 1,
4099
    "white-space"                   : "normal | pre | nowrap | pre-wrap | pre-line | inherit | -pre-wrap | -o-pre-wrap | -moz-pre-wrap | -hp-pre-wrap", //http://perishablepress.com/wrapping-content/
4100
    "white-space-collapse"          : 1,
4101
    "widows"                        : "<integer> | inherit",
4102
    "width"                         : "<length> | <percentage> | <content-sizing> | auto | inherit",
4103
    "word-break"                    : "normal | keep-all | break-all",
4104
    "word-spacing"                  : "<length> | normal | inherit",
4105
    "word-wrap"                     : "normal | break-word",
4106
    "writing-mode"                  : "horizontal-tb | vertical-rl | vertical-lr | lr-tb | rl-tb | tb-rl | bt-rl | tb-lr | bt-lr | lr-bt | rl-bt | lr | rl | tb | inherit",
4107
    "z-index"                       : "<integer> | auto | inherit",
4108
    "zoom"                          : "<number> | <percentage> | normal"
4109
};
4110
function PropertyName(text, hack, line, col){
4111

    
4112
    SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_NAME_TYPE);
4113
    this.hack = hack;
4114

    
4115
}
4116

    
4117
PropertyName.prototype = new SyntaxUnit();
4118
PropertyName.prototype.constructor = PropertyName;
4119
PropertyName.prototype.toString = function(){
4120
    return (this.hack ? this.hack : "") + this.text;
4121
};
4122
function PropertyValue(parts, line, col){
4123

    
4124
    SyntaxUnit.call(this, parts.join(" "), line, col, Parser.PROPERTY_VALUE_TYPE);
4125
    this.parts = parts;
4126

    
4127
}
4128

    
4129
PropertyValue.prototype = new SyntaxUnit();
4130
PropertyValue.prototype.constructor = PropertyValue;
4131
function PropertyValueIterator(value){
4132
    this._i = 0;
4133
    this._parts = value.parts;
4134
    this._marks = [];
4135
    this.value = value;
4136

    
4137
}
4138
PropertyValueIterator.prototype.count = function(){
4139
    return this._parts.length;
4140
};
4141
PropertyValueIterator.prototype.isFirst = function(){
4142
    return this._i === 0;
4143
};
4144
PropertyValueIterator.prototype.hasNext = function(){
4145
    return (this._i < this._parts.length);
4146
};
4147
PropertyValueIterator.prototype.mark = function(){
4148
    this._marks.push(this._i);
4149
};
4150
PropertyValueIterator.prototype.peek = function(count){
4151
    return this.hasNext() ? this._parts[this._i + (count || 0)] : null;
4152
};
4153
PropertyValueIterator.prototype.next = function(){
4154
    return this.hasNext() ? this._parts[this._i++] : null;
4155
};
4156
PropertyValueIterator.prototype.previous = function(){
4157
    return this._i > 0 ? this._parts[--this._i] : null;
4158
};
4159
PropertyValueIterator.prototype.restore = function(){
4160
    if (this._marks.length){
4161
        this._i = this._marks.pop();
4162
    }
4163
};
4164
function PropertyValuePart(text, line, col){
4165

    
4166
    SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_VALUE_PART_TYPE);
4167
    this.type = "unknown";
4168

    
4169
    var temp;
4170
    if (/^([+\-]?[\d\.]+)([a-z]+)$/i.test(text)){  //dimension
4171
        this.type = "dimension";
4172
        this.value = +RegExp.$1;
4173
        this.units = RegExp.$2;
4174
        switch(this.units.toLowerCase()){
4175

    
4176
            case "em":
4177
            case "rem":
4178
            case "ex":
4179
            case "px":
4180
            case "cm":
4181
            case "mm":
4182
            case "in":
4183
            case "pt":
4184
            case "pc":
4185
            case "ch":
4186
            case "vh":
4187
            case "vw":
4188
            case "fr":
4189
            case "vmax":
4190
            case "vmin":
4191
                this.type = "length";
4192
                break;
4193

    
4194
            case "deg":
4195
            case "rad":
4196
            case "grad":
4197
                this.type = "angle";
4198
                break;
4199

    
4200
            case "ms":
4201
            case "s":
4202
                this.type = "time";
4203
                break;
4204

    
4205
            case "hz":
4206
            case "khz":
4207
                this.type = "frequency";
4208
                break;
4209

    
4210
            case "dpi":
4211
            case "dpcm":
4212
                this.type = "resolution";
4213
                break;
4214

    
4215
        }
4216

    
4217
    } else if (/^([+\-]?[\d\.]+)%$/i.test(text)){  //percentage
4218
        this.type = "percentage";
4219
        this.value = +RegExp.$1;
4220
    } else if (/^([+\-]?\d+)$/i.test(text)){  //integer
4221
        this.type = "integer";
4222
        this.value = +RegExp.$1;
4223
    } else if (/^([+\-]?[\d\.]+)$/i.test(text)){  //number
4224
        this.type = "number";
4225
        this.value = +RegExp.$1;
4226

    
4227
    } else if (/^#([a-f0-9]{3,6})/i.test(text)){  //hexcolor
4228
        this.type = "color";
4229
        temp = RegExp.$1;
4230
        if (temp.length == 3){
4231
            this.red    = parseInt(temp.charAt(0)+temp.charAt(0),16);
4232
            this.green  = parseInt(temp.charAt(1)+temp.charAt(1),16);
4233
            this.blue   = parseInt(temp.charAt(2)+temp.charAt(2),16);
4234
        } else {
4235
            this.red    = parseInt(temp.substring(0,2),16);
4236
            this.green  = parseInt(temp.substring(2,4),16);
4237
            this.blue   = parseInt(temp.substring(4,6),16);
4238
        }
4239
    } else if (/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i.test(text)){ //rgb() color with absolute numbers
4240
        this.type   = "color";
4241
        this.red    = +RegExp.$1;
4242
        this.green  = +RegExp.$2;
4243
        this.blue   = +RegExp.$3;
4244
    } else if (/^rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)){ //rgb() color with percentages
4245
        this.type   = "color";
4246
        this.red    = +RegExp.$1 * 255 / 100;
4247
        this.green  = +RegExp.$2 * 255 / 100;
4248
        this.blue   = +RegExp.$3 * 255 / 100;
4249
    } else if (/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\s*\)/i.test(text)){ //rgba() color with absolute numbers
4250
        this.type   = "color";
4251
        this.red    = +RegExp.$1;
4252
        this.green  = +RegExp.$2;
4253
        this.blue   = +RegExp.$3;
4254
        this.alpha  = +RegExp.$4;
4255
    } else if (/^rgba\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)){ //rgba() color with percentages
4256
        this.type   = "color";
4257
        this.red    = +RegExp.$1 * 255 / 100;
4258
        this.green  = +RegExp.$2 * 255 / 100;
4259
        this.blue   = +RegExp.$3 * 255 / 100;
4260
        this.alpha  = +RegExp.$4;
4261
    } else if (/^hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)){ //hsl()
4262
        this.type   = "color";
4263
        this.hue    = +RegExp.$1;
4264
        this.saturation = +RegExp.$2 / 100;
4265
        this.lightness  = +RegExp.$3 / 100;
4266
    } else if (/^hsla\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)){ //hsla() color with percentages
4267
        this.type   = "color";
4268
        this.hue    = +RegExp.$1;
4269
        this.saturation = +RegExp.$2 / 100;
4270
        this.lightness  = +RegExp.$3 / 100;
4271
        this.alpha  = +RegExp.$4;
4272
    } else if (/^url\(["']?([^\)"']+)["']?\)/i.test(text)){ //URI
4273
        this.type   = "uri";
4274
        this.uri    = RegExp.$1;
4275
    } else if (/^([^\(]+)\(/i.test(text)){
4276
        this.type   = "function";
4277
        this.name   = RegExp.$1;
4278
        this.value  = text;
4279
    } else if (/^["'][^"']*["']/.test(text)){    //string
4280
        this.type   = "string";
4281
        this.value  = eval(text);
4282
    } else if (Colors[text.toLowerCase()]){  //named color
4283
        this.type   = "color";
4284
        temp        = Colors[text.toLowerCase()].substring(1);
4285
        this.red    = parseInt(temp.substring(0,2),16);
4286
        this.green  = parseInt(temp.substring(2,4),16);
4287
        this.blue   = parseInt(temp.substring(4,6),16);
4288
    } else if (/^[\,\/]$/.test(text)){
4289
        this.type   = "operator";
4290
        this.value  = text;
4291
    } else if (/^[a-z\-_\u0080-\uFFFF][a-z0-9\-_\u0080-\uFFFF]*$/i.test(text)){
4292
        this.type   = "identifier";
4293
        this.value  = text;
4294
    }
4295

    
4296
}
4297

    
4298
PropertyValuePart.prototype = new SyntaxUnit();
4299
PropertyValuePart.prototype.constructor = PropertyValuePart;
4300
PropertyValuePart.fromToken = function(token){
4301
    return new PropertyValuePart(token.value, token.startLine, token.startCol);
4302
};
4303
var Pseudos = {
4304
    ":first-letter": 1,
4305
    ":first-line":   1,
4306
    ":before":       1,
4307
    ":after":        1
4308
};
4309

    
4310
Pseudos.ELEMENT = 1;
4311
Pseudos.CLASS = 2;
4312

    
4313
Pseudos.isElement = function(pseudo){
4314
    return pseudo.indexOf("::") === 0 || Pseudos[pseudo.toLowerCase()] == Pseudos.ELEMENT;
4315
};
4316
function Selector(parts, line, col){
4317

    
4318
    SyntaxUnit.call(this, parts.join(" "), line, col, Parser.SELECTOR_TYPE);
4319
    this.parts = parts;
4320
    this.specificity = Specificity.calculate(this);
4321

    
4322
}
4323

    
4324
Selector.prototype = new SyntaxUnit();
4325
Selector.prototype.constructor = Selector;
4326
function SelectorPart(elementName, modifiers, text, line, col){
4327

    
4328
    SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_PART_TYPE);
4329
    this.elementName = elementName;
4330
    this.modifiers = modifiers;
4331

    
4332
}
4333

    
4334
SelectorPart.prototype = new SyntaxUnit();
4335
SelectorPart.prototype.constructor = SelectorPart;
4336
function SelectorSubPart(text, type, line, col){
4337

    
4338
    SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_SUB_PART_TYPE);
4339
    this.type = type;
4340
    this.args = [];
4341

    
4342
}
4343

    
4344
SelectorSubPart.prototype = new SyntaxUnit();
4345
SelectorSubPart.prototype.constructor = SelectorSubPart;
4346
function Specificity(a, b, c, d){
4347
    this.a = a;
4348
    this.b = b;
4349
    this.c = c;
4350
    this.d = d;
4351
}
4352

    
4353
Specificity.prototype = {
4354
    constructor: Specificity,
4355
    compare: function(other){
4356
        var comps = ["a", "b", "c", "d"],
4357
            i, len;
4358

    
4359
        for (i=0, len=comps.length; i < len; i++){
4360
            if (this[comps[i]] < other[comps[i]]){
4361
                return -1;
4362
            } else if (this[comps[i]] > other[comps[i]]){
4363
                return 1;
4364
            }
4365
        }
4366

    
4367
        return 0;
4368
    },
4369
    valueOf: function(){
4370
        return (this.a * 1000) + (this.b * 100) + (this.c * 10) + this.d;
4371
    },
4372
    toString: function(){
4373
        return this.a + "," + this.b + "," + this.c + "," + this.d;
4374
    }
4375

    
4376
};
4377
Specificity.calculate = function(selector){
4378

    
4379
    var i, len,
4380
        part,
4381
        b=0, c=0, d=0;
4382

    
4383
    function updateValues(part){
4384

    
4385
        var i, j, len, num,
4386
            elementName = part.elementName ? part.elementName.text : "",
4387
            modifier;
4388

    
4389
        if (elementName && elementName.charAt(elementName.length-1) != "*") {
4390
            d++;
4391
        }
4392

    
4393
        for (i=0, len=part.modifiers.length; i < len; i++){
4394
            modifier = part.modifiers[i];
4395
            switch(modifier.type){
4396
                case "class":
4397
                case "attribute":
4398
                    c++;
4399
                    break;
4400

    
4401
                case "id":
4402
                    b++;
4403
                    break;
4404

    
4405
                case "pseudo":
4406
                    if (Pseudos.isElement(modifier.text)){
4407
                        d++;
4408
                    } else {
4409
                        c++;
4410
                    }
4411
                    break;
4412

    
4413
                case "not":
4414
                    for (j=0, num=modifier.args.length; j < num; j++){
4415
                        updateValues(modifier.args[j]);
4416
                    }
4417
            }
4418
         }
4419
    }
4420

    
4421
    for (i=0, len=selector.parts.length; i < len; i++){
4422
        part = selector.parts[i];
4423

    
4424
        if (part instanceof SelectorPart){
4425
            updateValues(part);
4426
        }
4427
    }
4428

    
4429
    return new Specificity(0, b, c, d);
4430
};
4431

    
4432
var h = /^[0-9a-fA-F]$/,
4433
    nonascii = /^[\u0080-\uFFFF]$/,
4434
    nl = /\n|\r\n|\r|\f/;
4435

    
4436

    
4437
function isHexDigit(c){
4438
    return c !== null && h.test(c);
4439
}
4440

    
4441
function isDigit(c){
4442
    return c !== null && /\d/.test(c);
4443
}
4444

    
4445
function isWhitespace(c){
4446
    return c !== null && /\s/.test(c);
4447
}
4448

    
4449
function isNewLine(c){
4450
    return c !== null && nl.test(c);
4451
}
4452

    
4453
function isNameStart(c){
4454
    return c !== null && (/[a-z_\u0080-\uFFFF\\]/i.test(c));
4455
}
4456

    
4457
function isNameChar(c){
4458
    return c !== null && (isNameStart(c) || /[0-9\-\\]/.test(c));
4459
}
4460

    
4461
function isIdentStart(c){
4462
    return c !== null && (isNameStart(c) || /\-\\/.test(c));
4463
}
4464

    
4465
function mix(receiver, supplier){
4466
    for (var prop in supplier){
4467
        if (supplier.hasOwnProperty(prop)){
4468
            receiver[prop] = supplier[prop];
4469
        }
4470
    }
4471
    return receiver;
4472
}
4473
function TokenStream(input){
4474
    TokenStreamBase.call(this, input, Tokens);
4475
}
4476

    
4477
TokenStream.prototype = mix(new TokenStreamBase(), {
4478
    _getToken: function(channel){
4479

    
4480
        var c,
4481
            reader = this._reader,
4482
            token   = null,
4483
            startLine   = reader.getLine(),
4484
            startCol    = reader.getCol();
4485

    
4486
        c = reader.read();
4487

    
4488

    
4489
        while(c){
4490
            switch(c){
4491
                case "/":
4492

    
4493
                    if(reader.peek() == "*"){
4494
                        token = this.commentToken(c, startLine, startCol);
4495
                    } else {
4496
                        token = this.charToken(c, startLine, startCol);
4497
                    }
4498
                    break;
4499
                case "|":
4500
                case "~":
4501
                case "^":
4502
                case "$":
4503
                case "*":
4504
                    if(reader.peek() == "="){
4505
                        token = this.comparisonToken(c, startLine, startCol);
4506
                    } else {
4507
                        token = this.charToken(c, startLine, startCol);
4508
                    }
4509
                    break;
4510
                case "\"":
4511
                case "'":
4512
                    token = this.stringToken(c, startLine, startCol);
4513
                    break;
4514
                case "#":
4515
                    if (isNameChar(reader.peek())){
4516
                        token = this.hashToken(c, startLine, startCol);
4517
                    } else {
4518
                        token = this.charToken(c, startLine, startCol);
4519
                    }
4520
                    break;
4521
                case ".":
4522
                    if (isDigit(reader.peek())){
4523
                        token = this.numberToken(c, startLine, startCol);
4524
                    } else {
4525
                        token = this.charToken(c, startLine, startCol);
4526
                    }
4527
                    break;
4528
                case "-":
4529
                    if (reader.peek() == "-"){  //could be closing HTML-style comment
4530
                        token = this.htmlCommentEndToken(c, startLine, startCol);
4531
                    } else if (isNameStart(reader.peek())){
4532
                        token = this.identOrFunctionToken(c, startLine, startCol);
4533
                    } else {
4534
                        token = this.charToken(c, startLine, startCol);
4535
                    }
4536
                    break;
4537
                case "!":
4538
                    token = this.importantToken(c, startLine, startCol);
4539
                    break;
4540
                case "@":
4541
                    token = this.atRuleToken(c, startLine, startCol);
4542
                    break;
4543
                case ":":
4544
                    token = this.notToken(c, startLine, startCol);
4545
                    break;
4546
                case "<":
4547
                    token = this.htmlCommentStartToken(c, startLine, startCol);
4548
                    break;
4549
                case "U":
4550
                case "u":
4551
                    if (reader.peek() == "+"){
4552
                        token = this.unicodeRangeToken(c, startLine, startCol);
4553
                        break;
4554
                    }
4555
                default:
4556
                    if (isDigit(c)){
4557
                        token = this.numberToken(c, startLine, startCol);
4558
                    } else
4559
                    if (isWhitespace(c)){
4560
                        token = this.whitespaceToken(c, startLine, startCol);
4561
                    } else
4562
                    if (isIdentStart(c)){
4563
                        token = this.identOrFunctionToken(c, startLine, startCol);
4564
                    } else
4565
                    {
4566
                        token = this.charToken(c, startLine, startCol);
4567
                    }
4568

    
4569

    
4570

    
4571

    
4572

    
4573

    
4574
            }
4575
            break;
4576
        }
4577

    
4578
        if (!token && c === null){
4579
            token = this.createToken(Tokens.EOF,null,startLine,startCol);
4580
        }
4581

    
4582
        return token;
4583
    },
4584
    createToken: function(tt, value, startLine, startCol, options){
4585
        var reader = this._reader;
4586
        options = options || {};
4587

    
4588
        return {
4589
            value:      value,
4590
            type:       tt,
4591
            channel:    options.channel,
4592
            endChar:    options.endChar,
4593
            hide:       options.hide || false,
4594
            startLine:  startLine,
4595
            startCol:   startCol,
4596
            endLine:    reader.getLine(),
4597
            endCol:     reader.getCol()
4598
        };
4599
    },
4600
    atRuleToken: function(first, startLine, startCol){
4601
        var rule    = first,
4602
            reader  = this._reader,
4603
            tt      = Tokens.CHAR,
4604
            valid   = false,
4605
            ident,
4606
            c;
4607
        reader.mark();
4608
        ident = this.readName();
4609
        rule = first + ident;
4610
        tt = Tokens.type(rule.toLowerCase());
4611
        if (tt == Tokens.CHAR || tt == Tokens.UNKNOWN){
4612
            if (rule.length > 1){
4613
                tt = Tokens.UNKNOWN_SYM;
4614
            } else {
4615
                tt = Tokens.CHAR;
4616
                rule = first;
4617
                reader.reset();
4618
            }
4619
        }
4620

    
4621
        return this.createToken(tt, rule, startLine, startCol);
4622
    },
4623
    charToken: function(c, startLine, startCol){
4624
        var tt = Tokens.type(c);
4625
        var opts = {};
4626

    
4627
        if (tt == -1){
4628
            tt = Tokens.CHAR;
4629
        } else {
4630
            opts.endChar = Tokens[tt].endChar;
4631
        }
4632

    
4633
        return this.createToken(tt, c, startLine, startCol, opts);
4634
    },
4635
    commentToken: function(first, startLine, startCol){
4636
        var reader  = this._reader,
4637
            comment = this.readComment(first);
4638

    
4639
        return this.createToken(Tokens.COMMENT, comment, startLine, startCol);
4640
    },
4641
    comparisonToken: function(c, startLine, startCol){
4642
        var reader  = this._reader,
4643
            comparison  = c + reader.read(),
4644
            tt      = Tokens.type(comparison) || Tokens.CHAR;
4645

    
4646
        return this.createToken(tt, comparison, startLine, startCol);
4647
    },
4648
    hashToken: function(first, startLine, startCol){
4649
        var reader  = this._reader,
4650
            name    = this.readName(first);
4651

    
4652
        return this.createToken(Tokens.HASH, name, startLine, startCol);
4653
    },
4654
    htmlCommentStartToken: function(first, startLine, startCol){
4655
        var reader      = this._reader,
4656
            text        = first;
4657

    
4658
        reader.mark();
4659
        text += reader.readCount(3);
4660

    
4661
        if (text == "<!--"){
4662
            return this.createToken(Tokens.CDO, text, startLine, startCol);
4663
        } else {
4664
            reader.reset();
4665
            return this.charToken(first, startLine, startCol);
4666
        }
4667
    },
4668
    htmlCommentEndToken: function(first, startLine, startCol){
4669
        var reader      = this._reader,
4670
            text        = first;
4671

    
4672
        reader.mark();
4673
        text += reader.readCount(2);
4674

    
4675
        if (text == "-->"){
4676
            return this.createToken(Tokens.CDC, text, startLine, startCol);
4677
        } else {
4678
            reader.reset();
4679
            return this.charToken(first, startLine, startCol);
4680
        }
4681
    },
4682
    identOrFunctionToken: function(first, startLine, startCol){
4683
        var reader  = this._reader,
4684
            ident   = this.readName(first),
4685
            tt      = Tokens.IDENT;
4686
        if (reader.peek() == "("){
4687
            ident += reader.read();
4688
            if (ident.toLowerCase() == "url("){
4689
                tt = Tokens.URI;
4690
                ident = this.readURI(ident);
4691
                if (ident.toLowerCase() == "url("){
4692
                    tt = Tokens.FUNCTION;
4693
                }
4694
            } else {
4695
                tt = Tokens.FUNCTION;
4696
            }
4697
        } else if (reader.peek() == ":"){  //might be an IE function
4698
            if (ident.toLowerCase() == "progid"){
4699
                ident += reader.readTo("(");
4700
                tt = Tokens.IE_FUNCTION;
4701
            }
4702
        }
4703

    
4704
        return this.createToken(tt, ident, startLine, startCol);
4705
    },
4706
    importantToken: function(first, startLine, startCol){
4707
        var reader      = this._reader,
4708
            important   = first,
4709
            tt          = Tokens.CHAR,
4710
            temp,
4711
            c;
4712

    
4713
        reader.mark();
4714
        c = reader.read();
4715

    
4716
        while(c){
4717
            if (c == "/"){
4718
                if (reader.peek() != "*"){
4719
                    break;
4720
                } else {
4721
                    temp = this.readComment(c);
4722
                    if (temp === ""){    //broken!
4723
                        break;
4724
                    }
4725
                }
4726
            } else if (isWhitespace(c)){
4727
                important += c + this.readWhitespace();
4728
            } else if (/i/i.test(c)){
4729
                temp = reader.readCount(8);
4730
                if (/mportant/i.test(temp)){
4731
                    important += c + temp;
4732
                    tt = Tokens.IMPORTANT_SYM;
4733

    
4734
                }
4735
                break;  //we're done
4736
            } else {
4737
                break;
4738
            }
4739

    
4740
            c = reader.read();
4741
        }
4742

    
4743
        if (tt == Tokens.CHAR){
4744
            reader.reset();
4745
            return this.charToken(first, startLine, startCol);
4746
        } else {
4747
            return this.createToken(tt, important, startLine, startCol);
4748
        }
4749

    
4750

    
4751
    },
4752
    notToken: function(first, startLine, startCol){
4753
        var reader      = this._reader,
4754
            text        = first;
4755

    
4756
        reader.mark();
4757
        text += reader.readCount(4);
4758

    
4759
        if (text.toLowerCase() == ":not("){
4760
            return this.createToken(Tokens.NOT, text, startLine, startCol);
4761
        } else {
4762
            reader.reset();
4763
            return this.charToken(first, startLine, startCol);
4764
        }
4765
    },
4766
    numberToken: function(first, startLine, startCol){
4767
        var reader  = this._reader,
4768
            value   = this.readNumber(first),
4769
            ident,
4770
            tt      = Tokens.NUMBER,
4771
            c       = reader.peek();
4772

    
4773
        if (isIdentStart(c)){
4774
            ident = this.readName(reader.read());
4775
            value += ident;
4776

    
4777
            if (/^em$|^ex$|^px$|^gd$|^rem$|^vw$|^vh$|^fr$|^vmax$|^vmin$|^ch$|^cm$|^mm$|^in$|^pt$|^pc$/i.test(ident)){
4778
                tt = Tokens.LENGTH;
4779
            } else if (/^deg|^rad$|^grad$/i.test(ident)){
4780
                tt = Tokens.ANGLE;
4781
            } else if (/^ms$|^s$/i.test(ident)){
4782
                tt = Tokens.TIME;
4783
            } else if (/^hz$|^khz$/i.test(ident)){
4784
                tt = Tokens.FREQ;
4785
            } else if (/^dpi$|^dpcm$/i.test(ident)){
4786
                tt = Tokens.RESOLUTION;
4787
            } else {
4788
                tt = Tokens.DIMENSION;
4789
            }
4790

    
4791
        } else if (c == "%"){
4792
            value += reader.read();
4793
            tt = Tokens.PERCENTAGE;
4794
        }
4795

    
4796
        return this.createToken(tt, value, startLine, startCol);
4797
    },
4798
    stringToken: function(first, startLine, startCol){
4799
        var delim   = first,
4800
            string  = first,
4801
            reader  = this._reader,
4802
            prev    = first,
4803
            tt      = Tokens.STRING,
4804
            c       = reader.read();
4805

    
4806
        while(c){
4807
            string += c;
4808
            if (c == delim && prev != "\\"){
4809
                break;
4810
            }
4811
            if (isNewLine(reader.peek()) && c != "\\"){
4812
                tt = Tokens.INVALID;
4813
                break;
4814
            }
4815
            prev = c;
4816
            c = reader.read();
4817
        }
4818
        if (c === null){
4819
            tt = Tokens.INVALID;
4820
        }
4821

    
4822
        return this.createToken(tt, string, startLine, startCol);
4823
    },
4824

    
4825
    unicodeRangeToken: function(first, startLine, startCol){
4826
        var reader  = this._reader,
4827
            value   = first,
4828
            temp,
4829
            tt      = Tokens.CHAR;
4830
        if (reader.peek() == "+"){
4831
            reader.mark();
4832
            value += reader.read();
4833
            value += this.readUnicodeRangePart(true);
4834
            if (value.length == 2){
4835
                reader.reset();
4836
            } else {
4837

    
4838
                tt = Tokens.UNICODE_RANGE;
4839
                if (value.indexOf("?") == -1){
4840

    
4841
                    if (reader.peek() == "-"){
4842
                        reader.mark();
4843
                        temp = reader.read();
4844
                        temp += this.readUnicodeRangePart(false);
4845
                        if (temp.length == 1){
4846
                            reader.reset();
4847
                        } else {
4848
                            value += temp;
4849
                        }
4850
                    }
4851

    
4852
                }
4853
            }
4854
        }
4855

    
4856
        return this.createToken(tt, value, startLine, startCol);
4857
    },
4858
    whitespaceToken: function(first, startLine, startCol){
4859
        var reader  = this._reader,
4860
            value   = first + this.readWhitespace();
4861
        return this.createToken(Tokens.S, value, startLine, startCol);
4862
    },
4863

    
4864
    readUnicodeRangePart: function(allowQuestionMark){
4865
        var reader  = this._reader,
4866
            part = "",
4867
            c       = reader.peek();
4868
        while(isHexDigit(c) && part.length < 6){
4869
            reader.read();
4870
            part += c;
4871
            c = reader.peek();
4872
        }
4873
        if (allowQuestionMark){
4874
            while(c == "?" && part.length < 6){
4875
                reader.read();
4876
                part += c;
4877
                c = reader.peek();
4878
            }
4879
        }
4880

    
4881
        return part;
4882
    },
4883

    
4884
    readWhitespace: function(){
4885
        var reader  = this._reader,
4886
            whitespace = "",
4887
            c       = reader.peek();
4888

    
4889
        while(isWhitespace(c)){
4890
            reader.read();
4891
            whitespace += c;
4892
            c = reader.peek();
4893
        }
4894

    
4895
        return whitespace;
4896
    },
4897
    readNumber: function(first){
4898
        var reader  = this._reader,
4899
            number  = first,
4900
            hasDot  = (first == "."),
4901
            c       = reader.peek();
4902

    
4903

    
4904
        while(c){
4905
            if (isDigit(c)){
4906
                number += reader.read();
4907
            } else if (c == "."){
4908
                if (hasDot){
4909
                    break;
4910
                } else {
4911
                    hasDot = true;
4912
                    number += reader.read();
4913
                }
4914
            } else {
4915
                break;
4916
            }
4917

    
4918
            c = reader.peek();
4919
        }
4920

    
4921
        return number;
4922
    },
4923
    readString: function(){
4924
        var reader  = this._reader,
4925
            delim   = reader.read(),
4926
            string  = delim,
4927
            prev    = delim,
4928
            c       = reader.peek();
4929

    
4930
        while(c){
4931
            c = reader.read();
4932
            string += c;
4933
            if (c == delim && prev != "\\"){
4934
                break;
4935
            }
4936
            if (isNewLine(reader.peek()) && c != "\\"){
4937
                string = "";
4938
                break;
4939
            }
4940
            prev = c;
4941
            c = reader.peek();
4942
        }
4943
        if (c === null){
4944
            string = "";
4945
        }
4946

    
4947
        return string;
4948
    },
4949
    readURI: function(first){
4950
        var reader  = this._reader,
4951
            uri     = first,
4952
            inner   = "",
4953
            c       = reader.peek();
4954

    
4955
        reader.mark();
4956
        while(c && isWhitespace(c)){
4957
            reader.read();
4958
            c = reader.peek();
4959
        }
4960
        if (c == "'" || c == "\""){
4961
            inner = this.readString();
4962
        } else {
4963
            inner = this.readURL();
4964
        }
4965

    
4966
        c = reader.peek();
4967
        while(c && isWhitespace(c)){
4968
            reader.read();
4969
            c = reader.peek();
4970
        }
4971
        if (inner === "" || c != ")"){
4972
            uri = first;
4973
            reader.reset();
4974
        } else {
4975
            uri += inner + reader.read();
4976
        }
4977

    
4978
        return uri;
4979
    },
4980
    readURL: function(){
4981
        var reader  = this._reader,
4982
            url     = "",
4983
            c       = reader.peek();
4984
        while (/^[!#$%&\\*-~]$/.test(c)){
4985
            url += reader.read();
4986
            c = reader.peek();
4987
        }
4988

    
4989
        return url;
4990

    
4991
    },
4992
    readName: function(first){
4993
        var reader  = this._reader,
4994
            ident   = first || "",
4995
            c       = reader.peek();
4996

    
4997
        while(true){
4998
            if (c == "\\"){
4999
                ident += this.readEscape(reader.read());
5000
                c = reader.peek();
5001
            } else if(c && isNameChar(c)){
5002
                ident += reader.read();
5003
                c = reader.peek();
5004
            } else {
5005
                break;
5006
            }
5007
        }
5008

    
5009
        return ident;
5010
    },
5011

    
5012
    readEscape: function(first){
5013
        var reader  = this._reader,
5014
            cssEscape = first || "",
5015
            i       = 0,
5016
            c       = reader.peek();
5017

    
5018
        if (isHexDigit(c)){
5019
            do {
5020
                cssEscape += reader.read();
5021
                c = reader.peek();
5022
            } while(c && isHexDigit(c) && ++i < 6);
5023
        }
5024

    
5025
        if (cssEscape.length == 3 && /\s/.test(c) ||
5026
            cssEscape.length == 7 || cssEscape.length == 1){
5027
                reader.read();
5028
        } else {
5029
            c = "";
5030
        }
5031

    
5032
        return cssEscape + c;
5033
    },
5034

    
5035
    readComment: function(first){
5036
        var reader  = this._reader,
5037
            comment = first || "",
5038
            c       = reader.read();
5039

    
5040
        if (c == "*"){
5041
            while(c){
5042
                comment += c;
5043
                if (comment.length > 2 && c == "*" && reader.peek() == "/"){
5044
                    comment += reader.read();
5045
                    break;
5046
                }
5047

    
5048
                c = reader.read();
5049
            }
5050

    
5051
            return comment;
5052
        } else {
5053
            return "";
5054
        }
5055

    
5056
    }
5057
});
5058

    
5059
var Tokens  = [
5060
    { name: "CDO"},
5061
    { name: "CDC"},
5062
    { name: "S", whitespace: true/*, channel: "ws"*/},
5063
    { name: "COMMENT", comment: true, hide: true, channel: "comment" },
5064
    { name: "INCLUDES", text: "~="},
5065
    { name: "DASHMATCH", text: "|="},
5066
    { name: "PREFIXMATCH", text: "^="},
5067
    { name: "SUFFIXMATCH", text: "$="},
5068
    { name: "SUBSTRINGMATCH", text: "*="},
5069
    { name: "STRING"},
5070
    { name: "IDENT"},
5071
    { name: "HASH"},
5072
    { name: "IMPORT_SYM", text: "@import"},
5073
    { name: "PAGE_SYM", text: "@page"},
5074
    { name: "MEDIA_SYM", text: "@media"},
5075
    { name: "FONT_FACE_SYM", text: "@font-face"},
5076
    { name: "CHARSET_SYM", text: "@charset"},
5077
    { name: "NAMESPACE_SYM", text: "@namespace"},
5078
    { name: "VIEWPORT_SYM", text: ["@viewport", "@-ms-viewport"]},
5079
    { name: "UNKNOWN_SYM" },
5080
    { name: "KEYFRAMES_SYM", text: [ "@keyframes", "@-webkit-keyframes", "@-moz-keyframes", "@-o-keyframes" ] },
5081
    { name: "IMPORTANT_SYM"},
5082
    { name: "LENGTH"},
5083
    { name: "ANGLE"},
5084
    { name: "TIME"},
5085
    { name: "FREQ"},
5086
    { name: "DIMENSION"},
5087
    { name: "PERCENTAGE"},
5088
    { name: "NUMBER"},
5089
    { name: "URI"},
5090
    { name: "FUNCTION"},
5091
    { name: "UNICODE_RANGE"},
5092
    { name: "INVALID"},
5093
    { name: "PLUS", text: "+" },
5094
    { name: "GREATER", text: ">"},
5095
    { name: "COMMA", text: ","},
5096
    { name: "TILDE", text: "~"},
5097
    { name: "NOT"},
5098
    { name: "TOPLEFTCORNER_SYM", text: "@top-left-corner"},
5099
    { name: "TOPLEFT_SYM", text: "@top-left"},
5100
    { name: "TOPCENTER_SYM", text: "@top-center"},
5101
    { name: "TOPRIGHT_SYM", text: "@top-right"},
5102
    { name: "TOPRIGHTCORNER_SYM", text: "@top-right-corner"},
5103
    { name: "BOTTOMLEFTCORNER_SYM", text: "@bottom-left-corner"},
5104
    { name: "BOTTOMLEFT_SYM", text: "@bottom-left"},
5105
    { name: "BOTTOMCENTER_SYM", text: "@bottom-center"},
5106
    { name: "BOTTOMRIGHT_SYM", text: "@bottom-right"},
5107
    { name: "BOTTOMRIGHTCORNER_SYM", text: "@bottom-right-corner"},
5108
    { name: "LEFTTOP_SYM", text: "@left-top"},
5109
    { name: "LEFTMIDDLE_SYM", text: "@left-middle"},
5110
    { name: "LEFTBOTTOM_SYM", text: "@left-bottom"},
5111
    { name: "RIGHTTOP_SYM", text: "@right-top"},
5112
    { name: "RIGHTMIDDLE_SYM", text: "@right-middle"},
5113
    { name: "RIGHTBOTTOM_SYM", text: "@right-bottom"},
5114
    { name: "RESOLUTION", state: "media"},
5115
    { name: "IE_FUNCTION" },
5116
    { name: "CHAR" },
5117
    {
5118
        name: "PIPE",
5119
        text: "|"
5120
    },
5121
    {
5122
        name: "SLASH",
5123
        text: "/"
5124
    },
5125
    {
5126
        name: "MINUS",
5127
        text: "-"
5128
    },
5129
    {
5130
        name: "STAR",
5131
        text: "*"
5132
    },
5133

    
5134
    {
5135
        name: "LBRACE",
5136
        endChar: "}",
5137
        text: "{"
5138
    },
5139
    {
5140
        name: "RBRACE",
5141
        text: "}"
5142
    },
5143
    {
5144
        name: "LBRACKET",
5145
        endChar: "]",
5146
        text: "["
5147
    },
5148
    {
5149
        name: "RBRACKET",
5150
        text: "]"
5151
    },
5152
    {
5153
        name: "EQUALS",
5154
        text: "="
5155
    },
5156
    {
5157
        name: "COLON",
5158
        text: ":"
5159
    },
5160
    {
5161
        name: "SEMICOLON",
5162
        text: ";"
5163
    },
5164

    
5165
    {
5166
        name: "LPAREN",
5167
        endChar: ")",
5168
        text: "("
5169
    },
5170
    {
5171
        name: "RPAREN",
5172
        text: ")"
5173
    },
5174
    {
5175
        name: "DOT",
5176
        text: "."
5177
    }
5178
];
5179

    
5180
(function(){
5181

    
5182
    var nameMap = [],
5183
        typeMap = {};
5184

    
5185
    Tokens.UNKNOWN = -1;
5186
    Tokens.unshift({name:"EOF"});
5187
    for (var i=0, len = Tokens.length; i < len; i++){
5188
        nameMap.push(Tokens[i].name);
5189
        Tokens[Tokens[i].name] = i;
5190
        if (Tokens[i].text){
5191
            if (Tokens[i].text instanceof Array){
5192
                for (var j=0; j < Tokens[i].text.length; j++){
5193
                    typeMap[Tokens[i].text[j]] = i;
5194
                }
5195
            } else {
5196
                typeMap[Tokens[i].text] = i;
5197
            }
5198
        }
5199
    }
5200

    
5201
    Tokens.name = function(tt){
5202
        return nameMap[tt];
5203
    };
5204

    
5205
    Tokens.type = function(c){
5206
        return typeMap[c] || -1;
5207
    };
5208

    
5209
})();
5210
var Validation = {
5211

    
5212
    validate: function(property, value){
5213
        var name        = property.toString().toLowerCase(),
5214
            parts       = value.parts,
5215
            expression  = new PropertyValueIterator(value),
5216
            spec        = Properties[name],
5217
            part,
5218
            valid,
5219
            j, count,
5220
            msg,
5221
            types,
5222
            last,
5223
            literals,
5224
            max, multi, group;
5225

    
5226
        if (!spec) {
5227
            if (name.indexOf("-") !== 0){    //vendor prefixed are ok
5228
                throw new ValidationError("Unknown property '" + property + "'.", property.line, property.col);
5229
            }
5230
        } else if (typeof spec != "number"){
5231
            if (typeof spec == "string"){
5232
                if (spec.indexOf("||") > -1) {
5233
                    this.groupProperty(spec, expression);
5234
                } else {
5235
                    this.singleProperty(spec, expression, 1);
5236
                }
5237

    
5238
            } else if (spec.multi) {
5239
                this.multiProperty(spec.multi, expression, spec.comma, spec.max || Infinity);
5240
            } else if (typeof spec == "function") {
5241
                spec(expression);
5242
            }
5243

    
5244
        }
5245

    
5246
    },
5247

    
5248
    singleProperty: function(types, expression, max, partial) {
5249

    
5250
        var result      = false,
5251
            value       = expression.value,
5252
            count       = 0,
5253
            part;
5254

    
5255
        while (expression.hasNext() && count < max) {
5256
            result = ValidationTypes.isAny(expression, types);
5257
            if (!result) {
5258
                break;
5259
            }
5260
            count++;
5261
        }
5262

    
5263
        if (!result) {
5264
            if (expression.hasNext() && !expression.isFirst()) {
5265
                part = expression.peek();
5266
                throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5267
            } else {
5268
                 throw new ValidationError("Expected (" + types + ") but found '" + value + "'.", value.line, value.col);
5269
            }
5270
        } else if (expression.hasNext()) {
5271
            part = expression.next();
5272
            throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5273
        }
5274

    
5275
    },
5276

    
5277
    multiProperty: function (types, expression, comma, max) {
5278

    
5279
        var result      = false,
5280
            value       = expression.value,
5281
            count       = 0,
5282
            sep         = false,
5283
            part;
5284

    
5285
        while(expression.hasNext() && !result && count < max) {
5286
            if (ValidationTypes.isAny(expression, types)) {
5287
                count++;
5288
                if (!expression.hasNext()) {
5289
                    result = true;
5290

    
5291
                } else if (comma) {
5292
                    if (expression.peek() == ",") {
5293
                        part = expression.next();
5294
                    } else {
5295
                        break;
5296
                    }
5297
                }
5298
            } else {
5299
                break;
5300

    
5301
            }
5302
        }
5303

    
5304
        if (!result) {
5305
            if (expression.hasNext() && !expression.isFirst()) {
5306
                part = expression.peek();
5307
                throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5308
            } else {
5309
                part = expression.previous();
5310
                if (comma && part == ",") {
5311
                    throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5312
                } else {
5313
                    throw new ValidationError("Expected (" + types + ") but found '" + value + "'.", value.line, value.col);
5314
                }
5315
            }
5316

    
5317
        } else if (expression.hasNext()) {
5318
            part = expression.next();
5319
            throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5320
        }
5321

    
5322
    },
5323

    
5324
    groupProperty: function (types, expression, comma) {
5325

    
5326
        var result      = false,
5327
            value       = expression.value,
5328
            typeCount   = types.split("||").length,
5329
            groups      = { count: 0 },
5330
            partial     = false,
5331
            name,
5332
            part;
5333

    
5334
        while(expression.hasNext() && !result) {
5335
            name = ValidationTypes.isAnyOfGroup(expression, types);
5336
            if (name) {
5337
                if (groups[name]) {
5338
                    break;
5339
                } else {
5340
                    groups[name] = 1;
5341
                    groups.count++;
5342
                    partial = true;
5343

    
5344
                    if (groups.count == typeCount || !expression.hasNext()) {
5345
                        result = true;
5346
                    }
5347
                }
5348
            } else {
5349
                break;
5350
            }
5351
        }
5352

    
5353
        if (!result) {
5354
            if (partial && expression.hasNext()) {
5355
                    part = expression.peek();
5356
                    throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5357
            } else {
5358
                throw new ValidationError("Expected (" + types + ") but found '" + value + "'.", value.line, value.col);
5359
            }
5360
        } else if (expression.hasNext()) {
5361
            part = expression.next();
5362
            throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5363
        }
5364
    }
5365

    
5366

    
5367

    
5368
};
5369
function ValidationError(message, line, col){
5370
    this.col = col;
5371
    this.line = line;
5372
    this.message = message;
5373

    
5374
}
5375
ValidationError.prototype = new Error();
5376
var ValidationTypes = {
5377

    
5378
    isLiteral: function (part, literals) {
5379
        var text = part.text.toString().toLowerCase(),
5380
            args = literals.split(" | "),
5381
            i, len, found = false;
5382

    
5383
        for (i=0,len=args.length; i < len && !found; i++){
5384
            if (text == args[i].toLowerCase()){
5385
                found = true;
5386
            }
5387
        }
5388

    
5389
        return found;
5390
    },
5391

    
5392
    isSimple: function(type) {
5393
        return !!this.simple[type];
5394
    },
5395

    
5396
    isComplex: function(type) {
5397
        return !!this.complex[type];
5398
    },
5399
    isAny: function (expression, types) {
5400
        var args = types.split(" | "),
5401
            i, len, found = false;
5402

    
5403
        for (i=0,len=args.length; i < len && !found && expression.hasNext(); i++){
5404
            found = this.isType(expression, args[i]);
5405
        }
5406

    
5407
        return found;
5408
    },
5409
    isAnyOfGroup: function(expression, types) {
5410
        var args = types.split(" || "),
5411
            i, len, found = false;
5412

    
5413
        for (i=0,len=args.length; i < len && !found; i++){
5414
            found = this.isType(expression, args[i]);
5415
        }
5416

    
5417
        return found ? args[i-1] : false;
5418
    },
5419
    isType: function (expression, type) {
5420
        var part = expression.peek(),
5421
            result = false;
5422

    
5423
        if (type.charAt(0) != "<") {
5424
            result = this.isLiteral(part, type);
5425
            if (result) {
5426
                expression.next();
5427
            }
5428
        } else if (this.simple[type]) {
5429
            result = this.simple[type](part);
5430
            if (result) {
5431
                expression.next();
5432
            }
5433
        } else {
5434
            result = this.complex[type](expression);
5435
        }
5436

    
5437
        return result;
5438
    },
5439

    
5440

    
5441

    
5442
    simple: {
5443

    
5444
        "<absolute-size>": function(part){
5445
            return ValidationTypes.isLiteral(part, "xx-small | x-small | small | medium | large | x-large | xx-large");
5446
        },
5447

    
5448
        "<attachment>": function(part){
5449
            return ValidationTypes.isLiteral(part, "scroll | fixed | local");
5450
        },
5451

    
5452
        "<attr>": function(part){
5453
            return part.type == "function" && part.name == "attr";
5454
        },
5455

    
5456
        "<bg-image>": function(part){
5457
            return this["<image>"](part) || this["<gradient>"](part) ||  part == "none";
5458
        },
5459

    
5460
        "<gradient>": function(part) {
5461
            return part.type == "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?(?:repeating\-)?(?:radial\-|linear\-)?gradient/i.test(part);
5462
        },
5463

    
5464
        "<box>": function(part){
5465
            return ValidationTypes.isLiteral(part, "padding-box | border-box | content-box");
5466
        },
5467

    
5468
        "<content>": function(part){
5469
            return part.type == "function" && part.name == "content";
5470
        },
5471

    
5472
        "<relative-size>": function(part){
5473
            return ValidationTypes.isLiteral(part, "smaller | larger");
5474
        },
5475
        "<ident>": function(part){
5476
            return part.type == "identifier";
5477
        },
5478

    
5479
        "<length>": function(part){
5480
            if (part.type == "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?calc/i.test(part)){
5481
                return true;
5482
            }else{
5483
                return part.type == "length" || part.type == "number" || part.type == "integer" || part == "0";
5484
            }
5485
        },
5486

    
5487
        "<color>": function(part){
5488
            return part.type == "color" || part == "transparent";
5489
        },
5490

    
5491
        "<number>": function(part){
5492
            return part.type == "number" || this["<integer>"](part);
5493
        },
5494

    
5495
        "<integer>": function(part){
5496
            return part.type == "integer";
5497
        },
5498

    
5499
        "<line>": function(part){
5500
            return part.type == "integer";
5501
        },
5502

    
5503
        "<angle>": function(part){
5504
            return part.type == "angle";
5505
        },
5506

    
5507
        "<uri>": function(part){
5508
            return part.type == "uri";
5509
        },
5510

    
5511
        "<image>": function(part){
5512
            return this["<uri>"](part);
5513
        },
5514

    
5515
        "<percentage>": function(part){
5516
            return part.type == "percentage" || part == "0";
5517
        },
5518

    
5519
        "<border-width>": function(part){
5520
            return this["<length>"](part) || ValidationTypes.isLiteral(part, "thin | medium | thick");
5521
        },
5522

    
5523
        "<border-style>": function(part){
5524
            return ValidationTypes.isLiteral(part, "none | hidden | dotted | dashed | solid | double | groove | ridge | inset | outset");
5525
        },
5526

    
5527
        "<content-sizing>": function(part){ // http://www.w3.org/TR/css3-sizing/#width-height-keywords
5528
            return ValidationTypes.isLiteral(part, "fill-available | -moz-available | -webkit-fill-available | max-content | -moz-max-content | -webkit-max-content | min-content | -moz-min-content | -webkit-min-content | fit-content | -moz-fit-content | -webkit-fit-content");
5529
        },
5530

    
5531
        "<margin-width>": function(part){
5532
            return this["<length>"](part) || this["<percentage>"](part) || ValidationTypes.isLiteral(part, "auto");
5533
        },
5534

    
5535
        "<padding-width>": function(part){
5536
            return this["<length>"](part) || this["<percentage>"](part);
5537
        },
5538

    
5539
        "<shape>": function(part){
5540
            return part.type == "function" && (part.name == "rect" || part.name == "inset-rect");
5541
        },
5542

    
5543
        "<time>": function(part) {
5544
            return part.type == "time";
5545
        },
5546

    
5547
        "<flex-grow>": function(part){
5548
            return this["<number>"](part);
5549
        },
5550

    
5551
        "<flex-shrink>": function(part){
5552
            return this["<number>"](part);
5553
        },
5554

    
5555
        "<width>": function(part){
5556
            return this["<margin-width>"](part);
5557
        },
5558

    
5559
        "<flex-basis>": function(part){
5560
            return this["<width>"](part);
5561
        },
5562

    
5563
        "<flex-direction>": function(part){
5564
            return ValidationTypes.isLiteral(part, "row | row-reverse | column | column-reverse");
5565
        },
5566

    
5567
        "<flex-wrap>": function(part){
5568
            return ValidationTypes.isLiteral(part, "nowrap | wrap | wrap-reverse");
5569
        }
5570
    },
5571

    
5572
    complex: {
5573

    
5574
        "<bg-position>": function(expression){
5575
            var types   = this,
5576
                result  = false,
5577
                numeric = "<percentage> | <length>",
5578
                xDir    = "left | right",
5579
                yDir    = "top | bottom",
5580
                count = 0,
5581
                hasNext = function() {
5582
                    return expression.hasNext() && expression.peek() != ",";
5583
                };
5584

    
5585
            while (expression.peek(count) && expression.peek(count) != ",") {
5586
                count++;
5587
            }
5588

    
5589
            if (count < 3) {
5590
                if (ValidationTypes.isAny(expression, xDir + " | center | " + numeric)) {
5591
                        result = true;
5592
                        ValidationTypes.isAny(expression, yDir + " | center | " + numeric);
5593
                } else if (ValidationTypes.isAny(expression, yDir)) {
5594
                        result = true;
5595
                        ValidationTypes.isAny(expression, xDir + " | center");
5596
                }
5597
            } else {
5598
                if (ValidationTypes.isAny(expression, xDir)) {
5599
                    if (ValidationTypes.isAny(expression, yDir)) {
5600
                        result = true;
5601
                        ValidationTypes.isAny(expression, numeric);
5602
                    } else if (ValidationTypes.isAny(expression, numeric)) {
5603
                        if (ValidationTypes.isAny(expression, yDir)) {
5604
                            result = true;
5605
                            ValidationTypes.isAny(expression, numeric);
5606
                        } else if (ValidationTypes.isAny(expression, "center")) {
5607
                            result = true;
5608
                        }
5609
                    }
5610
                } else if (ValidationTypes.isAny(expression, yDir)) {
5611
                    if (ValidationTypes.isAny(expression, xDir)) {
5612
                        result = true;
5613
                        ValidationTypes.isAny(expression, numeric);
5614
                    } else if (ValidationTypes.isAny(expression, numeric)) {
5615
                        if (ValidationTypes.isAny(expression, xDir)) {
5616
                                result = true;
5617
                                ValidationTypes.isAny(expression, numeric);
5618
                        } else if (ValidationTypes.isAny(expression, "center")) {
5619
                            result = true;
5620
                        }
5621
                    }
5622
                } else if (ValidationTypes.isAny(expression, "center")) {
5623
                    if (ValidationTypes.isAny(expression, xDir + " | " + yDir)) {
5624
                        result = true;
5625
                        ValidationTypes.isAny(expression, numeric);
5626
                    }
5627
                }
5628
            }
5629

    
5630
            return result;
5631
        },
5632

    
5633
        "<bg-size>": function(expression){
5634
            var types   = this,
5635
                result  = false,
5636
                numeric = "<percentage> | <length> | auto",
5637
                part,
5638
                i, len;
5639

    
5640
            if (ValidationTypes.isAny(expression, "cover | contain")) {
5641
                result = true;
5642
            } else if (ValidationTypes.isAny(expression, numeric)) {
5643
                result = true;
5644
                ValidationTypes.isAny(expression, numeric);
5645
            }
5646

    
5647
            return result;
5648
        },
5649

    
5650
        "<repeat-style>": function(expression){
5651
            var result  = false,
5652
                values  = "repeat | space | round | no-repeat",
5653
                part;
5654

    
5655
            if (expression.hasNext()){
5656
                part = expression.next();
5657

    
5658
                if (ValidationTypes.isLiteral(part, "repeat-x | repeat-y")) {
5659
                    result = true;
5660
                } else if (ValidationTypes.isLiteral(part, values)) {
5661
                    result = true;
5662

    
5663
                    if (expression.hasNext() && ValidationTypes.isLiteral(expression.peek(), values)) {
5664
                        expression.next();
5665
                    }
5666
                }
5667
            }
5668

    
5669
            return result;
5670

    
5671
        },
5672

    
5673
        "<shadow>": function(expression) {
5674
            var result  = false,
5675
                count   = 0,
5676
                inset   = false,
5677
                color   = false,
5678
                part;
5679

    
5680
            if (expression.hasNext()) {
5681

    
5682
                if (ValidationTypes.isAny(expression, "inset")){
5683
                    inset = true;
5684
                }
5685

    
5686
                if (ValidationTypes.isAny(expression, "<color>")) {
5687
                    color = true;
5688
                }
5689

    
5690
                while (ValidationTypes.isAny(expression, "<length>") && count < 4) {
5691
                    count++;
5692
                }
5693

    
5694

    
5695
                if (expression.hasNext()) {
5696
                    if (!color) {
5697
                        ValidationTypes.isAny(expression, "<color>");
5698
                    }
5699

    
5700
                    if (!inset) {
5701
                        ValidationTypes.isAny(expression, "inset");
5702
                    }
5703

    
5704
                }
5705

    
5706
                result = (count >= 2 && count <= 4);
5707

    
5708
            }
5709

    
5710
            return result;
5711
        },
5712

    
5713
        "<x-one-radius>": function(expression) {
5714
            var result  = false,
5715
                simple = "<length> | <percentage> | inherit";
5716

    
5717
            if (ValidationTypes.isAny(expression, simple)){
5718
                result = true;
5719
                ValidationTypes.isAny(expression, simple);
5720
            }
5721

    
5722
            return result;
5723
        },
5724

    
5725
        "<flex>": function(expression) {
5726
            var part,
5727
                result = false;
5728
            if (ValidationTypes.isAny(expression, "none | inherit")) {
5729
                result = true;
5730
            } else {
5731
                if (ValidationTypes.isType(expression, "<flex-grow>")) {
5732
                    if (expression.peek()) {
5733
                        if (ValidationTypes.isType(expression, "<flex-shrink>")) {
5734
                            if (expression.peek()) {
5735
                                result = ValidationTypes.isType(expression, "<flex-basis>");
5736
                            } else {
5737
                                result = true;
5738
                            }
5739
                        } else if (ValidationTypes.isType(expression, "<flex-basis>")) {
5740
                            result = expression.peek() === null;
5741
                        }
5742
                    } else {
5743
                        result = true;
5744
                    }
5745
                } else if (ValidationTypes.isType(expression, "<flex-basis>")) {
5746
                    result = true;
5747
                }
5748
            }
5749

    
5750
            if (!result) {
5751
                part = expression.peek();
5752
                throw new ValidationError("Expected (none | [ <flex-grow> <flex-shrink>? || <flex-basis> ]) but found '" + expression.value.text + "'.", part.line, part.col);
5753
            }
5754

    
5755
            return result;
5756
        }
5757
    }
5758
};
5759

    
5760
parserlib.css = {
5761
Colors              :Colors,
5762
Combinator          :Combinator,
5763
Parser              :Parser,
5764
PropertyName        :PropertyName,
5765
PropertyValue       :PropertyValue,
5766
PropertyValuePart   :PropertyValuePart,
5767
MediaFeature        :MediaFeature,
5768
MediaQuery          :MediaQuery,
5769
Selector            :Selector,
5770
SelectorPart        :SelectorPart,
5771
SelectorSubPart     :SelectorSubPart,
5772
Specificity         :Specificity,
5773
TokenStream         :TokenStream,
5774
Tokens              :Tokens,
5775
ValidationError     :ValidationError
5776
};
5777
})();
5778

    
5779
(function(){
5780
for(var prop in parserlib){
5781
exports[prop] = parserlib[prop];
5782
}
5783
})();
5784

    
5785

    
5786
function objectToString(o) {
5787
  return Object.prototype.toString.call(o);
5788
}
5789
var util = {
5790
  isArray: function (ar) {
5791
    return Array.isArray(ar) || (typeof ar === 'object' && objectToString(ar) === '[object Array]');
5792
  },
5793
  isDate: function (d) {
5794
    return typeof d === 'object' && objectToString(d) === '[object Date]';
5795
  },
5796
  isRegExp: function (re) {
5797
    return typeof re === 'object' && objectToString(re) === '[object RegExp]';
5798
  },
5799
  getRegExpFlags: function (re) {
5800
    var flags = '';
5801
    re.global && (flags += 'g');
5802
    re.ignoreCase && (flags += 'i');
5803
    re.multiline && (flags += 'm');
5804
    return flags;
5805
  }
5806
};
5807

    
5808

    
5809
if (typeof module === 'object')
5810
  module.exports = clone;
5811

    
5812
function clone(parent, circular, depth, prototype) {
5813
  var allParents = [];
5814
  var allChildren = [];
5815

    
5816
  var useBuffer = typeof Buffer != 'undefined';
5817

    
5818
  if (typeof circular == 'undefined')
5819
    circular = true;
5820

    
5821
  if (typeof depth == 'undefined')
5822
    depth = Infinity;
5823
  function _clone(parent, depth) {
5824
    if (parent === null)
5825
      return null;
5826

    
5827
    if (depth == 0)
5828
      return parent;
5829

    
5830
    var child;
5831
    if (typeof parent != 'object') {
5832
      return parent;
5833
    }
5834

    
5835
    if (util.isArray(parent)) {
5836
      child = [];
5837
    } else if (util.isRegExp(parent)) {
5838
      child = new RegExp(parent.source, util.getRegExpFlags(parent));
5839
      if (parent.lastIndex) child.lastIndex = parent.lastIndex;
5840
    } else if (util.isDate(parent)) {
5841
      child = new Date(parent.getTime());
5842
    } else if (useBuffer && Buffer.isBuffer(parent)) {
5843
      child = new Buffer(parent.length);
5844
      parent.copy(child);
5845
      return child;
5846
    } else {
5847
      if (typeof prototype == 'undefined') child = Object.create(Object.getPrototypeOf(parent));
5848
      else child = Object.create(prototype);
5849
    }
5850

    
5851
    if (circular) {
5852
      var index = allParents.indexOf(parent);
5853

    
5854
      if (index != -1) {
5855
        return allChildren[index];
5856
      }
5857
      allParents.push(parent);
5858
      allChildren.push(child);
5859
    }
5860

    
5861
    for (var i in parent) {
5862
      child[i] = _clone(parent[i], depth - 1);
5863
    }
5864

    
5865
    return child;
5866
  }
5867

    
5868
  return _clone(parent, depth);
5869
}
5870
clone.clonePrototype = function(parent) {
5871
  if (parent === null)
5872
    return null;
5873

    
5874
  var c = function () {};
5875
  c.prototype = parent;
5876
  return new c();
5877
};
5878

    
5879
var CSSLint = (function(){
5880

    
5881
    var rules           = [],
5882
        formatters      = [],
5883
        embeddedRuleset = /\/\*csslint([^\*]*)\*\//,
5884
        api             = new parserlib.util.EventTarget();
5885

    
5886
    api.version = "@VERSION@";
5887
    api.addRule = function(rule){
5888
        rules.push(rule);
5889
        rules[rule.id] = rule;
5890
    };
5891
    api.clearRules = function(){
5892
        rules = [];
5893
    };
5894
    api.getRules = function(){
5895
        return [].concat(rules).sort(function(a,b){
5896
            return a.id > b.id ? 1 : 0;
5897
        });
5898
    };
5899
    api.getRuleset = function() {
5900
        var ruleset = {},
5901
            i = 0,
5902
            len = rules.length;
5903

    
5904
        while (i < len){
5905
            ruleset[rules[i++].id] = 1;    //by default, everything is a warning
5906
        }
5907

    
5908
        return ruleset;
5909
    };
5910
    function applyEmbeddedRuleset(text, ruleset){
5911
        var valueMap,
5912
            embedded = text && text.match(embeddedRuleset),
5913
            rules = embedded && embedded[1];
5914

    
5915
        if (rules) {
5916
            valueMap = {
5917
                "true": 2,  // true is error
5918
                "": 1,      // blank is warning
5919
                "false": 0, // false is ignore
5920

    
5921
                "2": 2,     // explicit error
5922
                "1": 1,     // explicit warning
5923
                "0": 0      // explicit ignore
5924
            };
5925

    
5926
            rules.toLowerCase().split(",").forEach(function(rule){
5927
                var pair = rule.split(":"),
5928
                    property = pair[0] || "",
5929
                    value = pair[1] || "";
5930

    
5931
                ruleset[property.trim()] = valueMap[value.trim()];
5932
            });
5933
        }
5934

    
5935
        return ruleset;
5936
    }
5937
    api.addFormatter = function(formatter) {
5938
        formatters[formatter.id] = formatter;
5939
    };
5940
    api.getFormatter = function(formatId){
5941
        return formatters[formatId];
5942
    };
5943
    api.format = function(results, filename, formatId, options) {
5944
        var formatter = this.getFormatter(formatId),
5945
            result = null;
5946

    
5947
        if (formatter){
5948
            result = formatter.startFormat();
5949
            result += formatter.formatResults(results, filename, options || {});
5950
            result += formatter.endFormat();
5951
        }
5952

    
5953
        return result;
5954
    };
5955
    api.hasFormat = function(formatId){
5956
        return formatters.hasOwnProperty(formatId);
5957
    };
5958
    api.verify = function(text, ruleset){
5959

    
5960
        var i = 0,
5961
            reporter,
5962
            lines,
5963
            report,
5964
            parser = new parserlib.css.Parser({ starHack: true, ieFilters: true,
5965
                                                underscoreHack: true, strict: false });
5966
        lines = text.replace(/\n\r?/g, "$split$").split("$split$");
5967

    
5968
        if (!ruleset){
5969
            ruleset = this.getRuleset();
5970
        }
5971

    
5972
        if (embeddedRuleset.test(text)){
5973
            ruleset = clone(ruleset);
5974
            ruleset = applyEmbeddedRuleset(text, ruleset);
5975
        }
5976

    
5977
        reporter = new Reporter(lines, ruleset);
5978

    
5979
        ruleset.errors = 2;       //always report parsing errors as errors
5980
        for (i in ruleset){
5981
            if(ruleset.hasOwnProperty(i) && ruleset[i]){
5982
                if (rules[i]){
5983
                    rules[i].init(parser, reporter);
5984
                }
5985
            }
5986
        }
5987
        try {
5988
            parser.parse(text);
5989
        } catch (ex) {
5990
            reporter.error("Fatal error, cannot continue: " + ex.message, ex.line, ex.col, {});
5991
        }
5992

    
5993
        report = {
5994
            messages    : reporter.messages,
5995
            stats       : reporter.stats,
5996
            ruleset     : reporter.ruleset
5997
        };
5998
        report.messages.sort(function (a, b){
5999
            if (a.rollup && !b.rollup){
6000
                return 1;
6001
            } else if (!a.rollup && b.rollup){
6002
                return -1;
6003
            } else {
6004
                return a.line - b.line;
6005
            }
6006
        });
6007

    
6008
        return report;
6009
    };
6010

    
6011
    return api;
6012

    
6013
})();
6014
function Reporter(lines, ruleset){
6015
    this.messages = [];
6016
    this.stats = [];
6017
    this.lines = lines;
6018
    this.ruleset = ruleset;
6019
}
6020

    
6021
Reporter.prototype = {
6022
    constructor: Reporter,
6023
    error: function(message, line, col, rule){
6024
        this.messages.push({
6025
            type    : "error",
6026
            line    : line,
6027
            col     : col,
6028
            message : message,
6029
            evidence: this.lines[line-1],
6030
            rule    : rule || {}
6031
        });
6032
    },
6033
    warn: function(message, line, col, rule){
6034
        this.report(message, line, col, rule);
6035
    },
6036
    report: function(message, line, col, rule){
6037
        this.messages.push({
6038
            type    : this.ruleset[rule.id] === 2 ? "error" : "warning",
6039
            line    : line,
6040
            col     : col,
6041
            message : message,
6042
            evidence: this.lines[line-1],
6043
            rule    : rule
6044
        });
6045
    },
6046
    info: function(message, line, col, rule){
6047
        this.messages.push({
6048
            type    : "info",
6049
            line    : line,
6050
            col     : col,
6051
            message : message,
6052
            evidence: this.lines[line-1],
6053
            rule    : rule
6054
        });
6055
    },
6056
    rollupError: function(message, rule){
6057
        this.messages.push({
6058
            type    : "error",
6059
            rollup  : true,
6060
            message : message,
6061
            rule    : rule
6062
        });
6063
    },
6064
    rollupWarn: function(message, rule){
6065
        this.messages.push({
6066
            type    : "warning",
6067
            rollup  : true,
6068
            message : message,
6069
            rule    : rule
6070
        });
6071
    },
6072
    stat: function(name, value){
6073
        this.stats[name] = value;
6074
    }
6075
};
6076
CSSLint._Reporter = Reporter;
6077
CSSLint.Util = {
6078
    mix: function(receiver, supplier){
6079
        var prop;
6080

    
6081
        for (prop in supplier){
6082
            if (supplier.hasOwnProperty(prop)){
6083
                receiver[prop] = supplier[prop];
6084
            }
6085
        }
6086

    
6087
        return prop;
6088
    },
6089
    indexOf: function(values, value){
6090
        if (values.indexOf){
6091
            return values.indexOf(value);
6092
        } else {
6093
            for (var i=0, len=values.length; i < len; i++){
6094
                if (values[i] === value){
6095
                    return i;
6096
                }
6097
            }
6098
            return -1;
6099
        }
6100
    },
6101
    forEach: function(values, func) {
6102
        if (values.forEach){
6103
            return values.forEach(func);
6104
        } else {
6105
            for (var i=0, len=values.length; i < len; i++){
6106
                func(values[i], i, values);
6107
            }
6108
        }
6109
    }
6110
};
6111

    
6112
CSSLint.addRule({
6113
    id: "adjoining-classes",
6114
    name: "Disallow adjoining classes",
6115
    desc: "Don't use adjoining classes.",
6116
    browsers: "IE6",
6117
    init: function(parser, reporter){
6118
        var rule = this;
6119
        parser.addListener("startrule", function(event){
6120
            var selectors = event.selectors,
6121
                selector,
6122
                part,
6123
                modifier,
6124
                classCount,
6125
                i, j, k;
6126

    
6127
            for (i=0; i < selectors.length; i++){
6128
                selector = selectors[i];
6129
                for (j=0; j < selector.parts.length; j++){
6130
                    part = selector.parts[j];
6131
                    if (part.type === parser.SELECTOR_PART_TYPE){
6132
                        classCount = 0;
6133
                        for (k=0; k < part.modifiers.length; k++){
6134
                            modifier = part.modifiers[k];
6135
                            if (modifier.type === "class"){
6136
                                classCount++;
6137
                            }
6138
                            if (classCount > 1){
6139
                                reporter.report("Don't use adjoining classes.", part.line, part.col, rule);
6140
                            }
6141
                        }
6142
                    }
6143
                }
6144
            }
6145
        });
6146
    }
6147

    
6148
});
6149
CSSLint.addRule({
6150
    id: "box-model",
6151
    name: "Beware of broken box size",
6152
    desc: "Don't use width or height when using padding or border.",
6153
    browsers: "All",
6154
    init: function(parser, reporter){
6155
        var rule = this,
6156
            widthProperties = {
6157
                border: 1,
6158
                "border-left": 1,
6159
                "border-right": 1,
6160
                padding: 1,
6161
                "padding-left": 1,
6162
                "padding-right": 1
6163
            },
6164
            heightProperties = {
6165
                border: 1,
6166
                "border-bottom": 1,
6167
                "border-top": 1,
6168
                padding: 1,
6169
                "padding-bottom": 1,
6170
                "padding-top": 1
6171
            },
6172
            properties,
6173
            boxSizing = false;
6174

    
6175
        function startRule(){
6176
            properties = {};
6177
            boxSizing = false;
6178
        }
6179

    
6180
        function endRule(){
6181
            var prop, value;
6182

    
6183
            if (!boxSizing) {
6184
                if (properties.height){
6185
                    for (prop in heightProperties){
6186
                        if (heightProperties.hasOwnProperty(prop) && properties[prop]){
6187
                            value = properties[prop].value;
6188
                            if (!(prop === "padding" && value.parts.length === 2 && value.parts[0].value === 0)){
6189
                                reporter.report("Using height with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
6190
                            }
6191
                        }
6192
                    }
6193
                }
6194

    
6195
                if (properties.width){
6196
                    for (prop in widthProperties){
6197
                        if (widthProperties.hasOwnProperty(prop) && properties[prop]){
6198
                            value = properties[prop].value;
6199

    
6200
                            if (!(prop === "padding" && value.parts.length === 2 && value.parts[1].value === 0)){
6201
                                reporter.report("Using width with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
6202
                            }
6203
                        }
6204
                    }
6205
                }
6206
            }
6207
        }
6208

    
6209
        parser.addListener("startrule", startRule);
6210
        parser.addListener("startfontface", startRule);
6211
        parser.addListener("startpage", startRule);
6212
        parser.addListener("startpagemargin", startRule);
6213
        parser.addListener("startkeyframerule", startRule);
6214

    
6215
        parser.addListener("property", function(event){
6216
            var name = event.property.text.toLowerCase();
6217

    
6218
            if (heightProperties[name] || widthProperties[name]){
6219
                if (!/^0\S*$/.test(event.value) && !(name === "border" && event.value.toString() === "none")){
6220
                    properties[name] = { line: event.property.line, col: event.property.col, value: event.value };
6221
                }
6222
            } else {
6223
                if (/^(width|height)/i.test(name) && /^(length|percentage)/.test(event.value.parts[0].type)){
6224
                    properties[name] = 1;
6225
                } else if (name === "box-sizing") {
6226
                    boxSizing = true;
6227
                }
6228
            }
6229

    
6230
        });
6231

    
6232
        parser.addListener("endrule", endRule);
6233
        parser.addListener("endfontface", endRule);
6234
        parser.addListener("endpage", endRule);
6235
        parser.addListener("endpagemargin", endRule);
6236
        parser.addListener("endkeyframerule", endRule);
6237
    }
6238

    
6239
});
6240

    
6241
CSSLint.addRule({
6242
    id: "box-sizing",
6243
    name: "Disallow use of box-sizing",
6244
    desc: "The box-sizing properties isn't supported in IE6 and IE7.",
6245
    browsers: "IE6, IE7",
6246
    tags: ["Compatibility"],
6247
    init: function(parser, reporter){
6248
        var rule = this;
6249

    
6250
        parser.addListener("property", function(event){
6251
            var name = event.property.text.toLowerCase();
6252

    
6253
            if (name === "box-sizing"){
6254
                reporter.report("The box-sizing property isn't supported in IE6 and IE7.", event.line, event.col, rule);
6255
            }
6256
        });
6257
    }
6258

    
6259
});
6260

    
6261
CSSLint.addRule({
6262
    id: "bulletproof-font-face",
6263
    name: "Use the bulletproof @font-face syntax",
6264
    desc: "Use the bulletproof @font-face syntax to avoid 404's in old IE (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax).",
6265
    browsers: "All",
6266
    init: function(parser, reporter){
6267
        var rule = this,
6268
            fontFaceRule = false,
6269
            firstSrc     = true,
6270
            ruleFailed    = false,
6271
            line, col;
6272
        parser.addListener("startfontface", function(){
6273
            fontFaceRule = true;
6274
        });
6275

    
6276
        parser.addListener("property", function(event){
6277
            if (!fontFaceRule) {
6278
                return;
6279
            }
6280

    
6281
            var propertyName = event.property.toString().toLowerCase(),
6282
                value        = event.value.toString();
6283
            line = event.line;
6284
            col  = event.col;
6285
            if (propertyName === "src") {
6286
                var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i;
6287
                if (!value.match(regex) && firstSrc) {
6288
                    ruleFailed = true;
6289
                    firstSrc = false;
6290
                } else if (value.match(regex) && !firstSrc) {
6291
                    ruleFailed = false;
6292
                }
6293
            }
6294

    
6295

    
6296
        });
6297
        parser.addListener("endfontface", function(){
6298
            fontFaceRule = false;
6299

    
6300
            if (ruleFailed) {
6301
                reporter.report("@font-face declaration doesn't follow the fontspring bulletproof syntax.", line, col, rule);
6302
            }
6303
        });
6304
    }
6305
});
6306

    
6307
CSSLint.addRule({
6308
    id: "compatible-vendor-prefixes",
6309
    name: "Require compatible vendor prefixes",
6310
    desc: "Include all compatible vendor prefixes to reach a wider range of users.",
6311
    browsers: "All",
6312
    init: function (parser, reporter) {
6313
        var rule = this,
6314
            compatiblePrefixes,
6315
            properties,
6316
            prop,
6317
            variations,
6318
            prefixed,
6319
            i,
6320
            len,
6321
            inKeyFrame = false,
6322
            arrayPush = Array.prototype.push,
6323
            applyTo = [];
6324
        compatiblePrefixes = {
6325
            "animation"                  : "webkit moz",
6326
            "animation-delay"            : "webkit moz",
6327
            "animation-direction"        : "webkit moz",
6328
            "animation-duration"         : "webkit moz",
6329
            "animation-fill-mode"        : "webkit moz",
6330
            "animation-iteration-count"  : "webkit moz",
6331
            "animation-name"             : "webkit moz",
6332
            "animation-play-state"       : "webkit moz",
6333
            "animation-timing-function"  : "webkit moz",
6334
            "appearance"                 : "webkit moz",
6335
            "border-end"                 : "webkit moz",
6336
            "border-end-color"           : "webkit moz",
6337
            "border-end-style"           : "webkit moz",
6338
            "border-end-width"           : "webkit moz",
6339
            "border-image"               : "webkit moz o",
6340
            "border-radius"              : "webkit",
6341
            "border-start"               : "webkit moz",
6342
            "border-start-color"         : "webkit moz",
6343
            "border-start-style"         : "webkit moz",
6344
            "border-start-width"         : "webkit moz",
6345
            "box-align"                  : "webkit moz ms",
6346
            "box-direction"              : "webkit moz ms",
6347
            "box-flex"                   : "webkit moz ms",
6348
            "box-lines"                  : "webkit ms",
6349
            "box-ordinal-group"          : "webkit moz ms",
6350
            "box-orient"                 : "webkit moz ms",
6351
            "box-pack"                   : "webkit moz ms",
6352
            "box-sizing"                 : "webkit moz",
6353
            "box-shadow"                 : "webkit moz",
6354
            "column-count"               : "webkit moz ms",
6355
            "column-gap"                 : "webkit moz ms",
6356
            "column-rule"                : "webkit moz ms",
6357
            "column-rule-color"          : "webkit moz ms",
6358
            "column-rule-style"          : "webkit moz ms",
6359
            "column-rule-width"          : "webkit moz ms",
6360
            "column-width"               : "webkit moz ms",
6361
            "hyphens"                    : "epub moz",
6362
            "line-break"                 : "webkit ms",
6363
            "margin-end"                 : "webkit moz",
6364
            "margin-start"               : "webkit moz",
6365
            "marquee-speed"              : "webkit wap",
6366
            "marquee-style"              : "webkit wap",
6367
            "padding-end"                : "webkit moz",
6368
            "padding-start"              : "webkit moz",
6369
            "tab-size"                   : "moz o",
6370
            "text-size-adjust"           : "webkit ms",
6371
            "transform"                  : "webkit moz ms o",
6372
            "transform-origin"           : "webkit moz ms o",
6373
            "transition"                 : "webkit moz o",
6374
            "transition-delay"           : "webkit moz o",
6375
            "transition-duration"        : "webkit moz o",
6376
            "transition-property"        : "webkit moz o",
6377
            "transition-timing-function" : "webkit moz o",
6378
            "user-modify"                : "webkit moz",
6379
            "user-select"                : "webkit moz ms",
6380
            "word-break"                 : "epub ms",
6381
            "writing-mode"               : "epub ms"
6382
        };
6383

    
6384

    
6385
        for (prop in compatiblePrefixes) {
6386
            if (compatiblePrefixes.hasOwnProperty(prop)) {
6387
                variations = [];
6388
                prefixed = compatiblePrefixes[prop].split(" ");
6389
                for (i = 0, len = prefixed.length; i < len; i++) {
6390
                    variations.push("-" + prefixed[i] + "-" + prop);
6391
                }
6392
                compatiblePrefixes[prop] = variations;
6393
                arrayPush.apply(applyTo, variations);
6394
            }
6395
        }
6396

    
6397
        parser.addListener("startrule", function () {
6398
            properties = [];
6399
        });
6400

    
6401
        parser.addListener("startkeyframes", function (event) {
6402
            inKeyFrame = event.prefix || true;
6403
        });
6404

    
6405
        parser.addListener("endkeyframes", function () {
6406
            inKeyFrame = false;
6407
        });
6408

    
6409
        parser.addListener("property", function (event) {
6410
            var name = event.property;
6411
            if (CSSLint.Util.indexOf(applyTo, name.text) > -1) {
6412
                if (!inKeyFrame || typeof inKeyFrame !== "string" ||
6413
                        name.text.indexOf("-" + inKeyFrame + "-") !== 0) {
6414
                    properties.push(name);
6415
                }
6416
            }
6417
        });
6418

    
6419
        parser.addListener("endrule", function () {
6420
            if (!properties.length) {
6421
                return;
6422
            }
6423

    
6424
            var propertyGroups = {},
6425
                i,
6426
                len,
6427
                name,
6428
                prop,
6429
                variations,
6430
                value,
6431
                full,
6432
                actual,
6433
                item,
6434
                propertiesSpecified;
6435

    
6436
            for (i = 0, len = properties.length; i < len; i++) {
6437
                name = properties[i];
6438

    
6439
                for (prop in compatiblePrefixes) {
6440
                    if (compatiblePrefixes.hasOwnProperty(prop)) {
6441
                        variations = compatiblePrefixes[prop];
6442
                        if (CSSLint.Util.indexOf(variations, name.text) > -1) {
6443
                            if (!propertyGroups[prop]) {
6444
                                propertyGroups[prop] = {
6445
                                    full : variations.slice(0),
6446
                                    actual : [],
6447
                                    actualNodes: []
6448
                                };
6449
                            }
6450
                            if (CSSLint.Util.indexOf(propertyGroups[prop].actual, name.text) === -1) {
6451
                                propertyGroups[prop].actual.push(name.text);
6452
                                propertyGroups[prop].actualNodes.push(name);
6453
                            }
6454
                        }
6455
                    }
6456
                }
6457
            }
6458

    
6459
            for (prop in propertyGroups) {
6460
                if (propertyGroups.hasOwnProperty(prop)) {
6461
                    value = propertyGroups[prop];
6462
                    full = value.full;
6463
                    actual = value.actual;
6464

    
6465
                    if (full.length > actual.length) {
6466
                        for (i = 0, len = full.length; i < len; i++) {
6467
                            item = full[i];
6468
                            if (CSSLint.Util.indexOf(actual, item) === -1) {
6469
                                propertiesSpecified = (actual.length === 1) ? actual[0] : (actual.length === 2) ? actual.join(" and ") : actual.join(", ");
6470
                                reporter.report("The property " + item + " is compatible with " + propertiesSpecified + " and should be included as well.", value.actualNodes[0].line, value.actualNodes[0].col, rule);
6471
                            }
6472
                        }
6473

    
6474
                    }
6475
                }
6476
            }
6477
        });
6478
    }
6479
});
6480

    
6481
CSSLint.addRule({
6482
    id: "display-property-grouping",
6483
    name: "Require properties appropriate for display",
6484
    desc: "Certain properties shouldn't be used with certain display property values.",
6485
    browsers: "All",
6486
    init: function(parser, reporter){
6487
        var rule = this;
6488

    
6489
        var propertiesToCheck = {
6490
                display: 1,
6491
                "float": "none",
6492
                height: 1,
6493
                width: 1,
6494
                margin: 1,
6495
                "margin-left": 1,
6496
                "margin-right": 1,
6497
                "margin-bottom": 1,
6498
                "margin-top": 1,
6499
                padding: 1,
6500
                "padding-left": 1,
6501
                "padding-right": 1,
6502
                "padding-bottom": 1,
6503
                "padding-top": 1,
6504
                "vertical-align": 1
6505
            },
6506
            properties;
6507

    
6508
        function reportProperty(name, display, msg){
6509
            if (properties[name]){
6510
                if (typeof propertiesToCheck[name] !== "string" || properties[name].value.toLowerCase() !== propertiesToCheck[name]){
6511
                    reporter.report(msg || name + " can't be used with display: " + display + ".", properties[name].line, properties[name].col, rule);
6512
                }
6513
            }
6514
        }
6515

    
6516
        function startRule(){
6517
            properties = {};
6518
        }
6519

    
6520
        function endRule(){
6521

    
6522
            var display = properties.display ? properties.display.value : null;
6523
            if (display){
6524
                switch(display){
6525

    
6526
                    case "inline":
6527
                        reportProperty("height", display);
6528
                        reportProperty("width", display);
6529
                        reportProperty("margin", display);
6530
                        reportProperty("margin-top", display);
6531
                        reportProperty("margin-bottom", display);
6532
                        reportProperty("float", display, "display:inline has no effect on floated elements (but may be used to fix the IE6 double-margin bug).");
6533
                        break;
6534

    
6535
                    case "block":
6536
                        reportProperty("vertical-align", display);
6537
                        break;
6538

    
6539
                    case "inline-block":
6540
                        reportProperty("float", display);
6541
                        break;
6542

    
6543
                    default:
6544
                        if (display.indexOf("table-") === 0){
6545
                            reportProperty("margin", display);
6546
                            reportProperty("margin-left", display);
6547
                            reportProperty("margin-right", display);
6548
                            reportProperty("margin-top", display);
6549
                            reportProperty("margin-bottom", display);
6550
                            reportProperty("float", display);
6551
                        }
6552
                }
6553
            }
6554

    
6555
        }
6556

    
6557
        parser.addListener("startrule", startRule);
6558
        parser.addListener("startfontface", startRule);
6559
        parser.addListener("startkeyframerule", startRule);
6560
        parser.addListener("startpagemargin", startRule);
6561
        parser.addListener("startpage", startRule);
6562

    
6563
        parser.addListener("property", function(event){
6564
            var name = event.property.text.toLowerCase();
6565

    
6566
            if (propertiesToCheck[name]){
6567
                properties[name] = { value: event.value.text, line: event.property.line, col: event.property.col };
6568
            }
6569
        });
6570

    
6571
        parser.addListener("endrule", endRule);
6572
        parser.addListener("endfontface", endRule);
6573
        parser.addListener("endkeyframerule", endRule);
6574
        parser.addListener("endpagemargin", endRule);
6575
        parser.addListener("endpage", endRule);
6576

    
6577
    }
6578

    
6579
});
6580

    
6581
CSSLint.addRule({
6582
    id: "duplicate-background-images",
6583
    name: "Disallow duplicate background images",
6584
    desc: "Every background-image should be unique. Use a common class for e.g. sprites.",
6585
    browsers: "All",
6586
    init: function(parser, reporter){
6587
        var rule = this,
6588
            stack = {};
6589

    
6590
        parser.addListener("property", function(event){
6591
            var name = event.property.text,
6592
                value = event.value,
6593
                i, len;
6594

    
6595
            if (name.match(/background/i)) {
6596
                for (i=0, len=value.parts.length; i < len; i++) {
6597
                    if (value.parts[i].type === "uri") {
6598
                        if (typeof stack[value.parts[i].uri] === "undefined") {
6599
                            stack[value.parts[i].uri] = event;
6600
                        }
6601
                        else {
6602
                            reporter.report("Background image '" + value.parts[i].uri + "' was used multiple times, first declared at line " + stack[value.parts[i].uri].line + ", col " + stack[value.parts[i].uri].col + ".", event.line, event.col, rule);
6603
                        }
6604
                    }
6605
                }
6606
            }
6607
        });
6608
    }
6609
});
6610

    
6611
CSSLint.addRule({
6612
    id: "duplicate-properties",
6613
    name: "Disallow duplicate properties",
6614
    desc: "Duplicate properties must appear one after the other.",
6615
    browsers: "All",
6616
    init: function(parser, reporter){
6617
        var rule = this,
6618
            properties,
6619
            lastProperty;
6620

    
6621
        function startRule(){
6622
            properties = {};
6623
        }
6624

    
6625
        parser.addListener("startrule", startRule);
6626
        parser.addListener("startfontface", startRule);
6627
        parser.addListener("startpage", startRule);
6628
        parser.addListener("startpagemargin", startRule);
6629
        parser.addListener("startkeyframerule", startRule);
6630

    
6631
        parser.addListener("property", function(event){
6632
            var property = event.property,
6633
                name = property.text.toLowerCase();
6634

    
6635
            if (properties[name] && (lastProperty !== name || properties[name] === event.value.text)){
6636
                reporter.report("Duplicate property '" + event.property + "' found.", event.line, event.col, rule);
6637
            }
6638

    
6639
            properties[name] = event.value.text;
6640
            lastProperty = name;
6641

    
6642
        });
6643

    
6644

    
6645
    }
6646

    
6647
});
6648

    
6649
CSSLint.addRule({
6650
    id: "empty-rules",
6651
    name: "Disallow empty rules",
6652
    desc: "Rules without any properties specified should be removed.",
6653
    browsers: "All",
6654
    init: function(parser, reporter){
6655
        var rule = this,
6656
            count = 0;
6657

    
6658
        parser.addListener("startrule", function(){
6659
            count=0;
6660
        });
6661

    
6662
        parser.addListener("property", function(){
6663
            count++;
6664
        });
6665

    
6666
        parser.addListener("endrule", function(event){
6667
            var selectors = event.selectors;
6668
            if (count === 0){
6669
                reporter.report("Rule is empty.", selectors[0].line, selectors[0].col, rule);
6670
            }
6671
        });
6672
    }
6673

    
6674
});
6675

    
6676
CSSLint.addRule({
6677
    id: "errors",
6678
    name: "Parsing Errors",
6679
    desc: "This rule looks for recoverable syntax errors.",
6680
    browsers: "All",
6681
    init: function(parser, reporter){
6682
        var rule = this;
6683

    
6684
        parser.addListener("error", function(event){
6685
            reporter.error(event.message, event.line, event.col, rule);
6686
        });
6687

    
6688
    }
6689

    
6690
});
6691

    
6692
CSSLint.addRule({
6693
    id: "fallback-colors",
6694
    name: "Require fallback colors",
6695
    desc: "For older browsers that don't support RGBA, HSL, or HSLA, provide a fallback color.",
6696
    browsers: "IE6,IE7,IE8",
6697
    init: function(parser, reporter){
6698
        var rule = this,
6699
            lastProperty,
6700
            propertiesToCheck = {
6701
                color: 1,
6702
                background: 1,
6703
                "border-color": 1,
6704
                "border-top-color": 1,
6705
                "border-right-color": 1,
6706
                "border-bottom-color": 1,
6707
                "border-left-color": 1,
6708
                border: 1,
6709
                "border-top": 1,
6710
                "border-right": 1,
6711
                "border-bottom": 1,
6712
                "border-left": 1,
6713
                "background-color": 1
6714
            },
6715
            properties;
6716

    
6717
        function startRule(){
6718
            properties = {};
6719
            lastProperty = null;
6720
        }
6721

    
6722
        parser.addListener("startrule", startRule);
6723
        parser.addListener("startfontface", startRule);
6724
        parser.addListener("startpage", startRule);
6725
        parser.addListener("startpagemargin", startRule);
6726
        parser.addListener("startkeyframerule", startRule);
6727

    
6728
        parser.addListener("property", function(event){
6729
            var property = event.property,
6730
                name = property.text.toLowerCase(),
6731
                parts = event.value.parts,
6732
                i = 0,
6733
                colorType = "",
6734
                len = parts.length;
6735

    
6736
            if(propertiesToCheck[name]){
6737
                while(i < len){
6738
                    if (parts[i].type === "color"){
6739
                        if ("alpha" in parts[i] || "hue" in parts[i]){
6740

    
6741
                            if (/([^\)]+)\(/.test(parts[i])){
6742
                                colorType = RegExp.$1.toUpperCase();
6743
                            }
6744

    
6745
                            if (!lastProperty || (lastProperty.property.text.toLowerCase() !== name || lastProperty.colorType !== "compat")){
6746
                                reporter.report("Fallback " + name + " (hex or RGB) should precede " + colorType + " " + name + ".", event.line, event.col, rule);
6747
                            }
6748
                        } else {
6749
                            event.colorType = "compat";
6750
                        }
6751
                    }
6752

    
6753
                    i++;
6754
                }
6755
            }
6756

    
6757
            lastProperty = event;
6758
        });
6759

    
6760
    }
6761

    
6762
});
6763

    
6764
CSSLint.addRule({
6765
    id: "floats",
6766
    name: "Disallow too many floats",
6767
    desc: "This rule tests if the float property is used too many times",
6768
    browsers: "All",
6769
    init: function(parser, reporter){
6770
        var rule = this;
6771
        var count = 0;
6772
        parser.addListener("property", function(event){
6773
            if (event.property.text.toLowerCase() === "float" &&
6774
                    event.value.text.toLowerCase() !== "none"){
6775
                count++;
6776
            }
6777
        });
6778
        parser.addListener("endstylesheet", function(){
6779
            reporter.stat("floats", count);
6780
            if (count >= 10){
6781
                reporter.rollupWarn("Too many floats (" + count + "), you're probably using them for layout. Consider using a grid system instead.", rule);
6782
            }
6783
        });
6784
    }
6785

    
6786
});
6787

    
6788
CSSLint.addRule({
6789
    id: "font-faces",
6790
    name: "Don't use too many web fonts",
6791
    desc: "Too many different web fonts in the same stylesheet.",
6792
    browsers: "All",
6793
    init: function(parser, reporter){
6794
        var rule = this,
6795
            count = 0;
6796

    
6797

    
6798
        parser.addListener("startfontface", function(){
6799
            count++;
6800
        });
6801

    
6802
        parser.addListener("endstylesheet", function(){
6803
            if (count > 5){
6804
                reporter.rollupWarn("Too many @font-face declarations (" + count + ").", rule);
6805
            }
6806
        });
6807
    }
6808

    
6809
});
6810

    
6811
CSSLint.addRule({
6812
    id: "font-sizes",
6813
    name: "Disallow too many font sizes",
6814
    desc: "Checks the number of font-size declarations.",
6815
    browsers: "All",
6816
    init: function(parser, reporter){
6817
        var rule = this,
6818
            count = 0;
6819
        parser.addListener("property", function(event){
6820
            if (event.property.toString() === "font-size"){
6821
                count++;
6822
            }
6823
        });
6824
        parser.addListener("endstylesheet", function(){
6825
            reporter.stat("font-sizes", count);
6826
            if (count >= 10){
6827
                reporter.rollupWarn("Too many font-size declarations (" + count + "), abstraction needed.", rule);
6828
            }
6829
        });
6830
    }
6831

    
6832
});
6833

    
6834
CSSLint.addRule({
6835
    id: "gradients",
6836
    name: "Require all gradient definitions",
6837
    desc: "When using a vendor-prefixed gradient, make sure to use them all.",
6838
    browsers: "All",
6839
    init: function(parser, reporter){
6840
        var rule = this,
6841
            gradients;
6842

    
6843
        parser.addListener("startrule", function(){
6844
            gradients = {
6845
                moz: 0,
6846
                webkit: 0,
6847
                oldWebkit: 0,
6848
                o: 0
6849
            };
6850
        });
6851

    
6852
        parser.addListener("property", function(event){
6853

    
6854
            if (/\-(moz|o|webkit)(?:\-(?:linear|radial))\-gradient/i.test(event.value)){
6855
                gradients[RegExp.$1] = 1;
6856
            } else if (/\-webkit\-gradient/i.test(event.value)){
6857
                gradients.oldWebkit = 1;
6858
            }
6859

    
6860
        });
6861

    
6862
        parser.addListener("endrule", function(event){
6863
            var missing = [];
6864

    
6865
            if (!gradients.moz){
6866
                missing.push("Firefox 3.6+");
6867
            }
6868

    
6869
            if (!gradients.webkit){
6870
                missing.push("Webkit (Safari 5+, Chrome)");
6871
            }
6872

    
6873
            if (!gradients.oldWebkit){
6874
                missing.push("Old Webkit (Safari 4+, Chrome)");
6875
            }
6876

    
6877
            if (!gradients.o){
6878
                missing.push("Opera 11.1+");
6879
            }
6880

    
6881
            if (missing.length && missing.length < 4){
6882
                reporter.report("Missing vendor-prefixed CSS gradients for " + missing.join(", ") + ".", event.selectors[0].line, event.selectors[0].col, rule);
6883
            }
6884

    
6885
        });
6886

    
6887
    }
6888

    
6889
});
6890

    
6891
CSSLint.addRule({
6892
    id: "ids",
6893
    name: "Disallow IDs in selectors",
6894
    desc: "Selectors should not contain IDs.",
6895
    browsers: "All",
6896
    init: function(parser, reporter){
6897
        var rule = this;
6898
        parser.addListener("startrule", function(event){
6899
            var selectors = event.selectors,
6900
                selector,
6901
                part,
6902
                modifier,
6903
                idCount,
6904
                i, j, k;
6905

    
6906
            for (i=0; i < selectors.length; i++){
6907
                selector = selectors[i];
6908
                idCount = 0;
6909

    
6910
                for (j=0; j < selector.parts.length; j++){
6911
                    part = selector.parts[j];
6912
                    if (part.type === parser.SELECTOR_PART_TYPE){
6913
                        for (k=0; k < part.modifiers.length; k++){
6914
                            modifier = part.modifiers[k];
6915
                            if (modifier.type === "id"){
6916
                                idCount++;
6917
                            }
6918
                        }
6919
                    }
6920
                }
6921

    
6922
                if (idCount === 1){
6923
                    reporter.report("Don't use IDs in selectors.", selector.line, selector.col, rule);
6924
                } else if (idCount > 1){
6925
                    reporter.report(idCount + " IDs in the selector, really?", selector.line, selector.col, rule);
6926
                }
6927
            }
6928

    
6929
        });
6930
    }
6931

    
6932
});
6933

    
6934
CSSLint.addRule({
6935
    id: "import",
6936
    name: "Disallow @import",
6937
    desc: "Don't use @import, use <link> instead.",
6938
    browsers: "All",
6939
    init: function(parser, reporter){
6940
        var rule = this;
6941

    
6942
        parser.addListener("import", function(event){
6943
            reporter.report("@import prevents parallel downloads, use <link> instead.", event.line, event.col, rule);
6944
        });
6945

    
6946
    }
6947

    
6948
});
6949

    
6950
CSSLint.addRule({
6951
    id: "important",
6952
    name: "Disallow !important",
6953
    desc: "Be careful when using !important declaration",
6954
    browsers: "All",
6955
    init: function(parser, reporter){
6956
        var rule = this,
6957
            count = 0;
6958
        parser.addListener("property", function(event){
6959
            if (event.important === true){
6960
                count++;
6961
                reporter.report("Use of !important", event.line, event.col, rule);
6962
            }
6963
        });
6964
        parser.addListener("endstylesheet", function(){
6965
            reporter.stat("important", count);
6966
            if (count >= 10){
6967
                reporter.rollupWarn("Too many !important declarations (" + count + "), try to use less than 10 to avoid specificity issues.", rule);
6968
            }
6969
        });
6970
    }
6971

    
6972
});
6973

    
6974
CSSLint.addRule({
6975
    id: "known-properties",
6976
    name: "Require use of known properties",
6977
    desc: "Properties should be known (listed in CSS3 specification) or be a vendor-prefixed property.",
6978
    browsers: "All",
6979
    init: function(parser, reporter){
6980
        var rule = this;
6981

    
6982
        parser.addListener("property", function(event){
6983
            if (event.invalid) {
6984
                reporter.report(event.invalid.message, event.line, event.col, rule);
6985
            }
6986

    
6987
        });
6988
    }
6989

    
6990
});
6991
CSSLint.addRule({
6992
    id: "order-alphabetical",
6993
    name: "Alphabetical order",
6994
    desc: "Assure properties are in alphabetical order",
6995
    browsers: "All",
6996
    init: function(parser, reporter){
6997
        var rule = this,
6998
            properties;
6999

    
7000
        var startRule = function () {
7001
            properties = [];
7002
        };
7003

    
7004
        parser.addListener("startrule", startRule);
7005
        parser.addListener("startfontface", startRule);
7006
        parser.addListener("startpage", startRule);
7007
        parser.addListener("startpagemargin", startRule);
7008
        parser.addListener("startkeyframerule", startRule);
7009

    
7010
        parser.addListener("property", function(event){
7011
            var name = event.property.text,
7012
                lowerCasePrefixLessName = name.toLowerCase().replace(/^-.*?-/, "");
7013

    
7014
            properties.push(lowerCasePrefixLessName);
7015
        });
7016

    
7017
        parser.addListener("endrule", function(event){
7018
            var currentProperties = properties.join(","),
7019
                expectedProperties = properties.sort().join(",");
7020

    
7021
            if (currentProperties !== expectedProperties){
7022
                reporter.report("Rule doesn't have all its properties in alphabetical ordered.", event.line, event.col, rule);
7023
            }
7024
        });
7025
    }
7026

    
7027
});
7028

    
7029
CSSLint.addRule({
7030
    id: "outline-none",
7031
    name: "Disallow outline: none",
7032
    desc: "Use of outline: none or outline: 0 should be limited to :focus rules.",
7033
    browsers: "All",
7034
    tags: ["Accessibility"],
7035
    init: function(parser, reporter){
7036
        var rule = this,
7037
            lastRule;
7038

    
7039
        function startRule(event){
7040
            if (event.selectors){
7041
                lastRule = {
7042
                    line: event.line,
7043
                    col: event.col,
7044
                    selectors: event.selectors,
7045
                    propCount: 0,
7046
                    outline: false
7047
                };
7048
            } else {
7049
                lastRule = null;
7050
            }
7051
        }
7052

    
7053
        function endRule(){
7054
            if (lastRule){
7055
                if (lastRule.outline){
7056
                    if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") === -1){
7057
                        reporter.report("Outlines should only be modified using :focus.", lastRule.line, lastRule.col, rule);
7058
                    } else if (lastRule.propCount === 1) {
7059
                        reporter.report("Outlines shouldn't be hidden unless other visual changes are made.", lastRule.line, lastRule.col, rule);
7060
                    }
7061
                }
7062
            }
7063
        }
7064

    
7065
        parser.addListener("startrule", startRule);
7066
        parser.addListener("startfontface", startRule);
7067
        parser.addListener("startpage", startRule);
7068
        parser.addListener("startpagemargin", startRule);
7069
        parser.addListener("startkeyframerule", startRule);
7070

    
7071
        parser.addListener("property", function(event){
7072
            var name = event.property.text.toLowerCase(),
7073
                value = event.value;
7074

    
7075
            if (lastRule){
7076
                lastRule.propCount++;
7077
                if (name === "outline" && (value.toString() === "none" || value.toString() === "0")){
7078
                    lastRule.outline = true;
7079
                }
7080
            }
7081

    
7082
        });
7083

    
7084
        parser.addListener("endrule", endRule);
7085
        parser.addListener("endfontface", endRule);
7086
        parser.addListener("endpage", endRule);
7087
        parser.addListener("endpagemargin", endRule);
7088
        parser.addListener("endkeyframerule", endRule);
7089

    
7090
    }
7091

    
7092
});
7093

    
7094
CSSLint.addRule({
7095
    id: "overqualified-elements",
7096
    name: "Disallow overqualified elements",
7097
    desc: "Don't use classes or IDs with elements (a.foo or a#foo).",
7098
    browsers: "All",
7099
    init: function(parser, reporter){
7100
        var rule = this,
7101
            classes = {};
7102

    
7103
        parser.addListener("startrule", function(event){
7104
            var selectors = event.selectors,
7105
                selector,
7106
                part,
7107
                modifier,
7108
                i, j, k;
7109

    
7110
            for (i=0; i < selectors.length; i++){
7111
                selector = selectors[i];
7112

    
7113
                for (j=0; j < selector.parts.length; j++){
7114
                    part = selector.parts[j];
7115
                    if (part.type === parser.SELECTOR_PART_TYPE){
7116
                        for (k=0; k < part.modifiers.length; k++){
7117
                            modifier = part.modifiers[k];
7118
                            if (part.elementName && modifier.type === "id"){
7119
                                reporter.report("Element (" + part + ") is overqualified, just use " + modifier + " without element name.", part.line, part.col, rule);
7120
                            } else if (modifier.type === "class"){
7121

    
7122
                                if (!classes[modifier]){
7123
                                    classes[modifier] = [];
7124
                                }
7125
                                classes[modifier].push({ modifier: modifier, part: part });
7126
                            }
7127
                        }
7128
                    }
7129
                }
7130
            }
7131
        });
7132

    
7133
        parser.addListener("endstylesheet", function(){
7134

    
7135
            var prop;
7136
            for (prop in classes){
7137
                if (classes.hasOwnProperty(prop)){
7138
                    if (classes[prop].length === 1 && classes[prop][0].part.elementName){
7139
                        reporter.report("Element (" + classes[prop][0].part + ") is overqualified, just use " + classes[prop][0].modifier + " without element name.", classes[prop][0].part.line, classes[prop][0].part.col, rule);
7140
                    }
7141
                }
7142
            }
7143
        });
7144
    }
7145

    
7146
});
7147

    
7148
CSSLint.addRule({
7149
    id: "qualified-headings",
7150
    name: "Disallow qualified headings",
7151
    desc: "Headings should not be qualified (namespaced).",
7152
    browsers: "All",
7153
    init: function(parser, reporter){
7154
        var rule = this;
7155

    
7156
        parser.addListener("startrule", function(event){
7157
            var selectors = event.selectors,
7158
                selector,
7159
                part,
7160
                i, j;
7161

    
7162
            for (i=0; i < selectors.length; i++){
7163
                selector = selectors[i];
7164

    
7165
                for (j=0; j < selector.parts.length; j++){
7166
                    part = selector.parts[j];
7167
                    if (part.type === parser.SELECTOR_PART_TYPE){
7168
                        if (part.elementName && /h[1-6]/.test(part.elementName.toString()) && j > 0){
7169
                            reporter.report("Heading (" + part.elementName + ") should not be qualified.", part.line, part.col, rule);
7170
                        }
7171
                    }
7172
                }
7173
            }
7174
        });
7175
    }
7176

    
7177
});
7178

    
7179
CSSLint.addRule({
7180
    id: "regex-selectors",
7181
    name: "Disallow selectors that look like regexs",
7182
    desc: "Selectors that look like regular expressions are slow and should be avoided.",
7183
    browsers: "All",
7184
    init: function(parser, reporter){
7185
        var rule = this;
7186

    
7187
        parser.addListener("startrule", function(event){
7188
            var selectors = event.selectors,
7189
                selector,
7190
                part,
7191
                modifier,
7192
                i, j, k;
7193

    
7194
            for (i=0; i < selectors.length; i++){
7195
                selector = selectors[i];
7196
                for (j=0; j < selector.parts.length; j++){
7197
                    part = selector.parts[j];
7198
                    if (part.type === parser.SELECTOR_PART_TYPE){
7199
                        for (k=0; k < part.modifiers.length; k++){
7200
                            modifier = part.modifiers[k];
7201
                            if (modifier.type === "attribute"){
7202
                                if (/([\~\|\^\$\*]=)/.test(modifier)){
7203
                                    reporter.report("Attribute selectors with " + RegExp.$1 + " are slow!", modifier.line, modifier.col, rule);
7204
                                }
7205
                            }
7206

    
7207
                        }
7208
                    }
7209
                }
7210
            }
7211
        });
7212
    }
7213

    
7214
});
7215

    
7216
CSSLint.addRule({
7217
    id: "rules-count",
7218
    name: "Rules Count",
7219
    desc: "Track how many rules there are.",
7220
    browsers: "All",
7221
    init: function(parser, reporter){
7222
        var count = 0;
7223
        parser.addListener("startrule", function(){
7224
            count++;
7225
        });
7226

    
7227
        parser.addListener("endstylesheet", function(){
7228
            reporter.stat("rule-count", count);
7229
        });
7230
    }
7231

    
7232
});
7233

    
7234
CSSLint.addRule({
7235
    id: "selector-max-approaching",
7236
    name: "Warn when approaching the 4095 selector limit for IE",
7237
    desc: "Will warn when selector count is >= 3800 selectors.",
7238
    browsers: "IE",
7239
    init: function(parser, reporter) {
7240
        var rule = this, count = 0;
7241

    
7242
        parser.addListener("startrule", function(event) {
7243
            count += event.selectors.length;
7244
        });
7245

    
7246
        parser.addListener("endstylesheet", function() {
7247
            if (count >= 3800) {
7248
                reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.",0,0,rule);
7249
            }
7250
        });
7251
    }
7252

    
7253
});
7254

    
7255
CSSLint.addRule({
7256
    id: "selector-max",
7257
    name: "Error when past the 4095 selector limit for IE",
7258
    desc: "Will error when selector count is > 4095.",
7259
    browsers: "IE",
7260
    init: function(parser, reporter){
7261
        var rule = this, count = 0;
7262

    
7263
        parser.addListener("startrule", function(event) {
7264
            count += event.selectors.length;
7265
        });
7266

    
7267
        parser.addListener("endstylesheet", function() {
7268
            if (count > 4095) {
7269
                reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.",0,0,rule);
7270
            }
7271
        });
7272
    }
7273

    
7274
});
7275

    
7276
CSSLint.addRule({
7277
    id: "selector-newline",
7278
    name: "Disallow new-line characters in selectors",
7279
    desc: "New-line characters in selectors are usually a forgotten comma and not a descendant combinator.",
7280
    browsers: "All",
7281
    init: function(parser, reporter) {
7282
        var rule = this;
7283

    
7284
        function startRule(event) {
7285
            var i, len, selector, p, n, pLen, part, part2, type, currentLine, nextLine,
7286
                selectors = event.selectors;
7287

    
7288
            for (i = 0, len = selectors.length; i < len; i++) {
7289
                selector = selectors[i];
7290
                for (p = 0, pLen = selector.parts.length; p < pLen; p++) {
7291
                    for (n = p + 1; n < pLen; n++) {
7292
                        part = selector.parts[p];
7293
                        part2 = selector.parts[n];
7294
                        type = part.type;
7295
                        currentLine = part.line;
7296
                        nextLine = part2.line;
7297

    
7298
                        if (type === "descendant" && nextLine > currentLine) {
7299
                            reporter.report("newline character found in selector (forgot a comma?)", currentLine, selectors[i].parts[0].col, rule);
7300
                        }
7301
                    }
7302
                }
7303

    
7304
            }
7305
        }
7306

    
7307
        parser.addListener("startrule", startRule);
7308

    
7309
    }
7310
});
7311

    
7312
CSSLint.addRule({
7313
    id: "shorthand",
7314
    name: "Require shorthand properties",
7315
    desc: "Use shorthand properties where possible.",
7316
    browsers: "All",
7317
    init: function(parser, reporter){
7318
        var rule = this,
7319
            prop, i, len,
7320
            propertiesToCheck = {},
7321
            properties,
7322
            mapping = {
7323
                "margin": [
7324
                    "margin-top",
7325
                    "margin-bottom",
7326
                    "margin-left",
7327
                    "margin-right"
7328
                ],
7329
                "padding": [
7330
                    "padding-top",
7331
                    "padding-bottom",
7332
                    "padding-left",
7333
                    "padding-right"
7334
                ]
7335
            };
7336
        for (prop in mapping){
7337
            if (mapping.hasOwnProperty(prop)){
7338
                for (i=0, len=mapping[prop].length; i < len; i++){
7339
                    propertiesToCheck[mapping[prop][i]] = prop;
7340
                }
7341
            }
7342
        }
7343

    
7344
        function startRule(){
7345
            properties = {};
7346
        }
7347
        function endRule(event){
7348

    
7349
            var prop, i, len, total;
7350
            for (prop in mapping){
7351
                if (mapping.hasOwnProperty(prop)){
7352
                    total=0;
7353

    
7354
                    for (i=0, len=mapping[prop].length; i < len; i++){
7355
                        total += properties[mapping[prop][i]] ? 1 : 0;
7356
                    }
7357

    
7358
                    if (total === mapping[prop].length){
7359
                        reporter.report("The properties " + mapping[prop].join(", ") + " can be replaced by " + prop + ".", event.line, event.col, rule);
7360
                    }
7361
                }
7362
            }
7363
        }
7364

    
7365
        parser.addListener("startrule", startRule);
7366
        parser.addListener("startfontface", startRule);
7367
        parser.addListener("property", function(event){
7368
            var name = event.property.toString().toLowerCase();
7369

    
7370
            if (propertiesToCheck[name]){
7371
                properties[name] = 1;
7372
            }
7373
        });
7374

    
7375
        parser.addListener("endrule", endRule);
7376
        parser.addListener("endfontface", endRule);
7377

    
7378
    }
7379

    
7380
});
7381

    
7382
CSSLint.addRule({
7383
    id: "star-property-hack",
7384
    name: "Disallow properties with a star prefix",
7385
    desc: "Checks for the star property hack (targets IE6/7)",
7386
    browsers: "All",
7387
    init: function(parser, reporter){
7388
        var rule = this;
7389
        parser.addListener("property", function(event){
7390
            var property = event.property;
7391

    
7392
            if (property.hack === "*") {
7393
                reporter.report("Property with star prefix found.", event.property.line, event.property.col, rule);
7394
            }
7395
        });
7396
    }
7397
});
7398

    
7399
CSSLint.addRule({
7400
    id: "text-indent",
7401
    name: "Disallow negative text-indent",
7402
    desc: "Checks for text indent less than -99px",
7403
    browsers: "All",
7404
    init: function(parser, reporter){
7405
        var rule = this,
7406
            textIndent,
7407
            direction;
7408

    
7409

    
7410
        function startRule(){
7411
            textIndent = false;
7412
            direction = "inherit";
7413
        }
7414
        function endRule(){
7415
            if (textIndent && direction !== "ltr"){
7416
                reporter.report("Negative text-indent doesn't work well with RTL. If you use text-indent for image replacement explicitly set direction for that item to ltr.", textIndent.line, textIndent.col, rule);
7417
            }
7418
        }
7419

    
7420
        parser.addListener("startrule", startRule);
7421
        parser.addListener("startfontface", startRule);
7422
        parser.addListener("property", function(event){
7423
            var name = event.property.toString().toLowerCase(),
7424
                value = event.value;
7425

    
7426
            if (name === "text-indent" && value.parts[0].value < -99){
7427
                textIndent = event.property;
7428
            } else if (name === "direction" && value.toString() === "ltr"){
7429
                direction = "ltr";
7430
            }
7431
        });
7432

    
7433
        parser.addListener("endrule", endRule);
7434
        parser.addListener("endfontface", endRule);
7435

    
7436
    }
7437

    
7438
});
7439

    
7440
CSSLint.addRule({
7441
    id: "underscore-property-hack",
7442
    name: "Disallow properties with an underscore prefix",
7443
    desc: "Checks for the underscore property hack (targets IE6)",
7444
    browsers: "All",
7445
    init: function(parser, reporter){
7446
        var rule = this;
7447
        parser.addListener("property", function(event){
7448
            var property = event.property;
7449

    
7450
            if (property.hack === "_") {
7451
                reporter.report("Property with underscore prefix found.", event.property.line, event.property.col, rule);
7452
            }
7453
        });
7454
    }
7455
});
7456

    
7457
CSSLint.addRule({
7458
    id: "unique-headings",
7459
    name: "Headings should only be defined once",
7460
    desc: "Headings should be defined only once.",
7461
    browsers: "All",
7462
    init: function(parser, reporter){
7463
        var rule = this;
7464

    
7465
        var headings = {
7466
                h1: 0,
7467
                h2: 0,
7468
                h3: 0,
7469
                h4: 0,
7470
                h5: 0,
7471
                h6: 0
7472
            };
7473

    
7474
        parser.addListener("startrule", function(event){
7475
            var selectors = event.selectors,
7476
                selector,
7477
                part,
7478
                pseudo,
7479
                i, j;
7480

    
7481
            for (i=0; i < selectors.length; i++){
7482
                selector = selectors[i];
7483
                part = selector.parts[selector.parts.length-1];
7484

    
7485
                if (part.elementName && /(h[1-6])/i.test(part.elementName.toString())){
7486

    
7487
                    for (j=0; j < part.modifiers.length; j++){
7488
                        if (part.modifiers[j].type === "pseudo"){
7489
                            pseudo = true;
7490
                            break;
7491
                        }
7492
                    }
7493

    
7494
                    if (!pseudo){
7495
                        headings[RegExp.$1]++;
7496
                        if (headings[RegExp.$1] > 1) {
7497
                            reporter.report("Heading (" + part.elementName + ") has already been defined.", part.line, part.col, rule);
7498
                        }
7499
                    }
7500
                }
7501
            }
7502
        });
7503

    
7504
        parser.addListener("endstylesheet", function(){
7505
            var prop,
7506
                messages = [];
7507

    
7508
            for (prop in headings){
7509
                if (headings.hasOwnProperty(prop)){
7510
                    if (headings[prop] > 1){
7511
                        messages.push(headings[prop] + " " + prop + "s");
7512
                    }
7513
                }
7514
            }
7515

    
7516
            if (messages.length){
7517
                reporter.rollupWarn("You have " + messages.join(", ") + " defined in this stylesheet.", rule);
7518
            }
7519
        });
7520
    }
7521

    
7522
});
7523

    
7524
CSSLint.addRule({
7525
    id: "universal-selector",
7526
    name: "Disallow universal selector",
7527
    desc: "The universal selector (*) is known to be slow.",
7528
    browsers: "All",
7529
    init: function(parser, reporter){
7530
        var rule = this;
7531

    
7532
        parser.addListener("startrule", function(event){
7533
            var selectors = event.selectors,
7534
                selector,
7535
                part,
7536
                i;
7537

    
7538
            for (i=0; i < selectors.length; i++){
7539
                selector = selectors[i];
7540

    
7541
                part = selector.parts[selector.parts.length-1];
7542
                if (part.elementName === "*"){
7543
                    reporter.report(rule.desc, part.line, part.col, rule);
7544
                }
7545
            }
7546
        });
7547
    }
7548

    
7549
});
7550

    
7551
CSSLint.addRule({
7552
    id: "unqualified-attributes",
7553
    name: "Disallow unqualified attribute selectors",
7554
    desc: "Unqualified attribute selectors are known to be slow.",
7555
    browsers: "All",
7556
    init: function(parser, reporter){
7557
        var rule = this;
7558

    
7559
        parser.addListener("startrule", function(event){
7560

    
7561
            var selectors = event.selectors,
7562
                selector,
7563
                part,
7564
                modifier,
7565
                i, k;
7566

    
7567
            for (i=0; i < selectors.length; i++){
7568
                selector = selectors[i];
7569

    
7570
                part = selector.parts[selector.parts.length-1];
7571
                if (part.type === parser.SELECTOR_PART_TYPE){
7572
                    for (k=0; k < part.modifiers.length; k++){
7573
                        modifier = part.modifiers[k];
7574
                        if (modifier.type === "attribute" && (!part.elementName || part.elementName === "*")){
7575
                            reporter.report(rule.desc, part.line, part.col, rule);
7576
                        }
7577
                    }
7578
                }
7579

    
7580
            }
7581
        });
7582
    }
7583

    
7584
});
7585

    
7586
CSSLint.addRule({
7587
    id: "vendor-prefix",
7588
    name: "Require standard property with vendor prefix",
7589
    desc: "When using a vendor-prefixed property, make sure to include the standard one.",
7590
    browsers: "All",
7591
    init: function(parser, reporter){
7592
        var rule = this,
7593
            properties,
7594
            num,
7595
            propertiesToCheck = {
7596
                "-webkit-border-radius": "border-radius",
7597
                "-webkit-border-top-left-radius": "border-top-left-radius",
7598
                "-webkit-border-top-right-radius": "border-top-right-radius",
7599
                "-webkit-border-bottom-left-radius": "border-bottom-left-radius",
7600
                "-webkit-border-bottom-right-radius": "border-bottom-right-radius",
7601

    
7602
                "-o-border-radius": "border-radius",
7603
                "-o-border-top-left-radius": "border-top-left-radius",
7604
                "-o-border-top-right-radius": "border-top-right-radius",
7605
                "-o-border-bottom-left-radius": "border-bottom-left-radius",
7606
                "-o-border-bottom-right-radius": "border-bottom-right-radius",
7607

    
7608
                "-moz-border-radius": "border-radius",
7609
                "-moz-border-radius-topleft": "border-top-left-radius",
7610
                "-moz-border-radius-topright": "border-top-right-radius",
7611
                "-moz-border-radius-bottomleft": "border-bottom-left-radius",
7612
                "-moz-border-radius-bottomright": "border-bottom-right-radius",
7613

    
7614
                "-moz-column-count": "column-count",
7615
                "-webkit-column-count": "column-count",
7616

    
7617
                "-moz-column-gap": "column-gap",
7618
                "-webkit-column-gap": "column-gap",
7619

    
7620
                "-moz-column-rule": "column-rule",
7621
                "-webkit-column-rule": "column-rule",
7622

    
7623
                "-moz-column-rule-style": "column-rule-style",
7624
                "-webkit-column-rule-style": "column-rule-style",
7625

    
7626
                "-moz-column-rule-color": "column-rule-color",
7627
                "-webkit-column-rule-color": "column-rule-color",
7628

    
7629
                "-moz-column-rule-width": "column-rule-width",
7630
                "-webkit-column-rule-width": "column-rule-width",
7631

    
7632
                "-moz-column-width": "column-width",
7633
                "-webkit-column-width": "column-width",
7634

    
7635
                "-webkit-column-span": "column-span",
7636
                "-webkit-columns": "columns",
7637

    
7638
                "-moz-box-shadow": "box-shadow",
7639
                "-webkit-box-shadow": "box-shadow",
7640

    
7641
                "-moz-transform" : "transform",
7642
                "-webkit-transform" : "transform",
7643
                "-o-transform" : "transform",
7644
                "-ms-transform" : "transform",
7645

    
7646
                "-moz-transform-origin" : "transform-origin",
7647
                "-webkit-transform-origin" : "transform-origin",
7648
                "-o-transform-origin" : "transform-origin",
7649
                "-ms-transform-origin" : "transform-origin",
7650

    
7651
                "-moz-box-sizing" : "box-sizing",
7652
                "-webkit-box-sizing" : "box-sizing"
7653
            };
7654
        function startRule(){
7655
            properties = {};
7656
            num = 1;
7657
        }
7658
        function endRule(){
7659
            var prop,
7660
                i,
7661
                len,
7662
                needed,
7663
                actual,
7664
                needsStandard = [];
7665

    
7666
            for (prop in properties){
7667
                if (propertiesToCheck[prop]){
7668
                    needsStandard.push({ actual: prop, needed: propertiesToCheck[prop]});
7669
                }
7670
            }
7671

    
7672
            for (i=0, len=needsStandard.length; i < len; i++){
7673
                needed = needsStandard[i].needed;
7674
                actual = needsStandard[i].actual;
7675

    
7676
                if (!properties[needed]){
7677
                    reporter.report("Missing standard property '" + needed + "' to go along with '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
7678
                } else {
7679
                    if (properties[needed][0].pos < properties[actual][0].pos){
7680
                        reporter.report("Standard property '" + needed + "' should come after vendor-prefixed property '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
7681
                    }
7682
                }
7683
            }
7684

    
7685
        }
7686

    
7687
        parser.addListener("startrule", startRule);
7688
        parser.addListener("startfontface", startRule);
7689
        parser.addListener("startpage", startRule);
7690
        parser.addListener("startpagemargin", startRule);
7691
        parser.addListener("startkeyframerule", startRule);
7692

    
7693
        parser.addListener("property", function(event){
7694
            var name = event.property.text.toLowerCase();
7695

    
7696
            if (!properties[name]){
7697
                properties[name] = [];
7698
            }
7699

    
7700
            properties[name].push({ name: event.property, value : event.value, pos:num++ });
7701
        });
7702

    
7703
        parser.addListener("endrule", endRule);
7704
        parser.addListener("endfontface", endRule);
7705
        parser.addListener("endpage", endRule);
7706
        parser.addListener("endpagemargin", endRule);
7707
        parser.addListener("endkeyframerule", endRule);
7708
    }
7709

    
7710
});
7711

    
7712
CSSLint.addRule({
7713
    id: "zero-units",
7714
    name: "Disallow units for 0 values",
7715
    desc: "You don't need to specify units when a value is 0.",
7716
    browsers: "All",
7717
    init: function(parser, reporter){
7718
        var rule = this;
7719
        parser.addListener("property", function(event){
7720
            var parts = event.value.parts,
7721
                i = 0,
7722
                len = parts.length;
7723

    
7724
            while(i < len){
7725
                if ((parts[i].units || parts[i].type === "percentage") && parts[i].value === 0 && parts[i].type !== "time"){
7726
                    reporter.report("Values of 0 shouldn't have units specified.", parts[i].line, parts[i].col, rule);
7727
                }
7728
                i++;
7729
            }
7730

    
7731
        });
7732

    
7733
    }
7734

    
7735
});
7736

    
7737
(function() {
7738
    var xmlEscape = function(str) {
7739
        if (!str || str.constructor !== String) {
7740
            return "";
7741
        }
7742

    
7743
        return str.replace(/[\"&><]/g, function(match) {
7744
            switch (match) {
7745
                case "\"":
7746
                    return "&quot;";
7747
                case "&":
7748
                    return "&amp;";
7749
                case "<":
7750
                    return "&lt;";
7751
                case ">":
7752
                    return "&gt;";
7753
            }
7754
        });
7755
    };
7756

    
7757
    CSSLint.addFormatter({
7758
        id: "checkstyle-xml",
7759
        name: "Checkstyle XML format",
7760
        startFormat: function(){
7761
            return "<?xml version=\"1.0\" encoding=\"utf-8\"?><checkstyle>";
7762
        },
7763
        endFormat: function(){
7764
            return "</checkstyle>";
7765
        },
7766
        readError: function(filename, message) {
7767
            return "<file name=\"" + xmlEscape(filename) + "\"><error line=\"0\" column=\"0\" severty=\"error\" message=\"" + xmlEscape(message) + "\"></error></file>";
7768
        },
7769
        formatResults: function(results, filename/*, options*/) {
7770
            var messages = results.messages,
7771
                output = [];
7772
            var generateSource = function(rule) {
7773
                if (!rule || !("name" in rule)) {
7774
                    return "";
7775
                }
7776
                return "net.csslint." + rule.name.replace(/\s/g,"");
7777
            };
7778

    
7779

    
7780

    
7781
            if (messages.length > 0) {
7782
                output.push("<file name=\""+filename+"\">");
7783
                CSSLint.Util.forEach(messages, function (message) {
7784
                    if (!message.rollup) {
7785
                        output.push("<error line=\"" + message.line + "\" column=\"" + message.col + "\" severity=\"" + message.type + "\"" +
7786
                          " message=\"" + xmlEscape(message.message) + "\" source=\"" + generateSource(message.rule) +"\"/>");
7787
                    }
7788
                });
7789
                output.push("</file>");
7790
            }
7791

    
7792
            return output.join("");
7793
        }
7794
    });
7795

    
7796
}());
7797

    
7798
CSSLint.addFormatter({
7799
    id: "compact",
7800
    name: "Compact, 'porcelain' format",
7801
    startFormat: function() {
7802
        return "";
7803
    },
7804
    endFormat: function() {
7805
        return "";
7806
    },
7807
    formatResults: function(results, filename, options) {
7808
        var messages = results.messages,
7809
            output = "";
7810
        options = options || {};
7811
        var capitalize = function(str) {
7812
            return str.charAt(0).toUpperCase() + str.slice(1);
7813
        };
7814

    
7815
        if (messages.length === 0) {
7816
              return options.quiet ? "" : filename + ": Lint Free!";
7817
        }
7818

    
7819
        CSSLint.Util.forEach(messages, function(message) {
7820
            if (message.rollup) {
7821
                output += filename + ": " + capitalize(message.type) + " - " + message.message + "\n";
7822
            } else {
7823
                output += filename + ": " + "line " + message.line +
7824
                    ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
7825
            }
7826
        });
7827

    
7828
        return output;
7829
    }
7830
});
7831

    
7832
CSSLint.addFormatter({
7833
    id: "csslint-xml",
7834
    name: "CSSLint XML format",
7835
    startFormat: function(){
7836
        return "<?xml version=\"1.0\" encoding=\"utf-8\"?><csslint>";
7837
    },
7838
    endFormat: function(){
7839
        return "</csslint>";
7840
    },
7841
    formatResults: function(results, filename/*, options*/) {
7842
        var messages = results.messages,
7843
            output = [];
7844
        var escapeSpecialCharacters = function(str) {
7845
            if (!str || str.constructor !== String) {
7846
                return "";
7847
            }
7848
            return str.replace(/\"/g, "'").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
7849
        };
7850

    
7851
        if (messages.length > 0) {
7852
            output.push("<file name=\""+filename+"\">");
7853
            CSSLint.Util.forEach(messages, function (message) {
7854
                if (message.rollup) {
7855
                    output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
7856
                } else {
7857
                    output.push("<issue line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
7858
                        " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
7859
                }
7860
            });
7861
            output.push("</file>");
7862
        }
7863

    
7864
        return output.join("");
7865
    }
7866
});
7867

    
7868
CSSLint.addFormatter({
7869
    id: "junit-xml",
7870
    name: "JUNIT XML format",
7871
    startFormat: function(){
7872
        return "<?xml version=\"1.0\" encoding=\"utf-8\"?><testsuites>";
7873
    },
7874
    endFormat: function() {
7875
        return "</testsuites>";
7876
    },
7877
    formatResults: function(results, filename/*, options*/) {
7878

    
7879
        var messages = results.messages,
7880
            output = [],
7881
            tests = {
7882
                "error": 0,
7883
                "failure": 0
7884
            };
7885
        var generateSource = function(rule) {
7886
            if (!rule || !("name" in rule)) {
7887
                return "";
7888
            }
7889
            return "net.csslint." + rule.name.replace(/\s/g,"");
7890
        };
7891
        var escapeSpecialCharacters = function(str) {
7892

    
7893
            if (!str || str.constructor !== String) {
7894
                return "";
7895
            }
7896

    
7897
            return str.replace(/\"/g, "'").replace(/</g, "&lt;").replace(/>/g, "&gt;");
7898

    
7899
        };
7900

    
7901
        if (messages.length > 0) {
7902

    
7903
            messages.forEach(function (message) {
7904
                var type = message.type === "warning" ? "error" : message.type;
7905
                if (!message.rollup) {
7906
                    output.push("<testcase time=\"0\" name=\"" + generateSource(message.rule) + "\">");
7907
                    output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\"><![CDATA[" + message.line + ":" + message.col + ":" + escapeSpecialCharacters(message.evidence)  + "]]></" + type + ">");
7908
                    output.push("</testcase>");
7909

    
7910
                    tests[type] += 1;
7911

    
7912
                }
7913

    
7914
            });
7915

    
7916
            output.unshift("<testsuite time=\"0\" tests=\"" + messages.length + "\" skipped=\"0\" errors=\"" + tests.error + "\" failures=\"" + tests.failure + "\" package=\"net.csslint\" name=\"" + filename + "\">");
7917
            output.push("</testsuite>");
7918

    
7919
        }
7920

    
7921
        return output.join("");
7922

    
7923
    }
7924
});
7925

    
7926
CSSLint.addFormatter({
7927
    id: "lint-xml",
7928
    name: "Lint XML format",
7929
    startFormat: function(){
7930
        return "<?xml version=\"1.0\" encoding=\"utf-8\"?><lint>";
7931
    },
7932
    endFormat: function(){
7933
        return "</lint>";
7934
    },
7935
    formatResults: function(results, filename/*, options*/) {
7936
        var messages = results.messages,
7937
            output = [];
7938
        var escapeSpecialCharacters = function(str) {
7939
            if (!str || str.constructor !== String) {
7940
                return "";
7941
            }
7942
            return str.replace(/\"/g, "'").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
7943
        };
7944

    
7945
        if (messages.length > 0) {
7946

    
7947
            output.push("<file name=\""+filename+"\">");
7948
            CSSLint.Util.forEach(messages, function (message) {
7949
                if (message.rollup) {
7950
                    output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
7951
                } else {
7952
                    output.push("<issue line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
7953
                        " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
7954
                }
7955
            });
7956
            output.push("</file>");
7957
        }
7958

    
7959
        return output.join("");
7960
    }
7961
});
7962

    
7963
CSSLint.addFormatter({
7964
    id: "text",
7965
    name: "Plain Text",
7966
    startFormat: function() {
7967
        return "";
7968
    },
7969
    endFormat: function() {
7970
        return "";
7971
    },
7972
    formatResults: function(results, filename, options) {
7973
        var messages = results.messages,
7974
            output = "";
7975
        options = options || {};
7976

    
7977
        if (messages.length === 0) {
7978
            return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + ".";
7979
        }
7980

    
7981
        output = "\n\ncsslint: There ";
7982
        if (messages.length === 1) {
7983
            output += "is 1 problem";
7984
        } else {
7985
            output += "are " + messages.length  +  " problems";
7986
        }
7987
        output += " in " + filename + ".";
7988

    
7989
        var pos = filename.lastIndexOf("/"),
7990
            shortFilename = filename;
7991

    
7992
        if (pos === -1){
7993
            pos = filename.lastIndexOf("\\");
7994
        }
7995
        if (pos > -1){
7996
            shortFilename = filename.substring(pos+1);
7997
        }
7998

    
7999
        CSSLint.Util.forEach(messages, function (message, i) {
8000
            output = output + "\n\n" + shortFilename;
8001
            if (message.rollup) {
8002
                output += "\n" + (i+1) + ": " + message.type;
8003
                output += "\n" + message.message;
8004
            } else {
8005
                output += "\n" + (i+1) + ": " + message.type + " at line " + message.line + ", col " + message.col;
8006
                output += "\n" + message.message;
8007
                output += "\n" + message.evidence;
8008
            }
8009
        });
8010

    
8011
        return output;
8012
    }
8013
});
8014

    
8015
module.exports.CSSLint = CSSLint;
8016

    
8017
});
8018

    
8019
ace.define("ace/mode/css_worker",[], function(require, exports, module) {
8020
"use strict";
8021

    
8022
var oop = require("../lib/oop");
8023
var lang = require("../lib/lang");
8024
var Mirror = require("../worker/mirror").Mirror;
8025
var CSSLint = require("./css/csslint").CSSLint;
8026

    
8027
var Worker = exports.Worker = function(sender) {
8028
    Mirror.call(this, sender);
8029
    this.setTimeout(400);
8030
    this.ruleset = null;
8031
    this.setDisabledRules("ids|order-alphabetical");
8032
    this.setInfoRules(
8033
      "adjoining-classes|qualified-headings|zero-units|gradients|" +
8034
      "import|outline-none|vendor-prefix"
8035
    );
8036
};
8037

    
8038
oop.inherits(Worker, Mirror);
8039

    
8040
(function() {
8041
    this.setInfoRules = function(ruleNames) {
8042
        if (typeof ruleNames == "string")
8043
            ruleNames = ruleNames.split("|");
8044
        this.infoRules = lang.arrayToMap(ruleNames);
8045
        this.doc.getValue() && this.deferredUpdate.schedule(100);
8046
    };
8047

    
8048
    this.setDisabledRules = function(ruleNames) {
8049
        if (!ruleNames) {
8050
            this.ruleset = null;
8051
        } else {
8052
            if (typeof ruleNames == "string")
8053
                ruleNames = ruleNames.split("|");
8054
            var all = {};
8055

    
8056
            CSSLint.getRules().forEach(function(x){
8057
                all[x.id] = true;
8058
            });
8059
            ruleNames.forEach(function(x) {
8060
                delete all[x];
8061
            });
8062
            
8063
            this.ruleset = all;
8064
        }
8065
        this.doc.getValue() && this.deferredUpdate.schedule(100);
8066
    };
8067

    
8068
    this.onUpdate = function() {
8069
        var value = this.doc.getValue();
8070
        if (!value)
8071
            return this.sender.emit("annotate", []);
8072
        var infoRules = this.infoRules;
8073

    
8074
        var result = CSSLint.verify(value, this.ruleset);
8075
        this.sender.emit("annotate", result.messages.map(function(msg) {
8076
            return {
8077
                row: msg.line - 1,
8078
                column: msg.col - 1,
8079
                text: msg.message,
8080
                type: infoRules[msg.rule.id] ? "info" : msg.type,
8081
                rule: msg.rule.name
8082
            };
8083
        }));
8084
    };
8085

    
8086
}).call(Worker.prototype);
8087

    
8088
});
8089

    
8090
ace.define("ace/lib/es5-shim",[], function(require, exports, module) {
8091

    
8092
function Empty() {}
8093

    
8094
if (!Function.prototype.bind) {
8095
    Function.prototype.bind = function bind(that) { // .length is 1
8096
        var target = this;
8097
        if (typeof target != "function") {
8098
            throw new TypeError("Function.prototype.bind called on incompatible " + target);
8099
        }
8100
        var args = slice.call(arguments, 1); // for normal call
8101
        var bound = function () {
8102

    
8103
            if (this instanceof bound) {
8104

    
8105
                var result = target.apply(
8106
                    this,
8107
                    args.concat(slice.call(arguments))
8108
                );
8109
                if (Object(result) === result) {
8110
                    return result;
8111
                }
8112
                return this;
8113

    
8114
            } else {
8115
                return target.apply(
8116
                    that,
8117
                    args.concat(slice.call(arguments))
8118
                );
8119

    
8120
            }
8121

    
8122
        };
8123
        if(target.prototype) {
8124
            Empty.prototype = target.prototype;
8125
            bound.prototype = new Empty();
8126
            Empty.prototype = null;
8127
        }
8128
        return bound;
8129
    };
8130
}
8131
var call = Function.prototype.call;
8132
var prototypeOfArray = Array.prototype;
8133
var prototypeOfObject = Object.prototype;
8134
var slice = prototypeOfArray.slice;
8135
var _toString = call.bind(prototypeOfObject.toString);
8136
var owns = call.bind(prototypeOfObject.hasOwnProperty);
8137
var defineGetter;
8138
var defineSetter;
8139
var lookupGetter;
8140
var lookupSetter;
8141
var supportsAccessors;
8142
if ((supportsAccessors = owns(prototypeOfObject, "__defineGetter__"))) {
8143
    defineGetter = call.bind(prototypeOfObject.__defineGetter__);
8144
    defineSetter = call.bind(prototypeOfObject.__defineSetter__);
8145
    lookupGetter = call.bind(prototypeOfObject.__lookupGetter__);
8146
    lookupSetter = call.bind(prototypeOfObject.__lookupSetter__);
8147
}
8148
if ([1,2].splice(0).length != 2) {
8149
    if(function() { // test IE < 9 to splice bug - see issue #138
8150
        function makeArray(l) {
8151
            var a = new Array(l+2);
8152
            a[0] = a[1] = 0;
8153
            return a;
8154
        }
8155
        var array = [], lengthBefore;
8156
        
8157
        array.splice.apply(array, makeArray(20));
8158
        array.splice.apply(array, makeArray(26));
8159

    
8160
        lengthBefore = array.length; //46
8161
        array.splice(5, 0, "XXX"); // add one element
8162

    
8163
        lengthBefore + 1 == array.length
8164

    
8165
        if (lengthBefore + 1 == array.length) {
8166
            return true;// has right splice implementation without bugs
8167
        }
8168
    }()) {//IE 6/7
8169
        var array_splice = Array.prototype.splice;
8170
        Array.prototype.splice = function(start, deleteCount) {
8171
            if (!arguments.length) {
8172
                return [];
8173
            } else {
8174
                return array_splice.apply(this, [
8175
                    start === void 0 ? 0 : start,
8176
                    deleteCount === void 0 ? (this.length - start) : deleteCount
8177
                ].concat(slice.call(arguments, 2)))
8178
            }
8179
        };
8180
    } else {//IE8
8181
        Array.prototype.splice = function(pos, removeCount){
8182
            var length = this.length;
8183
            if (pos > 0) {
8184
                if (pos > length)
8185
                    pos = length;
8186
            } else if (pos == void 0) {
8187
                pos = 0;
8188
            } else if (pos < 0) {
8189
                pos = Math.max(length + pos, 0);
8190
            }
8191

    
8192
            if (!(pos+removeCount < length))
8193
                removeCount = length - pos;
8194

    
8195
            var removed = this.slice(pos, pos+removeCount);
8196
            var insert = slice.call(arguments, 2);
8197
            var add = insert.length;            
8198
            if (pos === length) {
8199
                if (add) {
8200
                    this.push.apply(this, insert);
8201
                }
8202
            } else {
8203
                var remove = Math.min(removeCount, length - pos);
8204
                var tailOldPos = pos + remove;
8205
                var tailNewPos = tailOldPos + add - remove;
8206
                var tailCount = length - tailOldPos;
8207
                var lengthAfterRemove = length - remove;
8208

    
8209
                if (tailNewPos < tailOldPos) { // case A
8210
                    for (var i = 0; i < tailCount; ++i) {
8211
                        this[tailNewPos+i] = this[tailOldPos+i];
8212
                    }
8213
                } else if (tailNewPos > tailOldPos) { // case B
8214
                    for (i = tailCount; i--; ) {
8215
                        this[tailNewPos+i] = this[tailOldPos+i];
8216
                    }
8217
                } // else, add == remove (nothing to do)
8218

    
8219
                if (add && pos === lengthAfterRemove) {
8220
                    this.length = lengthAfterRemove; // truncate array
8221
                    this.push.apply(this, insert);
8222
                } else {
8223
                    this.length = lengthAfterRemove + add; // reserves space
8224
                    for (i = 0; i < add; ++i) {
8225
                        this[pos+i] = insert[i];
8226
                    }
8227
                }
8228
            }
8229
            return removed;
8230
        };
8231
    }
8232
}
8233
if (!Array.isArray) {
8234
    Array.isArray = function isArray(obj) {
8235
        return _toString(obj) == "[object Array]";
8236
    };
8237
}
8238
var boxedString = Object("a"),
8239
    splitString = boxedString[0] != "a" || !(0 in boxedString);
8240

    
8241
if (!Array.prototype.forEach) {
8242
    Array.prototype.forEach = function forEach(fun /*, thisp*/) {
8243
        var object = toObject(this),
8244
            self = splitString && _toString(this) == "[object String]" ?
8245
                this.split("") :
8246
                object,
8247
            thisp = arguments[1],
8248
            i = -1,
8249
            length = self.length >>> 0;
8250
        if (_toString(fun) != "[object Function]") {
8251
            throw new TypeError(); // TODO message
8252
        }
8253

    
8254
        while (++i < length) {
8255
            if (i in self) {
8256
                fun.call(thisp, self[i], i, object);
8257
            }
8258
        }
8259
    };
8260
}
8261
if (!Array.prototype.map) {
8262
    Array.prototype.map = function map(fun /*, thisp*/) {
8263
        var object = toObject(this),
8264
            self = splitString && _toString(this) == "[object String]" ?
8265
                this.split("") :
8266
                object,
8267
            length = self.length >>> 0,
8268
            result = Array(length),
8269
            thisp = arguments[1];
8270
        if (_toString(fun) != "[object Function]") {
8271
            throw new TypeError(fun + " is not a function");
8272
        }
8273

    
8274
        for (var i = 0; i < length; i++) {
8275
            if (i in self)
8276
                result[i] = fun.call(thisp, self[i], i, object);
8277
        }
8278
        return result;
8279
    };
8280
}
8281
if (!Array.prototype.filter) {
8282
    Array.prototype.filter = function filter(fun /*, thisp */) {
8283
        var object = toObject(this),
8284
            self = splitString && _toString(this) == "[object String]" ?
8285
                this.split("") :
8286
                    object,
8287
            length = self.length >>> 0,
8288
            result = [],
8289
            value,
8290
            thisp = arguments[1];
8291
        if (_toString(fun) != "[object Function]") {
8292
            throw new TypeError(fun + " is not a function");
8293
        }
8294

    
8295
        for (var i = 0; i < length; i++) {
8296
            if (i in self) {
8297
                value = self[i];
8298
                if (fun.call(thisp, value, i, object)) {
8299
                    result.push(value);
8300
                }
8301
            }
8302
        }
8303
        return result;
8304
    };
8305
}
8306
if (!Array.prototype.every) {
8307
    Array.prototype.every = function every(fun /*, thisp */) {
8308
        var object = toObject(this),
8309
            self = splitString && _toString(this) == "[object String]" ?
8310
                this.split("") :
8311
                object,
8312
            length = self.length >>> 0,
8313
            thisp = arguments[1];
8314
        if (_toString(fun) != "[object Function]") {
8315
            throw new TypeError(fun + " is not a function");
8316
        }
8317

    
8318
        for (var i = 0; i < length; i++) {
8319
            if (i in self && !fun.call(thisp, self[i], i, object)) {
8320
                return false;
8321
            }
8322
        }
8323
        return true;
8324
    };
8325
}
8326
if (!Array.prototype.some) {
8327
    Array.prototype.some = function some(fun /*, thisp */) {
8328
        var object = toObject(this),
8329
            self = splitString && _toString(this) == "[object String]" ?
8330
                this.split("") :
8331
                object,
8332
            length = self.length >>> 0,
8333
            thisp = arguments[1];
8334
        if (_toString(fun) != "[object Function]") {
8335
            throw new TypeError(fun + " is not a function");
8336
        }
8337

    
8338
        for (var i = 0; i < length; i++) {
8339
            if (i in self && fun.call(thisp, self[i], i, object)) {
8340
                return true;
8341
            }
8342
        }
8343
        return false;
8344
    };
8345
}
8346
if (!Array.prototype.reduce) {
8347
    Array.prototype.reduce = function reduce(fun /*, initial*/) {
8348
        var object = toObject(this),
8349
            self = splitString && _toString(this) == "[object String]" ?
8350
                this.split("") :
8351
                object,
8352
            length = self.length >>> 0;
8353
        if (_toString(fun) != "[object Function]") {
8354
            throw new TypeError(fun + " is not a function");
8355
        }
8356
        if (!length && arguments.length == 1) {
8357
            throw new TypeError("reduce of empty array with no initial value");
8358
        }
8359

    
8360
        var i = 0;
8361
        var result;
8362
        if (arguments.length >= 2) {
8363
            result = arguments[1];
8364
        } else {
8365
            do {
8366
                if (i in self) {
8367
                    result = self[i++];
8368
                    break;
8369
                }
8370
                if (++i >= length) {
8371
                    throw new TypeError("reduce of empty array with no initial value");
8372
                }
8373
            } while (true);
8374
        }
8375

    
8376
        for (; i < length; i++) {
8377
            if (i in self) {
8378
                result = fun.call(void 0, result, self[i], i, object);
8379
            }
8380
        }
8381

    
8382
        return result;
8383
    };
8384
}
8385
if (!Array.prototype.reduceRight) {
8386
    Array.prototype.reduceRight = function reduceRight(fun /*, initial*/) {
8387
        var object = toObject(this),
8388
            self = splitString && _toString(this) == "[object String]" ?
8389
                this.split("") :
8390
                object,
8391
            length = self.length >>> 0;
8392
        if (_toString(fun) != "[object Function]") {
8393
            throw new TypeError(fun + " is not a function");
8394
        }
8395
        if (!length && arguments.length == 1) {
8396
            throw new TypeError("reduceRight of empty array with no initial value");
8397
        }
8398

    
8399
        var result, i = length - 1;
8400
        if (arguments.length >= 2) {
8401
            result = arguments[1];
8402
        } else {
8403
            do {
8404
                if (i in self) {
8405
                    result = self[i--];
8406
                    break;
8407
                }
8408
                if (--i < 0) {
8409
                    throw new TypeError("reduceRight of empty array with no initial value");
8410
                }
8411
            } while (true);
8412
        }
8413

    
8414
        do {
8415
            if (i in this) {
8416
                result = fun.call(void 0, result, self[i], i, object);
8417
            }
8418
        } while (i--);
8419

    
8420
        return result;
8421
    };
8422
}
8423
if (!Array.prototype.indexOf || ([0, 1].indexOf(1, 2) != -1)) {
8424
    Array.prototype.indexOf = function indexOf(sought /*, fromIndex */ ) {
8425
        var self = splitString && _toString(this) == "[object String]" ?
8426
                this.split("") :
8427
                toObject(this),
8428
            length = self.length >>> 0;
8429

    
8430
        if (!length) {
8431
            return -1;
8432
        }
8433

    
8434
        var i = 0;
8435
        if (arguments.length > 1) {
8436
            i = toInteger(arguments[1]);
8437
        }
8438
        i = i >= 0 ? i : Math.max(0, length + i);
8439
        for (; i < length; i++) {
8440
            if (i in self && self[i] === sought) {
8441
                return i;
8442
            }
8443
        }
8444
        return -1;
8445
    };
8446
}
8447
if (!Array.prototype.lastIndexOf || ([0, 1].lastIndexOf(0, -3) != -1)) {
8448
    Array.prototype.lastIndexOf = function lastIndexOf(sought /*, fromIndex */) {
8449
        var self = splitString && _toString(this) == "[object String]" ?
8450
                this.split("") :
8451
                toObject(this),
8452
            length = self.length >>> 0;
8453

    
8454
        if (!length) {
8455
            return -1;
8456
        }
8457
        var i = length - 1;
8458
        if (arguments.length > 1) {
8459
            i = Math.min(i, toInteger(arguments[1]));
8460
        }
8461
        i = i >= 0 ? i : length - Math.abs(i);
8462
        for (; i >= 0; i--) {
8463
            if (i in self && sought === self[i]) {
8464
                return i;
8465
            }
8466
        }
8467
        return -1;
8468
    };
8469
}
8470
if (!Object.getPrototypeOf) {
8471
    Object.getPrototypeOf = function getPrototypeOf(object) {
8472
        return object.__proto__ || (
8473
            object.constructor ?
8474
            object.constructor.prototype :
8475
            prototypeOfObject
8476
        );
8477
    };
8478
}
8479
if (!Object.getOwnPropertyDescriptor) {
8480
    var ERR_NON_OBJECT = "Object.getOwnPropertyDescriptor called on a " +
8481
                         "non-object: ";
8482
    Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(object, property) {
8483
        if ((typeof object != "object" && typeof object != "function") || object === null)
8484
            throw new TypeError(ERR_NON_OBJECT + object);
8485
        if (!owns(object, property))
8486
            return;
8487

    
8488
        var descriptor, getter, setter;
8489
        descriptor =  { enumerable: true, configurable: true };
8490
        if (supportsAccessors) {
8491
            var prototype = object.__proto__;
8492
            object.__proto__ = prototypeOfObject;
8493

    
8494
            var getter = lookupGetter(object, property);
8495
            var setter = lookupSetter(object, property);
8496
            object.__proto__ = prototype;
8497

    
8498
            if (getter || setter) {
8499
                if (getter) descriptor.get = getter;
8500
                if (setter) descriptor.set = setter;
8501
                return descriptor;
8502
            }
8503
        }
8504
        descriptor.value = object[property];
8505
        return descriptor;
8506
    };
8507
}
8508
if (!Object.getOwnPropertyNames) {
8509
    Object.getOwnPropertyNames = function getOwnPropertyNames(object) {
8510
        return Object.keys(object);
8511
    };
8512
}
8513
if (!Object.create) {
8514
    var createEmpty;
8515
    if (Object.prototype.__proto__ === null) {
8516
        createEmpty = function () {
8517
            return { "__proto__": null };
8518
        };
8519
    } else {
8520
        createEmpty = function () {
8521
            var empty = {};
8522
            for (var i in empty)
8523
                empty[i] = null;
8524
            empty.constructor =
8525
            empty.hasOwnProperty =
8526
            empty.propertyIsEnumerable =
8527
            empty.isPrototypeOf =
8528
            empty.toLocaleString =
8529
            empty.toString =
8530
            empty.valueOf =
8531
            empty.__proto__ = null;
8532
            return empty;
8533
        }
8534
    }
8535

    
8536
    Object.create = function create(prototype, properties) {
8537
        var object;
8538
        if (prototype === null) {
8539
            object = createEmpty();
8540
        } else {
8541
            if (typeof prototype != "object")
8542
                throw new TypeError("typeof prototype["+(typeof prototype)+"] != 'object'");
8543
            var Type = function () {};
8544
            Type.prototype = prototype;
8545
            object = new Type();
8546
            object.__proto__ = prototype;
8547
        }
8548
        if (properties !== void 0)
8549
            Object.defineProperties(object, properties);
8550
        return object;
8551
    };
8552
}
8553

    
8554
function doesDefinePropertyWork(object) {
8555
    try {
8556
        Object.defineProperty(object, "sentinel", {});
8557
        return "sentinel" in object;
8558
    } catch (exception) {
8559
    }
8560
}
8561
if (Object.defineProperty) {
8562
    var definePropertyWorksOnObject = doesDefinePropertyWork({});
8563
    var definePropertyWorksOnDom = typeof document == "undefined" ||
8564
        doesDefinePropertyWork(document.createElement("div"));
8565
    if (!definePropertyWorksOnObject || !definePropertyWorksOnDom) {
8566
        var definePropertyFallback = Object.defineProperty;
8567
    }
8568
}
8569

    
8570
if (!Object.defineProperty || definePropertyFallback) {
8571
    var ERR_NON_OBJECT_DESCRIPTOR = "Property description must be an object: ";
8572
    var ERR_NON_OBJECT_TARGET = "Object.defineProperty called on non-object: "
8573
    var ERR_ACCESSORS_NOT_SUPPORTED = "getters & setters can not be defined " +
8574
                                      "on this javascript engine";
8575

    
8576
    Object.defineProperty = function defineProperty(object, property, descriptor) {
8577
        if ((typeof object != "object" && typeof object != "function") || object === null)
8578
            throw new TypeError(ERR_NON_OBJECT_TARGET + object);
8579
        if ((typeof descriptor != "object" && typeof descriptor != "function") || descriptor === null)
8580
            throw new TypeError(ERR_NON_OBJECT_DESCRIPTOR + descriptor);
8581
        if (definePropertyFallback) {
8582
            try {
8583
                return definePropertyFallback.call(Object, object, property, descriptor);
8584
            } catch (exception) {
8585
            }
8586
        }
8587
        if (owns(descriptor, "value")) {
8588

    
8589
            if (supportsAccessors && (lookupGetter(object, property) ||
8590
                                      lookupSetter(object, property)))
8591
            {
8592
                var prototype = object.__proto__;
8593
                object.__proto__ = prototypeOfObject;
8594
                delete object[property];
8595
                object[property] = descriptor.value;
8596
                object.__proto__ = prototype;
8597
            } else {
8598
                object[property] = descriptor.value;
8599
            }
8600
        } else {
8601
            if (!supportsAccessors)
8602
                throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
8603
            if (owns(descriptor, "get"))
8604
                defineGetter(object, property, descriptor.get);
8605
            if (owns(descriptor, "set"))
8606
                defineSetter(object, property, descriptor.set);
8607
        }
8608

    
8609
        return object;
8610
    };
8611
}
8612
if (!Object.defineProperties) {
8613
    Object.defineProperties = function defineProperties(object, properties) {
8614
        for (var property in properties) {
8615
            if (owns(properties, property))
8616
                Object.defineProperty(object, property, properties[property]);
8617
        }
8618
        return object;
8619
    };
8620
}
8621
if (!Object.seal) {
8622
    Object.seal = function seal(object) {
8623
        return object;
8624
    };
8625
}
8626
if (!Object.freeze) {
8627
    Object.freeze = function freeze(object) {
8628
        return object;
8629
    };
8630
}
8631
try {
8632
    Object.freeze(function () {});
8633
} catch (exception) {
8634
    Object.freeze = (function freeze(freezeObject) {
8635
        return function freeze(object) {
8636
            if (typeof object == "function") {
8637
                return object;
8638
            } else {
8639
                return freezeObject(object);
8640
            }
8641
        };
8642
    })(Object.freeze);
8643
}
8644
if (!Object.preventExtensions) {
8645
    Object.preventExtensions = function preventExtensions(object) {
8646
        return object;
8647
    };
8648
}
8649
if (!Object.isSealed) {
8650
    Object.isSealed = function isSealed(object) {
8651
        return false;
8652
    };
8653
}
8654
if (!Object.isFrozen) {
8655
    Object.isFrozen = function isFrozen(object) {
8656
        return false;
8657
    };
8658
}
8659
if (!Object.isExtensible) {
8660
    Object.isExtensible = function isExtensible(object) {
8661
        if (Object(object) === object) {
8662
            throw new TypeError(); // TODO message
8663
        }
8664
        var name = '';
8665
        while (owns(object, name)) {
8666
            name += '?';
8667
        }
8668
        object[name] = true;
8669
        var returnValue = owns(object, name);
8670
        delete object[name];
8671
        return returnValue;
8672
    };
8673
}
8674
if (!Object.keys) {
8675
    var hasDontEnumBug = true,
8676
        dontEnums = [
8677
            "toString",
8678
            "toLocaleString",
8679
            "valueOf",
8680
            "hasOwnProperty",
8681
            "isPrototypeOf",
8682
            "propertyIsEnumerable",
8683
            "constructor"
8684
        ],
8685
        dontEnumsLength = dontEnums.length;
8686

    
8687
    for (var key in {"toString": null}) {
8688
        hasDontEnumBug = false;
8689
    }
8690

    
8691
    Object.keys = function keys(object) {
8692

    
8693
        if (
8694
            (typeof object != "object" && typeof object != "function") ||
8695
            object === null
8696
        ) {
8697
            throw new TypeError("Object.keys called on a non-object");
8698
        }
8699

    
8700
        var keys = [];
8701
        for (var name in object) {
8702
            if (owns(object, name)) {
8703
                keys.push(name);
8704
            }
8705
        }
8706

    
8707
        if (hasDontEnumBug) {
8708
            for (var i = 0, ii = dontEnumsLength; i < ii; i++) {
8709
                var dontEnum = dontEnums[i];
8710
                if (owns(object, dontEnum)) {
8711
                    keys.push(dontEnum);
8712
                }
8713
            }
8714
        }
8715
        return keys;
8716
    };
8717

    
8718
}
8719
if (!Date.now) {
8720
    Date.now = function now() {
8721
        return new Date().getTime();
8722
    };
8723
}
8724
var ws = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u2000\u2001\u2002\u2003" +
8725
    "\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028" +
8726
    "\u2029\uFEFF";
8727
if (!String.prototype.trim) {
8728
    ws = "[" + ws + "]";
8729
    var trimBeginRegexp = new RegExp("^" + ws + ws + "*"),
8730
        trimEndRegexp = new RegExp(ws + ws + "*$");
8731
    String.prototype.trim = function trim() {
8732
        return String(this).replace(trimBeginRegexp, "").replace(trimEndRegexp, "");
8733
    };
8734
}
8735

    
8736
function toInteger(n) {
8737
    n = +n;
8738
    if (n !== n) { // isNaN
8739
        n = 0;
8740
    } else if (n !== 0 && n !== (1/0) && n !== -(1/0)) {
8741
        n = (n > 0 || -1) * Math.floor(Math.abs(n));
8742
    }
8743
    return n;
8744
}
8745

    
8746
function isPrimitive(input) {
8747
    var type = typeof input;
8748
    return (
8749
        input === null ||
8750
        type === "undefined" ||
8751
        type === "boolean" ||
8752
        type === "number" ||
8753
        type === "string"
8754
    );
8755
}
8756

    
8757
function toPrimitive(input) {
8758
    var val, valueOf, toString;
8759
    if (isPrimitive(input)) {
8760
        return input;
8761
    }
8762
    valueOf = input.valueOf;
8763
    if (typeof valueOf === "function") {
8764
        val = valueOf.call(input);
8765
        if (isPrimitive(val)) {
8766
            return val;
8767
        }
8768
    }
8769
    toString = input.toString;
8770
    if (typeof toString === "function") {
8771
        val = toString.call(input);
8772
        if (isPrimitive(val)) {
8773
            return val;
8774
        }
8775
    }
8776
    throw new TypeError();
8777
}
8778
var toObject = function (o) {
8779
    if (o == null) { // this matches both null and undefined
8780
        throw new TypeError("can't convert "+o+" to object");
8781
    }
8782
    return Object(o);
8783
};
8784

    
8785
});
(237-237/244)