/lenasys/trunk

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/lenasys/trunk
15.1.1 by galaxyAbstractor
Started implementation of a new codeviewer using Ace
1
"no use strict";
2
;(function(window) {
3
if (typeof window.window != "undefined" && window.document) {
4
    return;
5
}
6
7
window.console = {
8
    log: function() {
9
        var msgs = Array.prototype.slice.call(arguments, 0);
10
        postMessage({type: "log", data: msgs});
11
    },
12
    error: function() {
13
        var msgs = Array.prototype.slice.call(arguments, 0);
14
        postMessage({type: "log", data: msgs});
15
    }
16
};
17
window.window = window;
18
window.ace = window;
19
20
window.normalizeModule = function(parentId, moduleName) {
21
    // normalize plugin requires
22
    if (moduleName.indexOf("!") !== -1) {
23
        var chunks = moduleName.split("!");
24
        return normalizeModule(parentId, chunks[0]) + "!" + normalizeModule(parentId, chunks[1]);
25
    }
26
    // normalize relative requires
27
    if (moduleName.charAt(0) == ".") {
28
        var base = parentId.split("/").slice(0, -1).join("/");
29
        moduleName = base + "/" + moduleName;
30
        
31
        while(moduleName.indexOf(".") !== -1 && previous != moduleName) {
32
            var previous = moduleName;
33
            moduleName = moduleName.replace(/\/\.\//, "/").replace(/[^\/]+\/\.\.\//, "");
34
        }
35
    }
36
    
37
    return moduleName;
38
};
39
40
window.require = function(parentId, id) {
41
    if (!id.charAt)
42
        throw new Error("worker.js require() accepts only (parentId, id) as arguments");
43
44
    id = normalizeModule(parentId, id);
45
46
    var module = require.modules[id];
47
    if (module) {
48
        if (!module.initialized) {
49
            module.initialized = true;
50
            module.exports = module.factory().exports;
51
        }
52
        return module.exports;
53
    }
54
    
55
    var chunks = id.split("/");
56
    chunks[0] = require.tlns[chunks[0]] || chunks[0];
57
    var path = chunks.join("/") + ".js";
58
    
59
    require.id = id;
60
    importScripts(path);
61
    return require(parentId, id);    
62
};
63
64
require.modules = {};
65
require.tlns = {};
66
67
window.define = function(id, deps, factory) {
68
    if (arguments.length == 2) {
69
        factory = deps;
70
        if (typeof id != "string") {
71
            deps = id;
72
            id = require.id;
73
        }
74
    } else if (arguments.length == 1) {
75
        factory = id;
76
        id = require.id;
77
    }
78
79
    if (id.indexOf("text!") === 0) 
80
        return;
81
    
82
    var req = function(deps, factory) {
83
        return require(id, deps, factory);
84
    };
85
86
    require.modules[id] = {
87
        factory: function() {
88
            var module = {
89
                exports: {}
90
            };
91
            var returnExports = factory(req, module.exports, module);
92
            if (returnExports)
93
                module.exports = returnExports;
94
            return module;
95
        }
96
    };
97
};
98
99
window.initBaseUrls  = function initBaseUrls(topLevelNamespaces) {
100
    require.tlns = topLevelNamespaces;
101
}
102
103
window.initSender = function initSender() {
104
105
    var EventEmitter = require(null, "ace/lib/event_emitter").EventEmitter;
106
    var oop = require(null, "ace/lib/oop");
107
    
108
    var Sender = function() {};
109
    
110
    (function() {
111
        
112
        oop.implement(this, EventEmitter);
113
                
114
        this.callback = function(data, callbackId) {
115
            postMessage({
116
                type: "call",
117
                id: callbackId,
118
                data: data
119
            });
120
        };
121
    
122
        this.emit = function(name, data) {
123
            postMessage({
124
                type: "event",
125
                name: name,
126
                data: data
127
            });
128
        };
129
        
130
    }).call(Sender.prototype);
131
    
132
    return new Sender();
133
}
134
135
window.main = null;
136
window.sender = null;
137
138
window.onmessage = function(e) {
139
    var msg = e.data;
140
    if (msg.command) {
141
        if (main[msg.command])
142
            main[msg.command].apply(main, msg.args);
143
        else
144
            throw new Error("Unknown command:" + msg.command);
145
    }
146
    else if (msg.init) {        
147
        initBaseUrls(msg.tlns);
148
        require(null, "ace/lib/fixoldbrowsers");
149
        sender = initSender();
150
        var clazz = require(null, msg.module)[msg.classname];
151
        main = new clazz(sender);
152
    } 
153
    else if (msg.event && sender) {
154
        sender._emit(msg.event, msg.data);
155
    }
156
};
157
})(this);// vim:set ts=4 sts=4 sw=4 st:
158
159
define('ace/lib/fixoldbrowsers', ['require', 'exports', 'module' , 'ace/lib/regexp', 'ace/lib/es5-shim'], function(require, exports, module) {
160
161
162
require("./regexp");
163
require("./es5-shim");
164
165
});
166
 
167
define('ace/lib/regexp', ['require', 'exports', 'module' ], function(require, exports, module) {
168
169
    var real = {
170
            exec: RegExp.prototype.exec,
171
            test: RegExp.prototype.test,
172
            match: String.prototype.match,
173
            replace: String.prototype.replace,
174
            split: String.prototype.split
175
        },
176
        compliantExecNpcg = real.exec.call(/()??/, "")[1] === undefined, // check `exec` handling of nonparticipating capturing groups
177
        compliantLastIndexIncrement = function () {
178
            var x = /^/g;
179
            real.test.call(x, "");
180
            return !x.lastIndex;
181
        }();
182
183
    if (compliantLastIndexIncrement && compliantExecNpcg)
184
        return;
185
    RegExp.prototype.exec = function (str) {
186
        var match = real.exec.apply(this, arguments),
187
            name, r2;
188
        if ( typeof(str) == 'string' && match) {
189
            if (!compliantExecNpcg && match.length > 1 && indexOf(match, "") > -1) {
190
                r2 = RegExp(this.source, real.replace.call(getNativeFlags(this), "g", ""));
191
                real.replace.call(str.slice(match.index), r2, function () {
192
                    for (var i = 1; i < arguments.length - 2; i++) {
193
                        if (arguments[i] === undefined)
194
                            match[i] = undefined;
195
                    }
196
                });
197
            }
198
            if (this._xregexp && this._xregexp.captureNames) {
199
                for (var i = 1; i < match.length; i++) {
200
                    name = this._xregexp.captureNames[i - 1];
201
                    if (name)
202
                       match[name] = match[i];
203
                }
204
            }
205
            if (!compliantLastIndexIncrement && this.global && !match[0].length && (this.lastIndex > match.index))
206
                this.lastIndex--;
207
        }
208
        return match;
209
    };
210
    if (!compliantLastIndexIncrement) {
211
        RegExp.prototype.test = function (str) {
212
            var match = real.exec.call(this, str);
213
            if (match && this.global && !match[0].length && (this.lastIndex > match.index))
214
                this.lastIndex--;
215
            return !!match;
216
        };
217
    }
218
219
    function getNativeFlags (regex) {
220
        return (regex.global     ? "g" : "") +
221
               (regex.ignoreCase ? "i" : "") +
222
               (regex.multiline  ? "m" : "") +
223
               (regex.extended   ? "x" : "") + // Proposed for ES4; included in AS3
224
               (regex.sticky     ? "y" : "");
225
    }
226
227
    function indexOf (array, item, from) {
228
        if (Array.prototype.indexOf) // Use the native array method if available
229
            return array.indexOf(item, from);
230
        for (var i = from || 0; i < array.length; i++) {
231
            if (array[i] === item)
232
                return i;
233
        }
234
        return -1;
235
    }
236
237
});
238
239
define('ace/lib/es5-shim', ['require', 'exports', 'module' ], function(require, exports, module) {
240
241
function Empty() {}
242
243
if (!Function.prototype.bind) {
244
    Function.prototype.bind = function bind(that) { // .length is 1
245
        var target = this;
246
        if (typeof target != "function") {
247
            throw new TypeError("Function.prototype.bind called on incompatible " + target);
248
        }
249
        var args = slice.call(arguments, 1); // for normal call
250
        var bound = function () {
251
252
            if (this instanceof bound) {
253
254
                var result = target.apply(
255
                    this,
256
                    args.concat(slice.call(arguments))
257
                );
258
                if (Object(result) === result) {
259
                    return result;
260
                }
261
                return this;
262
263
            } else {
264
                return target.apply(
265
                    that,
266
                    args.concat(slice.call(arguments))
267
                );
268
269
            }
270
271
        };
272
        if(target.prototype) {
273
            Empty.prototype = target.prototype;
274
            bound.prototype = new Empty();
275
            Empty.prototype = null;
276
        }
277
        return bound;
278
    };
279
}
280
var call = Function.prototype.call;
281
var prototypeOfArray = Array.prototype;
282
var prototypeOfObject = Object.prototype;
283
var slice = prototypeOfArray.slice;
284
var _toString = call.bind(prototypeOfObject.toString);
285
var owns = call.bind(prototypeOfObject.hasOwnProperty);
286
var defineGetter;
287
var defineSetter;
288
var lookupGetter;
289
var lookupSetter;
290
var supportsAccessors;
291
if ((supportsAccessors = owns(prototypeOfObject, "__defineGetter__"))) {
292
    defineGetter = call.bind(prototypeOfObject.__defineGetter__);
293
    defineSetter = call.bind(prototypeOfObject.__defineSetter__);
294
    lookupGetter = call.bind(prototypeOfObject.__lookupGetter__);
295
    lookupSetter = call.bind(prototypeOfObject.__lookupSetter__);
296
}
297
if ([1,2].splice(0).length != 2) {
298
    if(function() { // test IE < 9 to splice bug - see issue #138
299
        function makeArray(l) {
300
            var a = new Array(l+2);
301
            a[0] = a[1] = 0;
302
            return a;
303
        }
304
        var array = [], lengthBefore;
305
        
306
        array.splice.apply(array, makeArray(20));
307
        array.splice.apply(array, makeArray(26));
308
309
        lengthBefore = array.length; //46
310
        array.splice(5, 0, "XXX"); // add one element
311
312
        lengthBefore + 1 == array.length
313
314
        if (lengthBefore + 1 == array.length) {
315
            return true;// has right splice implementation without bugs
316
        }
317
    }()) {//IE 6/7
318
        var array_splice = Array.prototype.splice;
319
        Array.prototype.splice = function(start, deleteCount) {
320
            if (!arguments.length) {
321
                return [];
322
            } else {
323
                return array_splice.apply(this, [
324
                    start === void 0 ? 0 : start,
325
                    deleteCount === void 0 ? (this.length - start) : deleteCount
326
                ].concat(slice.call(arguments, 2)))
327
            }
328
        };
329
    } else {//IE8
330
        Array.prototype.splice = function(pos, removeCount){
331
            var length = this.length;
332
            if (pos > 0) {
333
                if (pos > length)
334
                    pos = length;
335
            } else if (pos == void 0) {
336
                pos = 0;
337
            } else if (pos < 0) {
338
                pos = Math.max(length + pos, 0);
339
            }
340
341
            if (!(pos+removeCount < length))
342
                removeCount = length - pos;
343
344
            var removed = this.slice(pos, pos+removeCount);
345
            var insert = slice.call(arguments, 2);
346
            var add = insert.length;            
347
            if (pos === length) {
348
                if (add) {
349
                    this.push.apply(this, insert);
350
                }
351
            } else {
352
                var remove = Math.min(removeCount, length - pos);
353
                var tailOldPos = pos + remove;
354
                var tailNewPos = tailOldPos + add - remove;
355
                var tailCount = length - tailOldPos;
356
                var lengthAfterRemove = length - remove;
357
358
                if (tailNewPos < tailOldPos) { // case A
359
                    for (var i = 0; i < tailCount; ++i) {
360
                        this[tailNewPos+i] = this[tailOldPos+i];
361
                    }
362
                } else if (tailNewPos > tailOldPos) { // case B
363
                    for (i = tailCount; i--; ) {
364
                        this[tailNewPos+i] = this[tailOldPos+i];
365
                    }
366
                } // else, add == remove (nothing to do)
367
368
                if (add && pos === lengthAfterRemove) {
369
                    this.length = lengthAfterRemove; // truncate array
370
                    this.push.apply(this, insert);
371
                } else {
372
                    this.length = lengthAfterRemove + add; // reserves space
373
                    for (i = 0; i < add; ++i) {
374
                        this[pos+i] = insert[i];
375
                    }
376
                }
377
            }
378
            return removed;
379
        };
380
    }
381
}
382
if (!Array.isArray) {
383
    Array.isArray = function isArray(obj) {
384
        return _toString(obj) == "[object Array]";
385
    };
386
}
387
var boxedString = Object("a"),
388
    splitString = boxedString[0] != "a" || !(0 in boxedString);
389
390
if (!Array.prototype.forEach) {
391
    Array.prototype.forEach = function forEach(fun /*, thisp*/) {
392
        var object = toObject(this),
393
            self = splitString && _toString(this) == "[object String]" ?
394
                this.split("") :
395
                object,
396
            thisp = arguments[1],
397
            i = -1,
398
            length = self.length >>> 0;
399
        if (_toString(fun) != "[object Function]") {
400
            throw new TypeError(); // TODO message
401
        }
402
403
        while (++i < length) {
404
            if (i in self) {
405
                fun.call(thisp, self[i], i, object);
406
            }
407
        }
408
    };
409
}
410
if (!Array.prototype.map) {
411
    Array.prototype.map = function map(fun /*, thisp*/) {
412
        var object = toObject(this),
413
            self = splitString && _toString(this) == "[object String]" ?
414
                this.split("") :
415
                object,
416
            length = self.length >>> 0,
417
            result = Array(length),
418
            thisp = arguments[1];
419
        if (_toString(fun) != "[object Function]") {
420
            throw new TypeError(fun + " is not a function");
421
        }
422
423
        for (var i = 0; i < length; i++) {
424
            if (i in self)
425
                result[i] = fun.call(thisp, self[i], i, object);
426
        }
427
        return result;
428
    };
429
}
430
if (!Array.prototype.filter) {
431
    Array.prototype.filter = function filter(fun /*, thisp */) {
432
        var object = toObject(this),
433
            self = splitString && _toString(this) == "[object String]" ?
434
                this.split("") :
435
                    object,
436
            length = self.length >>> 0,
437
            result = [],
438
            value,
439
            thisp = arguments[1];
440
        if (_toString(fun) != "[object Function]") {
441
            throw new TypeError(fun + " is not a function");
442
        }
443
444
        for (var i = 0; i < length; i++) {
445
            if (i in self) {
446
                value = self[i];
447
                if (fun.call(thisp, value, i, object)) {
448
                    result.push(value);
449
                }
450
            }
451
        }
452
        return result;
453
    };
454
}
455
if (!Array.prototype.every) {
456
    Array.prototype.every = function every(fun /*, thisp */) {
457
        var object = toObject(this),
458
            self = splitString && _toString(this) == "[object String]" ?
459
                this.split("") :
460
                object,
461
            length = self.length >>> 0,
462
            thisp = arguments[1];
463
        if (_toString(fun) != "[object Function]") {
464
            throw new TypeError(fun + " is not a function");
465
        }
466
467
        for (var i = 0; i < length; i++) {
468
            if (i in self && !fun.call(thisp, self[i], i, object)) {
469
                return false;
470
            }
471
        }
472
        return true;
473
    };
474
}
475
if (!Array.prototype.some) {
476
    Array.prototype.some = function some(fun /*, thisp */) {
477
        var object = toObject(this),
478
            self = splitString && _toString(this) == "[object String]" ?
479
                this.split("") :
480
                object,
481
            length = self.length >>> 0,
482
            thisp = arguments[1];
483
        if (_toString(fun) != "[object Function]") {
484
            throw new TypeError(fun + " is not a function");
485
        }
486
487
        for (var i = 0; i < length; i++) {
488
            if (i in self && fun.call(thisp, self[i], i, object)) {
489
                return true;
490
            }
491
        }
492
        return false;
493
    };
494
}
495
if (!Array.prototype.reduce) {
496
    Array.prototype.reduce = function reduce(fun /*, initial*/) {
497
        var object = toObject(this),
498
            self = splitString && _toString(this) == "[object String]" ?
499
                this.split("") :
500
                object,
501
            length = self.length >>> 0;
502
        if (_toString(fun) != "[object Function]") {
503
            throw new TypeError(fun + " is not a function");
504
        }
505
        if (!length && arguments.length == 1) {
506
            throw new TypeError("reduce of empty array with no initial value");
507
        }
508
509
        var i = 0;
510
        var result;
511
        if (arguments.length >= 2) {
512
            result = arguments[1];
513
        } else {
514
            do {
515
                if (i in self) {
516
                    result = self[i++];
517
                    break;
518
                }
519
                if (++i >= length) {
520
                    throw new TypeError("reduce of empty array with no initial value");
521
                }
522
            } while (true);
523
        }
524
525
        for (; i < length; i++) {
526
            if (i in self) {
527
                result = fun.call(void 0, result, self[i], i, object);
528
            }
529
        }
530
531
        return result;
532
    };
533
}
534
if (!Array.prototype.reduceRight) {
535
    Array.prototype.reduceRight = function reduceRight(fun /*, initial*/) {
536
        var object = toObject(this),
537
            self = splitString && _toString(this) == "[object String]" ?
538
                this.split("") :
539
                object,
540
            length = self.length >>> 0;
541
        if (_toString(fun) != "[object Function]") {
542
            throw new TypeError(fun + " is not a function");
543
        }
544
        if (!length && arguments.length == 1) {
545
            throw new TypeError("reduceRight of empty array with no initial value");
546
        }
547
548
        var result, i = length - 1;
549
        if (arguments.length >= 2) {
550
            result = arguments[1];
551
        } else {
552
            do {
553
                if (i in self) {
554
                    result = self[i--];
555
                    break;
556
                }
557
                if (--i < 0) {
558
                    throw new TypeError("reduceRight of empty array with no initial value");
559
                }
560
            } while (true);
561
        }
562
563
        do {
564
            if (i in this) {
565
                result = fun.call(void 0, result, self[i], i, object);
566
            }
567
        } while (i--);
568
569
        return result;
570
    };
571
}
572
if (!Array.prototype.indexOf || ([0, 1].indexOf(1, 2) != -1)) {
573
    Array.prototype.indexOf = function indexOf(sought /*, fromIndex */ ) {
574
        var self = splitString && _toString(this) == "[object String]" ?
575
                this.split("") :
576
                toObject(this),
577
            length = self.length >>> 0;
578
579
        if (!length) {
580
            return -1;
581
        }
582
583
        var i = 0;
584
        if (arguments.length > 1) {
585
            i = toInteger(arguments[1]);
586
        }
587
        i = i >= 0 ? i : Math.max(0, length + i);
588
        for (; i < length; i++) {
589
            if (i in self && self[i] === sought) {
590
                return i;
591
            }
592
        }
593
        return -1;
594
    };
595
}
596
if (!Array.prototype.lastIndexOf || ([0, 1].lastIndexOf(0, -3) != -1)) {
597
    Array.prototype.lastIndexOf = function lastIndexOf(sought /*, fromIndex */) {
598
        var self = splitString && _toString(this) == "[object String]" ?
599
                this.split("") :
600
                toObject(this),
601
            length = self.length >>> 0;
602
603
        if (!length) {
604
            return -1;
605
        }
606
        var i = length - 1;
607
        if (arguments.length > 1) {
608
            i = Math.min(i, toInteger(arguments[1]));
609
        }
610
        i = i >= 0 ? i : length - Math.abs(i);
611
        for (; i >= 0; i--) {
612
            if (i in self && sought === self[i]) {
613
                return i;
614
            }
615
        }
616
        return -1;
617
    };
618
}
619
if (!Object.getPrototypeOf) {
620
    Object.getPrototypeOf = function getPrototypeOf(object) {
621
        return object.__proto__ || (
622
            object.constructor ?
623
            object.constructor.prototype :
624
            prototypeOfObject
625
        );
626
    };
627
}
628
if (!Object.getOwnPropertyDescriptor) {
629
    var ERR_NON_OBJECT = "Object.getOwnPropertyDescriptor called on a " +
630
                         "non-object: ";
631
    Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(object, property) {
632
        if ((typeof object != "object" && typeof object != "function") || object === null)
633
            throw new TypeError(ERR_NON_OBJECT + object);
634
        if (!owns(object, property))
635
            return;
636
637
        var descriptor, getter, setter;
638
        descriptor =  { enumerable: true, configurable: true };
639
        if (supportsAccessors) {
640
            var prototype = object.__proto__;
641
            object.__proto__ = prototypeOfObject;
642
643
            var getter = lookupGetter(object, property);
644
            var setter = lookupSetter(object, property);
645
            object.__proto__ = prototype;
646
647
            if (getter || setter) {
648
                if (getter) descriptor.get = getter;
649
                if (setter) descriptor.set = setter;
650
                return descriptor;
651
            }
652
        }
653
        descriptor.value = object[property];
654
        return descriptor;
655
    };
656
}
657
if (!Object.getOwnPropertyNames) {
658
    Object.getOwnPropertyNames = function getOwnPropertyNames(object) {
659
        return Object.keys(object);
660
    };
661
}
662
if (!Object.create) {
663
    var createEmpty;
664
    if (Object.prototype.__proto__ === null) {
665
        createEmpty = function () {
666
            return { "__proto__": null };
667
        };
668
    } else {
669
        createEmpty = function () {
670
            var empty = {};
671
            for (var i in empty)
672
                empty[i] = null;
673
            empty.constructor =
674
            empty.hasOwnProperty =
675
            empty.propertyIsEnumerable =
676
            empty.isPrototypeOf =
677
            empty.toLocaleString =
678
            empty.toString =
679
            empty.valueOf =
680
            empty.__proto__ = null;
681
            return empty;
682
        }
683
    }
684
685
    Object.create = function create(prototype, properties) {
686
        var object;
687
        if (prototype === null) {
688
            object = createEmpty();
689
        } else {
690
            if (typeof prototype != "object")
691
                throw new TypeError("typeof prototype["+(typeof prototype)+"] != 'object'");
692
            var Type = function () {};
693
            Type.prototype = prototype;
694
            object = new Type();
695
            object.__proto__ = prototype;
696
        }
697
        if (properties !== void 0)
698
            Object.defineProperties(object, properties);
699
        return object;
700
    };
701
}
702
703
function doesDefinePropertyWork(object) {
704
    try {
705
        Object.defineProperty(object, "sentinel", {});
706
        return "sentinel" in object;
707
    } catch (exception) {
708
    }
709
}
710
if (Object.defineProperty) {
711
    var definePropertyWorksOnObject = doesDefinePropertyWork({});
712
    var definePropertyWorksOnDom = typeof document == "undefined" ||
713
        doesDefinePropertyWork(document.createElement("div"));
714
    if (!definePropertyWorksOnObject || !definePropertyWorksOnDom) {
715
        var definePropertyFallback = Object.defineProperty;
716
    }
717
}
718
719
if (!Object.defineProperty || definePropertyFallback) {
720
    var ERR_NON_OBJECT_DESCRIPTOR = "Property description must be an object: ";
721
    var ERR_NON_OBJECT_TARGET = "Object.defineProperty called on non-object: "
722
    var ERR_ACCESSORS_NOT_SUPPORTED = "getters & setters can not be defined " +
723
                                      "on this javascript engine";
724
725
    Object.defineProperty = function defineProperty(object, property, descriptor) {
726
        if ((typeof object != "object" && typeof object != "function") || object === null)
727
            throw new TypeError(ERR_NON_OBJECT_TARGET + object);
728
        if ((typeof descriptor != "object" && typeof descriptor != "function") || descriptor === null)
729
            throw new TypeError(ERR_NON_OBJECT_DESCRIPTOR + descriptor);
730
        if (definePropertyFallback) {
731
            try {
732
                return definePropertyFallback.call(Object, object, property, descriptor);
733
            } catch (exception) {
734
            }
735
        }
736
        if (owns(descriptor, "value")) {
737
738
            if (supportsAccessors && (lookupGetter(object, property) ||
739
                                      lookupSetter(object, property)))
740
            {
741
                var prototype = object.__proto__;
742
                object.__proto__ = prototypeOfObject;
743
                delete object[property];
744
                object[property] = descriptor.value;
745
                object.__proto__ = prototype;
746
            } else {
747
                object[property] = descriptor.value;
748
            }
749
        } else {
750
            if (!supportsAccessors)
751
                throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
752
            if (owns(descriptor, "get"))
753
                defineGetter(object, property, descriptor.get);
754
            if (owns(descriptor, "set"))
755
                defineSetter(object, property, descriptor.set);
756
        }
757
758
        return object;
759
    };
760
}
761
if (!Object.defineProperties) {
762
    Object.defineProperties = function defineProperties(object, properties) {
763
        for (var property in properties) {
764
            if (owns(properties, property))
765
                Object.defineProperty(object, property, properties[property]);
766
        }
767
        return object;
768
    };
769
}
770
if (!Object.seal) {
771
    Object.seal = function seal(object) {
772
        return object;
773
    };
774
}
775
if (!Object.freeze) {
776
    Object.freeze = function freeze(object) {
777
        return object;
778
    };
779
}
780
try {
781
    Object.freeze(function () {});
782
} catch (exception) {
783
    Object.freeze = (function freeze(freezeObject) {
784
        return function freeze(object) {
785
            if (typeof object == "function") {
786
                return object;
787
            } else {
788
                return freezeObject(object);
789
            }
790
        };
791
    })(Object.freeze);
792
}
793
if (!Object.preventExtensions) {
794
    Object.preventExtensions = function preventExtensions(object) {
795
        return object;
796
    };
797
}
798
if (!Object.isSealed) {
799
    Object.isSealed = function isSealed(object) {
800
        return false;
801
    };
802
}
803
if (!Object.isFrozen) {
804
    Object.isFrozen = function isFrozen(object) {
805
        return false;
806
    };
807
}
808
if (!Object.isExtensible) {
809
    Object.isExtensible = function isExtensible(object) {
810
        if (Object(object) === object) {
811
            throw new TypeError(); // TODO message
812
        }
813
        var name = '';
814
        while (owns(object, name)) {
815
            name += '?';
816
        }
817
        object[name] = true;
818
        var returnValue = owns(object, name);
819
        delete object[name];
820
        return returnValue;
821
    };
822
}
823
if (!Object.keys) {
824
    var hasDontEnumBug = true,
825
        dontEnums = [
826
            "toString",
827
            "toLocaleString",
828
            "valueOf",
829
            "hasOwnProperty",
830
            "isPrototypeOf",
831
            "propertyIsEnumerable",
832
            "constructor"
833
        ],
834
        dontEnumsLength = dontEnums.length;
835
836
    for (var key in {"toString": null}) {
837
        hasDontEnumBug = false;
838
    }
839
840
    Object.keys = function keys(object) {
841
842
        if (
843
            (typeof object != "object" && typeof object != "function") ||
844
            object === null
845
        ) {
846
            throw new TypeError("Object.keys called on a non-object");
847
        }
848
849
        var keys = [];
850
        for (var name in object) {
851
            if (owns(object, name)) {
852
                keys.push(name);
853
            }
854
        }
855
856
        if (hasDontEnumBug) {
857
            for (var i = 0, ii = dontEnumsLength; i < ii; i++) {
858
                var dontEnum = dontEnums[i];
859
                if (owns(object, dontEnum)) {
860
                    keys.push(dontEnum);
861
                }
862
            }
863
        }
864
        return keys;
865
    };
866
867
}
868
if (!Date.now) {
869
    Date.now = function now() {
870
        return new Date().getTime();
871
    };
872
}
873
var ws = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003" +
874
    "\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028" +
875
    "\u2029\uFEFF";
876
if (!String.prototype.trim || ws.trim()) {
877
    ws = "[" + ws + "]";
878
    var trimBeginRegexp = new RegExp("^" + ws + ws + "*"),
879
        trimEndRegexp = new RegExp(ws + ws + "*$");
880
    String.prototype.trim = function trim() {
881
        return String(this).replace(trimBeginRegexp, "").replace(trimEndRegexp, "");
882
    };
883
}
884
885
function toInteger(n) {
886
    n = +n;
887
    if (n !== n) { // isNaN
888
        n = 0;
889
    } else if (n !== 0 && n !== (1/0) && n !== -(1/0)) {
890
        n = (n > 0 || -1) * Math.floor(Math.abs(n));
891
    }
892
    return n;
893
}
894
895
function isPrimitive(input) {
896
    var type = typeof input;
897
    return (
898
        input === null ||
899
        type === "undefined" ||
900
        type === "boolean" ||
901
        type === "number" ||
902
        type === "string"
903
    );
904
}
905
906
function toPrimitive(input) {
907
    var val, valueOf, toString;
908
    if (isPrimitive(input)) {
909
        return input;
910
    }
911
    valueOf = input.valueOf;
912
    if (typeof valueOf === "function") {
913
        val = valueOf.call(input);
914
        if (isPrimitive(val)) {
915
            return val;
916
        }
917
    }
918
    toString = input.toString;
919
    if (typeof toString === "function") {
920
        val = toString.call(input);
921
        if (isPrimitive(val)) {
922
            return val;
923
        }
924
    }
925
    throw new TypeError();
926
}
927
var toObject = function (o) {
928
    if (o == null) { // this matches both null and undefined
929
        throw new TypeError("can't convert "+o+" to object");
930
    }
931
    return Object(o);
932
};
933
934
});
935
936
define('ace/lib/event_emitter', ['require', 'exports', 'module' ], function(require, exports, module) {
937
938
939
var EventEmitter = {};
940
var stopPropagation = function() { this.propagationStopped = true; };
941
var preventDefault = function() { this.defaultPrevented = true; };
942
943
EventEmitter._emit =
944
EventEmitter._dispatchEvent = function(eventName, e) {
945
    this._eventRegistry || (this._eventRegistry = {});
946
    this._defaultHandlers || (this._defaultHandlers = {});
947
948
    var listeners = this._eventRegistry[eventName] || [];
949
    var defaultHandler = this._defaultHandlers[eventName];
950
    if (!listeners.length && !defaultHandler)
951
        return;
952
953
    if (typeof e != "object" || !e)
954
        e = {};
955
956
    if (!e.type)
957
        e.type = eventName;
958
    if (!e.stopPropagation)
959
        e.stopPropagation = stopPropagation;
960
    if (!e.preventDefault)
961
        e.preventDefault = preventDefault;
962
    if (!e.target)
963
        e.target = this;
964
965
    for (var i=0; i<listeners.length; i++) {
966
        listeners[i](e);
967
        if (e.propagationStopped)
968
            break;
969
    }
970
    
971
    if (defaultHandler && !e.defaultPrevented)
972
        return defaultHandler(e);
973
};
974
975
976
EventEmitter._signal = function(eventName, e) {
977
    var listeners = (this._eventRegistry || {})[eventName];
978
    if (!listeners)
979
        return;
980
981
    for (var i=0; i<listeners.length; i++)
982
        listeners[i](e);
983
};
984
985
EventEmitter.once = function(eventName, callback) {
986
    var _self = this;
987
    var newCallback = function() {
988
        fun && fun.apply(null, arguments);
989
        _self.removeEventListener(event, newCallback);
990
    };
991
    this.addEventListener(event, newCallback);
992
};
993
994
995
EventEmitter.setDefaultHandler = function(eventName, callback) {
996
    this._defaultHandlers = this._defaultHandlers || {};
997
    
998
    if (this._defaultHandlers[eventName])
999
        throw new Error("The default handler for '" + eventName + "' is already set");
1000
        
1001
    this._defaultHandlers[eventName] = callback;
1002
};
1003
1004
EventEmitter.on =
1005
EventEmitter.addEventListener = function(eventName, callback, capturing) {
1006
    this._eventRegistry = this._eventRegistry || {};
1007
1008
    var listeners = this._eventRegistry[eventName];
1009
    if (!listeners)
1010
        listeners = this._eventRegistry[eventName] = [];
1011
1012
    if (listeners.indexOf(callback) == -1)
1013
        listeners[capturing ? "unshift" : "push"](callback);
1014
    return callback;
1015
};
1016
1017
EventEmitter.removeListener =
1018
EventEmitter.removeEventListener = function(eventName, callback) {
1019
    this._eventRegistry = this._eventRegistry || {};
1020
1021
    var listeners = this._eventRegistry[eventName];
1022
    if (!listeners)
1023
        return;
1024
1025
    var index = listeners.indexOf(callback);
1026
    if (index !== -1)
1027
        listeners.splice(index, 1);
1028
};
1029
1030
EventEmitter.removeAllListeners = function(eventName) {
1031
    if (this._eventRegistry) this._eventRegistry[eventName] = [];
1032
};
1033
1034
exports.EventEmitter = EventEmitter;
1035
1036
});
1037
1038
define('ace/lib/oop', ['require', 'exports', 'module' ], function(require, exports, module) {
1039
1040
1041
exports.inherits = (function() {
1042
    var tempCtor = function() {};
1043
    return function(ctor, superCtor) {
1044
        tempCtor.prototype = superCtor.prototype;
1045
        ctor.super_ = superCtor.prototype;
1046
        ctor.prototype = new tempCtor();
1047
        ctor.prototype.constructor = ctor;
1048
    };
1049
}());
1050
1051
exports.mixin = function(obj, mixin) {
1052
    for (var key in mixin) {
1053
        obj[key] = mixin[key];
1054
    }
1055
};
1056
1057
exports.implement = function(proto, mixin) {
1058
    exports.mixin(proto, mixin);
1059
};
1060
1061
});
1062
1063
define('ace/mode/css_worker', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/lang', 'ace/worker/mirror', 'ace/mode/css/csslint'], function(require, exports, module) {
1064
1065
1066
var oop = require("../lib/oop");
1067
var lang = require("../lib/lang");
1068
var Mirror = require("../worker/mirror").Mirror;
1069
var CSSLint = require("./css/csslint").CSSLint;
1070
1071
var Worker = exports.Worker = function(sender) {
1072
    Mirror.call(this, sender);
1073
    this.setTimeout(400);
1074
    this.ruleset = null;
1075
    this.setDisabledRules("ids");
1076
    this.setInfoRules("adjoining-classes|qualified-headings|zero-units|gradients|import|outline-none");
1077
};
1078
1079
oop.inherits(Worker, Mirror);
1080
1081
(function() {
1082
    this.setInfoRules = function(ruleNames) {
1083
        if (typeof ruleNames == "string")
1084
            ruleNames = ruleNames.split("|");
1085
        this.infoRules = lang.arrayToMap(ruleNames);
1086
        this.doc.getValue() && this.deferredUpdate.schedule(100);
1087
    };
1088
1089
    this.setDisabledRules = function(ruleNames) {
1090
        if (!ruleNames) {
1091
            this.ruleset = null;
1092
        } else {
1093
            if (typeof ruleNames == "string")
1094
                ruleNames = ruleNames.split("|");
1095
            var all = {};
1096
1097
            CSSLint.getRules().forEach(function(x){
1098
                all[x.id] = true;
1099
            });
1100
            ruleNames.forEach(function(x) {
1101
                delete all[x];
1102
            });
1103
            
1104
            this.ruleset = all;
1105
        }
1106
        this.doc.getValue() && this.deferredUpdate.schedule(100);
1107
    };
1108
1109
    this.onUpdate = function() {
1110
        var value = this.doc.getValue();
1111
        var infoRules = this.infoRules;
1112
1113
        var result = CSSLint.verify(value, this.ruleset);
1114
        this.sender.emit("csslint", result.messages.map(function(msg) {
1115
            return {
1116
                row: msg.line - 1,
1117
                column: msg.col - 1,
1118
                text: msg.message,
1119
                type: infoRules[msg.rule.id] ? "info" : msg.type
1120
            }
1121
        }));
1122
    };
1123
1124
}).call(Worker.prototype);
1125
1126
});
1127
1128
define('ace/lib/lang', ['require', 'exports', 'module' ], function(require, exports, module) {
1129
1130
1131
exports.stringReverse = function(string) {
1132
    return string.split("").reverse().join("");
1133
};
1134
1135
exports.stringRepeat = function (string, count) {
1136
    var result = '';
1137
    while (count > 0) {
1138
        if (count & 1)
1139
            result += string;
1140
1141
        if (count >>= 1)
1142
            string += string;
1143
    }
1144
    return result;
1145
};
1146
1147
var trimBeginRegexp = /^\s\s*/;
1148
var trimEndRegexp = /\s\s*$/;
1149
1150
exports.stringTrimLeft = function (string) {
1151
    return string.replace(trimBeginRegexp, '');
1152
};
1153
1154
exports.stringTrimRight = function (string) {
1155
    return string.replace(trimEndRegexp, '');
1156
};
1157
1158
exports.copyObject = function(obj) {
1159
    var copy = {};
1160
    for (var key in obj) {
1161
        copy[key] = obj[key];
1162
    }
1163
    return copy;
1164
};
1165
1166
exports.copyArray = function(array){
1167
    var copy = [];
1168
    for (var i=0, l=array.length; i<l; i++) {
1169
        if (array[i] && typeof array[i] == "object")
1170
            copy[i] = this.copyObject( array[i] );
1171
        else 
1172
            copy[i] = array[i];
1173
    }
1174
    return copy;
1175
};
1176
1177
exports.deepCopy = function (obj) {
1178
    if (typeof obj != "object") {
1179
        return obj;
1180
    }
1181
    
1182
    var copy = obj.constructor();
1183
    for (var key in obj) {
1184
        if (typeof obj[key] == "object") {
1185
            copy[key] = this.deepCopy(obj[key]);
1186
        } else {
1187
            copy[key] = obj[key];
1188
        }
1189
    }
1190
    return copy;
1191
};
1192
1193
exports.arrayToMap = function(arr) {
1194
    var map = {};
1195
    for (var i=0; i<arr.length; i++) {
1196
        map[arr[i]] = 1;
1197
    }
1198
    return map;
1199
1200
};
1201
1202
exports.createMap = function(props) {
1203
    var map = Object.create(null);
1204
    for (var i in props) {
1205
        map[i] = props[i];
1206
    }
1207
    return map;
1208
};
1209
exports.arrayRemove = function(array, value) {
1210
  for (var i = 0; i <= array.length; i++) {
1211
    if (value === array[i]) {
1212
      array.splice(i, 1);
1213
    }
1214
  }
1215
};
1216
1217
exports.escapeRegExp = function(str) {
1218
    return str.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1');
1219
};
1220
1221
exports.escapeHTML = function(str) {
1222
    return str.replace(/&/g, "&#38;").replace(/"/g, "&#34;").replace(/'/g, "&#39;").replace(/</g, "&#60;");
1223
};
1224
1225
exports.getMatchOffsets = function(string, regExp) {
1226
    var matches = [];
1227
1228
    string.replace(regExp, function(str) {
1229
        matches.push({
1230
            offset: arguments[arguments.length-2],
1231
            length: str.length
1232
        });
1233
    });
1234
1235
    return matches;
1236
};
1237
exports.deferredCall = function(fcn) {
1238
1239
    var timer = null;
1240
    var callback = function() {
1241
        timer = null;
1242
        fcn();
1243
    };
1244
1245
    var deferred = function(timeout) {
1246
        deferred.cancel();
1247
        timer = setTimeout(callback, timeout || 0);
1248
        return deferred;
1249
    };
1250
1251
    deferred.schedule = deferred;
1252
1253
    deferred.call = function() {
1254
        this.cancel();
1255
        fcn();
1256
        return deferred;
1257
    };
1258
1259
    deferred.cancel = function() {
1260
        clearTimeout(timer);
1261
        timer = null;
1262
        return deferred;
1263
    };
1264
1265
    return deferred;
1266
};
1267
1268
1269
exports.delayedCall = function(fcn, defaultTimeout) {
1270
    var timer = null;
1271
    var callback = function() {
1272
        timer = null;
1273
        fcn();
1274
    };
1275
1276
    var _self = function(timeout) {
1277
        timer && clearTimeout(timer);
1278
        timer = setTimeout(callback, timeout || defaultTimeout);
1279
    };
1280
1281
    _self.delay = _self;
1282
    _self.schedule = function(timeout) {
1283
        if (timer == null)
1284
            timer = setTimeout(callback, timeout || 0);
1285
    };
1286
1287
    _self.call = function() {
1288
        this.cancel();
1289
        fcn();
1290
    };
1291
1292
    _self.cancel = function() {
1293
        timer && clearTimeout(timer);
1294
        timer = null;
1295
    };
1296
1297
    _self.isPending = function() {
1298
        return timer;
1299
    };
1300
1301
    return _self;
1302
};
1303
});
1304
define('ace/worker/mirror', ['require', 'exports', 'module' , 'ace/document', 'ace/lib/lang'], function(require, exports, module) {
1305
1306
1307
var Document = require("../document").Document;
1308
var lang = require("../lib/lang");
1309
    
1310
var Mirror = exports.Mirror = function(sender) {
1311
    this.sender = sender;
1312
    var doc = this.doc = new Document("");
1313
    
1314
    var deferredUpdate = this.deferredUpdate = lang.delayedCall(this.onUpdate.bind(this));
1315
    
1316
    var _self = this;
1317
    sender.on("change", function(e) {
1318
        doc.applyDeltas([e.data]);        
1319
        deferredUpdate.schedule(_self.$timeout);
1320
    });
1321
};
1322
1323
(function() {
1324
    
1325
    this.$timeout = 500;
1326
    
1327
    this.setTimeout = function(timeout) {
1328
        this.$timeout = timeout;
1329
    };
1330
    
1331
    this.setValue = function(value) {
1332
        this.doc.setValue(value);
1333
        this.deferredUpdate.schedule(this.$timeout);
1334
    };
1335
    
1336
    this.getValue = function(callbackId) {
1337
        this.sender.callback(this.doc.getValue(), callbackId);
1338
    };
1339
    
1340
    this.onUpdate = function() {
1341
    };
1342
    
1343
}).call(Mirror.prototype);
1344
1345
});
1346
1347
define('ace/document', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/event_emitter', 'ace/range', 'ace/anchor'], function(require, exports, module) {
1348
1349
1350
var oop = require("./lib/oop");
1351
var EventEmitter = require("./lib/event_emitter").EventEmitter;
1352
var Range = require("./range").Range;
1353
var Anchor = require("./anchor").Anchor;
1354
1355
var Document = function(text) {
1356
    this.$lines = [];
1357
    if (text.length == 0) {
1358
        this.$lines = [""];
1359
    } else if (Array.isArray(text)) {
1360
        this.insertLines(0, text);
1361
    } else {
1362
        this.insert({row: 0, column:0}, text);
1363
    }
1364
};
1365
1366
(function() {
1367
1368
    oop.implement(this, EventEmitter);
1369
    this.setValue = function(text) {
1370
        var len = this.getLength();
1371
        this.remove(new Range(0, 0, len, this.getLine(len-1).length));
1372
        this.insert({row: 0, column:0}, text);
1373
    };
1374
    this.getValue = function() {
1375
        return this.getAllLines().join(this.getNewLineCharacter());
1376
    };
1377
    this.createAnchor = function(row, column) {
1378
        return new Anchor(this, row, column);
1379
    };
1380
    if ("aaa".split(/a/).length == 0)
1381
        this.$split = function(text) {
1382
            return text.replace(/\r\n|\r/g, "\n").split("\n");
1383
        }
1384
    else
1385
        this.$split = function(text) {
1386
            return text.split(/\r\n|\r|\n/);
1387
        };
1388
1389
1390
 
1391
    this.$detectNewLine = function(text) {
1392
        var match = text.match(/^.*?(\r\n|\r|\n)/m);
1393
        if (match) {
1394
            this.$autoNewLine = match[1];
1395
        } else {
1396
            this.$autoNewLine = "\n";
1397
        }
1398
    };
1399
    this.getNewLineCharacter = function() {
1400
        switch (this.$newLineMode) {
1401
          case "windows":
1402
            return "\r\n";
1403
1404
          case "unix":
1405
            return "\n";
1406
1407
          default:
1408
            return this.$autoNewLine;
1409
        }
1410
    };
1411
1412
    this.$autoNewLine = "\n";
1413
    this.$newLineMode = "auto";
1414
    this.setNewLineMode = function(newLineMode) {
1415
        if (this.$newLineMode === newLineMode)
1416
            return;
1417
1418
        this.$newLineMode = newLineMode;
1419
    };
1420
    this.getNewLineMode = function() {
1421
        return this.$newLineMode;
1422
    };
1423
    this.isNewLine = function(text) {
1424
        return (text == "\r\n" || text == "\r" || text == "\n");
1425
    };
1426
    this.getLine = function(row) {
1427
        return this.$lines[row] || "";
1428
    };
1429
    this.getLines = function(firstRow, lastRow) {
1430
        return this.$lines.slice(firstRow, lastRow + 1);
1431
    };
1432
    this.getAllLines = function() {
1433
        return this.getLines(0, this.getLength());
1434
    };
1435
    this.getLength = function() {
1436
        return this.$lines.length;
1437
    };
1438
    this.getTextRange = function(range) {
1439
        if (range.start.row == range.end.row) {
1440
            return this.$lines[range.start.row].substring(range.start.column,
1441
                                                         range.end.column);
1442
        }
1443
        else {
1444
            var lines = this.getLines(range.start.row+1, range.end.row-1);
1445
            lines.unshift((this.$lines[range.start.row] || "").substring(range.start.column));
1446
            lines.push((this.$lines[range.end.row] || "").substring(0, range.end.column));
1447
            return lines.join(this.getNewLineCharacter());
1448
        }
1449
    };
1450
1451
    this.$clipPosition = function(position) {
1452
        var length = this.getLength();
1453
        if (position.row >= length) {
1454
            position.row = Math.max(0, length - 1);
1455
            position.column = this.getLine(length-1).length;
1456
        }
1457
        return position;
1458
    };
1459
    this.insert = function(position, text) {
1460
        if (!text || text.length === 0)
1461
            return position;
1462
1463
        position = this.$clipPosition(position);
1464
        if (this.getLength() <= 1)
1465
            this.$detectNewLine(text);
1466
1467
        var lines = this.$split(text);
1468
        var firstLine = lines.splice(0, 1)[0];
1469
        var lastLine = lines.length == 0 ? null : lines.splice(lines.length - 1, 1)[0];
1470
1471
        position = this.insertInLine(position, firstLine);
1472
        if (lastLine !== null) {
1473
            position = this.insertNewLine(position); // terminate first line
1474
            position = this.insertLines(position.row, lines);
1475
            position = this.insertInLine(position, lastLine || "");
1476
        }
1477
        return position;
1478
    };
1479
    this.insertLines = function(row, lines) {
1480
        if (lines.length == 0)
1481
            return {row: row, column: 0};
1482
        if (lines.length > 0xFFFF) {
1483
            var end = this.insertLines(row, lines.slice(0xFFFF));
1484
            lines = lines.slice(0, 0xFFFF);
1485
        }
1486
1487
        var args = [row, 0];
1488
        args.push.apply(args, lines);
1489
        this.$lines.splice.apply(this.$lines, args);
1490
1491
        var range = new Range(row, 0, row + lines.length, 0);
1492
        var delta = {
1493
            action: "insertLines",
1494
            range: range,
1495
            lines: lines
1496
        };
1497
        this._emit("change", { data: delta });
1498
        return end || range.end;
1499
    };
1500
    this.insertNewLine = function(position) {
1501
        position = this.$clipPosition(position);
1502
        var line = this.$lines[position.row] || "";
1503
1504
        this.$lines[position.row] = line.substring(0, position.column);
1505
        this.$lines.splice(position.row + 1, 0, line.substring(position.column, line.length));
1506
1507
        var end = {
1508
            row : position.row + 1,
1509
            column : 0
1510
        };
1511
1512
        var delta = {
1513
            action: "insertText",
1514
            range: Range.fromPoints(position, end),
1515
            text: this.getNewLineCharacter()
1516
        };
1517
        this._emit("change", { data: delta });
1518
1519
        return end;
1520
    };
1521
    this.insertInLine = function(position, text) {
1522
        if (text.length == 0)
1523
            return position;
1524
1525
        var line = this.$lines[position.row] || "";
1526
1527
        this.$lines[position.row] = line.substring(0, position.column) + text
1528
                + line.substring(position.column);
1529
1530
        var end = {
1531
            row : position.row,
1532
            column : position.column + text.length
1533
        };
1534
1535
        var delta = {
1536
            action: "insertText",
1537
            range: Range.fromPoints(position, end),
1538
            text: text
1539
        };
1540
        this._emit("change", { data: delta });
1541
1542
        return end;
1543
    };
1544
    this.remove = function(range) {
1545
        range.start = this.$clipPosition(range.start);
1546
        range.end = this.$clipPosition(range.end);
1547
1548
        if (range.isEmpty())
1549
            return range.start;
1550
1551
        var firstRow = range.start.row;
1552
        var lastRow = range.end.row;
1553
1554
        if (range.isMultiLine()) {
1555
            var firstFullRow = range.start.column == 0 ? firstRow : firstRow + 1;
1556
            var lastFullRow = lastRow - 1;
1557
1558
            if (range.end.column > 0)
1559
                this.removeInLine(lastRow, 0, range.end.column);
1560
1561
            if (lastFullRow >= firstFullRow)
1562
                this.removeLines(firstFullRow, lastFullRow);
1563
1564
            if (firstFullRow != firstRow) {
1565
                this.removeInLine(firstRow, range.start.column, this.getLine(firstRow).length);
1566
                this.removeNewLine(range.start.row);
1567
            }
1568
        }
1569
        else {
1570
            this.removeInLine(firstRow, range.start.column, range.end.column);
1571
        }
1572
        return range.start;
1573
    };
1574
    this.removeInLine = function(row, startColumn, endColumn) {
1575
        if (startColumn == endColumn)
1576
            return;
1577
1578
        var range = new Range(row, startColumn, row, endColumn);
1579
        var line = this.getLine(row);
1580
        var removed = line.substring(startColumn, endColumn);
1581
        var newLine = line.substring(0, startColumn) + line.substring(endColumn, line.length);
1582
        this.$lines.splice(row, 1, newLine);
1583
1584
        var delta = {
1585
            action: "removeText",
1586
            range: range,
1587
            text: removed
1588
        };
1589
        this._emit("change", { data: delta });
1590
        return range.start;
1591
    };
1592
    this.removeLines = function(firstRow, lastRow) {
1593
        var range = new Range(firstRow, 0, lastRow + 1, 0);
1594
        var removed = this.$lines.splice(firstRow, lastRow - firstRow + 1);
1595
1596
        var delta = {
1597
            action: "removeLines",
1598
            range: range,
1599
            nl: this.getNewLineCharacter(),
1600
            lines: removed
1601
        };
1602
        this._emit("change", { data: delta });
1603
        return removed;
1604
    };
1605
    this.removeNewLine = function(row) {
1606
        var firstLine = this.getLine(row);
1607
        var secondLine = this.getLine(row+1);
1608
1609
        var range = new Range(row, firstLine.length, row+1, 0);
1610
        var line = firstLine + secondLine;
1611
1612
        this.$lines.splice(row, 2, line);
1613
1614
        var delta = {
1615
            action: "removeText",
1616
            range: range,
1617
            text: this.getNewLineCharacter()
1618
        };
1619
        this._emit("change", { data: delta });
1620
    };
1621
    this.replace = function(range, text) {
1622
        if (text.length == 0 && range.isEmpty())
1623
            return range.start;
1624
        if (text == this.getTextRange(range))
1625
            return range.end;
1626
1627
        this.remove(range);
1628
        if (text) {
1629
            var end = this.insert(range.start, text);
1630
        }
1631
        else {
1632
            end = range.start;
1633
        }
1634
1635
        return end;
1636
    };
1637
    this.applyDeltas = function(deltas) {
1638
        for (var i=0; i<deltas.length; i++) {
1639
            var delta = deltas[i];
1640
            var range = Range.fromPoints(delta.range.start, delta.range.end);
1641
1642
            if (delta.action == "insertLines")
1643
                this.insertLines(range.start.row, delta.lines);
1644
            else if (delta.action == "insertText")
1645
                this.insert(range.start, delta.text);
1646
            else if (delta.action == "removeLines")
1647
                this.removeLines(range.start.row, range.end.row - 1);
1648
            else if (delta.action == "removeText")
1649
                this.remove(range);
1650
        }
1651
    };
1652
    this.revertDeltas = function(deltas) {
1653
        for (var i=deltas.length-1; i>=0; i--) {
1654
            var delta = deltas[i];
1655
1656
            var range = Range.fromPoints(delta.range.start, delta.range.end);
1657
1658
            if (delta.action == "insertLines")
1659
                this.removeLines(range.start.row, range.end.row - 1);
1660
            else if (delta.action == "insertText")
1661
                this.remove(range);
1662
            else if (delta.action == "removeLines")
1663
                this.insertLines(range.start.row, delta.lines);
1664
            else if (delta.action == "removeText")
1665
                this.insert(range.start, delta.text);
1666
        }
1667
    };
1668
    this.indexToPosition = function(index, startRow) {
1669
        var lines = this.$lines || this.getAllLines();
1670
        var newlineLength = this.getNewLineCharacter().length;
1671
        for (var i = startRow || 0, l = lines.length; i < l; i++) {
1672
            index -= lines[i].length + newlineLength;
1673
            if (index < 0)
1674
                return {row: i, column: index + lines[i].length + newlineLength};
1675
        }
1676
        return {row: l-1, column: lines[l-1].length};
1677
    };
1678
    this.positionToIndex = function(pos, startRow) {
1679
        var lines = this.$lines || this.getAllLines();
1680
        var newlineLength = this.getNewLineCharacter().length;
1681
        var index = 0;
1682
        var row = Math.min(pos.row, lines.length);
1683
        for (var i = startRow || 0; i < row; ++i)
1684
            index += lines[i].length;
1685
1686
        return index + newlineLength * i + pos.column;
1687
    };
1688
1689
}).call(Document.prototype);
1690
1691
exports.Document = Document;
1692
});
1693
1694
define('ace/range', ['require', 'exports', 'module' ], function(require, exports, module) {
1695
1696
var comparePoints = function(p1, p2) {
1697
    return p1.row - p2.row || p1.column - p2.column;
1698
};
1699
var Range = function(startRow, startColumn, endRow, endColumn) {
1700
    this.start = {
1701
        row: startRow,
1702
        column: startColumn
1703
    };
1704
1705
    this.end = {
1706
        row: endRow,
1707
        column: endColumn
1708
    };
1709
};
1710
1711
(function() {
1712
    this.isEqual = function(range) {
1713
        return this.start.row === range.start.row &&
1714
            this.end.row === range.end.row &&
1715
            this.start.column === range.start.column &&
1716
            this.end.column === range.end.column;
1717
    };
1718
    this.toString = function() {
1719
        return ("Range: [" + this.start.row + "/" + this.start.column +
1720
            "] -> [" + this.end.row + "/" + this.end.column + "]");
1721
    };
1722
1723
    this.contains = function(row, column) {
1724
        return this.compare(row, column) == 0;
1725
    };
1726
    this.compareRange = function(range) {
1727
        var cmp,
1728
            end = range.end,
1729
            start = range.start;
1730
1731
        cmp = this.compare(end.row, end.column);
1732
        if (cmp == 1) {
1733
            cmp = this.compare(start.row, start.column);
1734
            if (cmp == 1) {
1735
                return 2;
1736
            } else if (cmp == 0) {
1737
                return 1;
1738
            } else {
1739
                return 0;
1740
            }
1741
        } else if (cmp == -1) {
1742
            return -2;
1743
        } else {
1744
            cmp = this.compare(start.row, start.column);
1745
            if (cmp == -1) {
1746
                return -1;
1747
            } else if (cmp == 1) {
1748
                return 42;
1749
            } else {
1750
                return 0;
1751
            }
1752
        }
1753
    };
1754
    this.comparePoint = function(p) {
1755
        return this.compare(p.row, p.column);
1756
    };
1757
    this.containsRange = function(range) {
1758
        return this.comparePoint(range.start) == 0 && this.comparePoint(range.end) == 0;
1759
    };
1760
    this.intersects = function(range) {
1761
        var cmp = this.compareRange(range);
1762
        return (cmp == -1 || cmp == 0 || cmp == 1);
1763
    };
1764
    this.isEnd = function(row, column) {
1765
        return this.end.row == row && this.end.column == column;
1766
    };
1767
    this.isStart = function(row, column) {
1768
        return this.start.row == row && this.start.column == column;
1769
    };
1770
    this.setStart = function(row, column) {
1771
        if (typeof row == "object") {
1772
            this.start.column = row.column;
1773
            this.start.row = row.row;
1774
        } else {
1775
            this.start.row = row;
1776
            this.start.column = column;
1777
        }
1778
    };
1779
    this.setEnd = function(row, column) {
1780
        if (typeof row == "object") {
1781
            this.end.column = row.column;
1782
            this.end.row = row.row;
1783
        } else {
1784
            this.end.row = row;
1785
            this.end.column = column;
1786
        }
1787
    };
1788
    this.inside = function(row, column) {
1789
        if (this.compare(row, column) == 0) {
1790
            if (this.isEnd(row, column) || this.isStart(row, column)) {
1791
                return false;
1792
            } else {
1793
                return true;
1794
            }
1795
        }
1796
        return false;
1797
    };
1798
    this.insideStart = function(row, column) {
1799
        if (this.compare(row, column) == 0) {
1800
            if (this.isEnd(row, column)) {
1801
                return false;
1802
            } else {
1803
                return true;
1804
            }
1805
        }
1806
        return false;
1807
    };
1808
    this.insideEnd = function(row, column) {
1809
        if (this.compare(row, column) == 0) {
1810
            if (this.isStart(row, column)) {
1811
                return false;
1812
            } else {
1813
                return true;
1814
            }
1815
        }
1816
        return false;
1817
    };
1818
    this.compare = function(row, column) {
1819
        if (!this.isMultiLine()) {
1820
            if (row === this.start.row) {
1821
                return column < this.start.column ? -1 : (column > this.end.column ? 1 : 0);
1822
            };
1823
        }
1824
1825
        if (row < this.start.row)
1826
            return -1;
1827
1828
        if (row > this.end.row)
1829
            return 1;
1830
1831
        if (this.start.row === row)
1832
            return column >= this.start.column ? 0 : -1;
1833
1834
        if (this.end.row === row)
1835
            return column <= this.end.column ? 0 : 1;
1836
1837
        return 0;
1838
    };
1839
    this.compareStart = function(row, column) {
1840
        if (this.start.row == row && this.start.column == column) {
1841
            return -1;
1842
        } else {
1843
            return this.compare(row, column);
1844
        }
1845
    };
1846
    this.compareEnd = function(row, column) {
1847
        if (this.end.row == row && this.end.column == column) {
1848
            return 1;
1849
        } else {
1850
            return this.compare(row, column);
1851
        }
1852
    };
1853
    this.compareInside = function(row, column) {
1854
        if (this.end.row == row && this.end.column == column) {
1855
            return 1;
1856
        } else if (this.start.row == row && this.start.column == column) {
1857
            return -1;
1858
        } else {
1859
            return this.compare(row, column);
1860
        }
1861
    };
1862
    this.clipRows = function(firstRow, lastRow) {
1863
        if (this.end.row > lastRow)
1864
            var end = {row: lastRow + 1, column: 0};
1865
        else if (this.end.row < firstRow)
1866
            var end = {row: firstRow, column: 0};
1867
1868
        if (this.start.row > lastRow)
1869
            var start = {row: lastRow + 1, column: 0};
1870
        else if (this.start.row < firstRow)
1871
            var start = {row: firstRow, column: 0};
1872
1873
        return Range.fromPoints(start || this.start, end || this.end);
1874
    };
1875
    this.extend = function(row, column) {
1876
        var cmp = this.compare(row, column);
1877
1878
        if (cmp == 0)
1879
            return this;
1880
        else if (cmp == -1)
1881
            var start = {row: row, column: column};
1882
        else
1883
            var end = {row: row, column: column};
1884
1885
        return Range.fromPoints(start || this.start, end || this.end);
1886
    };
1887
1888
    this.isEmpty = function() {
1889
        return (this.start.row === this.end.row && this.start.column === this.end.column);
1890
    };
1891
    this.isMultiLine = function() {
1892
        return (this.start.row !== this.end.row);
1893
    };
1894
    this.clone = function() {
1895
        return Range.fromPoints(this.start, this.end);
1896
    };
1897
    this.collapseRows = function() {
1898
        if (this.end.column == 0)
1899
            return new Range(this.start.row, 0, Math.max(this.start.row, this.end.row-1), 0)
1900
        else
1901
            return new Range(this.start.row, 0, this.end.row, 0)
1902
    };
1903
    this.toScreenRange = function(session) {
1904
        var screenPosStart = session.documentToScreenPosition(this.start);
1905
        var screenPosEnd = session.documentToScreenPosition(this.end);
1906
1907
        return new Range(
1908
            screenPosStart.row, screenPosStart.column,
1909
            screenPosEnd.row, screenPosEnd.column
1910
        );
1911
    };
1912
    this.moveBy = function(row, column) {
1913
        this.start.row += row;
1914
        this.start.column += column;
1915
        this.end.row += row;
1916
        this.end.column += column;
1917
    };
1918
1919
}).call(Range.prototype);
1920
Range.fromPoints = function(start, end) {
1921
    return new Range(start.row, start.column, end.row, end.column);
1922
};
1923
Range.comparePoints = comparePoints;
1924
1925
exports.Range = Range;
1926
});
1927
1928
define('ace/anchor', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/event_emitter'], function(require, exports, module) {
1929
1930
1931
var oop = require("./lib/oop");
1932
var EventEmitter = require("./lib/event_emitter").EventEmitter;
1933
1934
var Anchor = exports.Anchor = function(doc, row, column) {
1935
    this.document = doc;
1936
    
1937
    if (typeof column == "undefined")
1938
        this.setPosition(row.row, row.column);
1939
    else
1940
        this.setPosition(row, column);
1941
1942
    this.$onChange = this.onChange.bind(this);
1943
    doc.on("change", this.$onChange);
1944
};
1945
1946
(function() {
1947
1948
    oop.implement(this, EventEmitter);
1949
1950
    this.getPosition = function() {
1951
        return this.$clipPositionToDocument(this.row, this.column);
1952
    };
1953
        
1954
    this.getDocument = function() {
1955
        return this.document;
1956
    };
1957
1958
    this.onChange = function(e) {
1959
        var delta = e.data;
1960
        var range = delta.range;
1961
            
1962
        if (range.start.row == range.end.row && range.start.row != this.row)
1963
            return;
1964
            
1965
        if (range.start.row > this.row)
1966
            return;
1967
            
1968
        if (range.start.row == this.row && range.start.column > this.column)
1969
            return;
1970
    
1971
        var row = this.row;
1972
        var column = this.column;
1973
        
1974
        if (delta.action === "insertText") {
1975
            if (range.start.row === row && range.start.column <= column) {
1976
                if (range.start.row === range.end.row) {
1977
                    column += range.end.column - range.start.column;
1978
                }
1979
                else {
1980
                    column -= range.start.column;
1981
                    row += range.end.row - range.start.row;
1982
                }
1983
            }
1984
            else if (range.start.row !== range.end.row && range.start.row < row) {
1985
                row += range.end.row - range.start.row;
1986
            }
1987
        } else if (delta.action === "insertLines") {
1988
            if (range.start.row <= row) {
1989
                row += range.end.row - range.start.row;
1990
            }
1991
        }
1992
        else if (delta.action == "removeText") {
1993
            if (range.start.row == row && range.start.column < column) {
1994
                if (range.end.column >= column)
1995
                    column = range.start.column;
1996
                else
1997
                    column = Math.max(0, column - (range.end.column - range.start.column));
1998
                
1999
            } else if (range.start.row !== range.end.row && range.start.row < row) {
2000
                if (range.end.row == row) {
2001
                    column = Math.max(0, column - range.end.column) + range.start.column;
2002
                }
2003
                row -= (range.end.row - range.start.row);
2004
            }
2005
            else if (range.end.row == row) {
2006
                row -= range.end.row - range.start.row;
2007
                column = Math.max(0, column - range.end.column) + range.start.column;
2008
            }
2009
        } else if (delta.action == "removeLines") {
2010
            if (range.start.row <= row) {
2011
                if (range.end.row <= row)
2012
                    row -= range.end.row - range.start.row;
2013
                else {
2014
                    row = range.start.row;
2015
                    column = 0;
2016
                }
2017
            }
2018
        }
2019
2020
        this.setPosition(row, column, true);
2021
    };
2022
2023
    this.setPosition = function(row, column, noClip) {
2024
        var pos;
2025
        if (noClip) {
2026
            pos = {
2027
                row: row,
2028
                column: column
2029
            };
2030
        }
2031
        else {
2032
            pos = this.$clipPositionToDocument(row, column);
2033
        }
2034
        
2035
        if (this.row == pos.row && this.column == pos.column)
2036
            return;
2037
            
2038
        var old = {
2039
            row: this.row,
2040
            column: this.column
2041
        };
2042
        
2043
        this.row = pos.row;
2044
        this.column = pos.column;
2045
        this._emit("change", {
2046
            old: old,
2047
            value: pos
2048
        });
2049
    };
2050
2051
    this.detach = function() {
2052
        this.document.removeEventListener("change", this.$onChange);
2053
    };
2054
    this.$clipPositionToDocument = function(row, column) {
2055
        var pos = {};
2056
    
2057
        if (row >= this.document.getLength()) {
2058
            pos.row = Math.max(0, this.document.getLength() - 1);
2059
            pos.column = this.document.getLine(pos.row).length;
2060
        }
2061
        else if (row < 0) {
2062
            pos.row = 0;
2063
            pos.column = 0;
2064
        }
2065
        else {
2066
            pos.row = row;
2067
            pos.column = Math.min(this.document.getLine(pos.row).length, Math.max(0, column));
2068
        }
2069
        
2070
        if (column < 0)
2071
            pos.column = 0;
2072
            
2073
        return pos;
2074
    };
2075
    
2076
}).call(Anchor.prototype);
2077
2078
});
2079
define('ace/mode/css/csslint', ['require', 'exports', 'module' ], function(require, exports, module) {
2080
var parserlib = {};
2081
(function(){
2082
function EventTarget(){
2083
    this._listeners = {};    
2084
}
2085
2086
EventTarget.prototype = {
2087
    constructor: EventTarget,
2088
    addListener: function(type, listener){
2089
        if (!this._listeners[type]){
2090
            this._listeners[type] = [];
2091
        }
2092
2093
        this._listeners[type].push(listener);
2094
    },    
2095
    fire: function(event){
2096
        if (typeof event == "string"){
2097
            event = { type: event };
2098
        }
2099
        if (typeof event.target != "undefined"){
2100
            event.target = this;
2101
        }
2102
        
2103
        if (typeof event.type == "undefined"){
2104
            throw new Error("Event object missing 'type' property.");
2105
        }
2106
        
2107
        if (this._listeners[event.type]){
2108
            var listeners = this._listeners[event.type].concat();
2109
            for (var i=0, len=listeners.length; i < len; i++){
2110
                listeners[i].call(this, event);
2111
            }
2112
        }            
2113
    },
2114
    removeListener: function(type, listener){
2115
        if (this._listeners[type]){
2116
            var listeners = this._listeners[type];
2117
            for (var i=0, len=listeners.length; i < len; i++){
2118
                if (listeners[i] === listener){
2119
                    listeners.splice(i, 1);
2120
                    break;
2121
                }
2122
            }
2123
            
2124
            
2125
        }            
2126
    }
2127
};
2128
function StringReader(text){
2129
    this._input = text.replace(/\n\r?/g, "\n");
2130
    this._line = 1;
2131
    this._col = 1;
2132
    this._cursor = 0;
2133
}
2134
2135
StringReader.prototype = {
2136
    constructor: StringReader,
2137
    getCol: function(){
2138
        return this._col;
2139
    },
2140
    getLine: function(){
2141
        return this._line ;
2142
    },
2143
    eof: function(){
2144
        return (this._cursor == this._input.length);
2145
    },
2146
    peek: function(count){
2147
        var c = null;
2148
        count = (typeof count == "undefined" ? 1 : count);
2149
        if (this._cursor < this._input.length){
2150
            c = this._input.charAt(this._cursor + count - 1);
2151
        }
2152
2153
        return c;
2154
    },
2155
    read: function(){
2156
        var c = null;
2157
        if (this._cursor < this._input.length){
2158
            if (this._input.charAt(this._cursor) == "\n"){
2159
                this._line++;
2160
                this._col=1;
2161
            } else {
2162
                this._col++;
2163
            }
2164
            c = this._input.charAt(this._cursor++);
2165
        }
2166
2167
        return c;
2168
    },
2169
    mark: function(){
2170
        this._bookmark = {
2171
            cursor: this._cursor,
2172
            line:   this._line,
2173
            col:    this._col
2174
        };
2175
    },
2176
2177
    reset: function(){
2178
        if (this._bookmark){
2179
            this._cursor = this._bookmark.cursor;
2180
            this._line = this._bookmark.line;
2181
            this._col = this._bookmark.col;
2182
            delete this._bookmark;
2183
        }
2184
    },
2185
    readTo: function(pattern){
2186
2187
        var buffer = "",
2188
            c;
2189
        while (buffer.length < pattern.length || buffer.lastIndexOf(pattern) != buffer.length - pattern.length){
2190
            c = this.read();
2191
            if (c){
2192
                buffer += c;
2193
            } else {
2194
                throw new Error("Expected \"" + pattern + "\" at line " + this._line  + ", col " + this._col + ".");
2195
            }
2196
        }
2197
2198
        return buffer;
2199
2200
    },
2201
    readWhile: function(filter){
2202
2203
        var buffer = "",
2204
            c = this.read();
2205
2206
        while(c !== null && filter(c)){
2207
            buffer += c;
2208
            c = this.read();
2209
        }
2210
2211
        return buffer;
2212
2213
    },
2214
    readMatch: function(matcher){
2215
2216
        var source = this._input.substring(this._cursor),
2217
            value = null;
2218
        if (typeof matcher == "string"){
2219
            if (source.indexOf(matcher) === 0){
2220
                value = this.readCount(matcher.length);
2221
            }
2222
        } else if (matcher instanceof RegExp){
2223
            if (matcher.test(source)){
2224
                value = this.readCount(RegExp.lastMatch.length);
2225
            }
2226
        }
2227
2228
        return value;
2229
    },
2230
    readCount: function(count){
2231
        var buffer = "";
2232
2233
        while(count--){
2234
            buffer += this.read();
2235
        }
2236
2237
        return buffer;
2238
    }
2239
2240
};
2241
function SyntaxError(message, line, col){
2242
    this.col = col;
2243
    this.line = line;
2244
    this.message = message;
2245
2246
}
2247
SyntaxError.prototype = new Error();
2248
function SyntaxUnit(text, line, col, type){
2249
    this.col = col;
2250
    this.line = line;
2251
    this.text = text;
2252
    this.type = type;
2253
}
2254
SyntaxUnit.fromToken = function(token){
2255
    return new SyntaxUnit(token.value, token.startLine, token.startCol);
2256
};
2257
2258
SyntaxUnit.prototype = {
2259
    constructor: SyntaxUnit,
2260
    valueOf: function(){
2261
        return this.toString();
2262
    },
2263
    toString: function(){
2264
        return this.text;
2265
    }
2266
2267
};
2268
function TokenStreamBase(input, tokenData){
2269
    this._reader = input ? new StringReader(input.toString()) : null;
2270
    this._token = null;
2271
    this._tokenData = tokenData;
2272
    this._lt = [];
2273
    this._ltIndex = 0;
2274
    
2275
    this._ltIndexCache = [];
2276
}
2277
TokenStreamBase.createTokenData = function(tokens){
2278
2279
    var nameMap     = [],
2280
        typeMap     = {},
2281
        tokenData     = tokens.concat([]),
2282
        i            = 0,
2283
        len            = tokenData.length+1;
2284
    
2285
    tokenData.UNKNOWN = -1;
2286
    tokenData.unshift({name:"EOF"});
2287
2288
    for (; i < len; i++){
2289
        nameMap.push(tokenData[i].name);
2290
        tokenData[tokenData[i].name] = i;
2291
        if (tokenData[i].text){
2292
            typeMap[tokenData[i].text] = i;
2293
        }
2294
    }
2295
    
2296
    tokenData.name = function(tt){
2297
        return nameMap[tt];
2298
    };
2299
    
2300
    tokenData.type = function(c){
2301
        return typeMap[c];
2302
    };
2303
    
2304
    return tokenData;
2305
};
2306
2307
TokenStreamBase.prototype = {
2308
    constructor: TokenStreamBase,    
2309
    match: function(tokenTypes, channel){
2310
        if (!(tokenTypes instanceof Array)){
2311
            tokenTypes = [tokenTypes];
2312
        }
2313
                
2314
        var tt  = this.get(channel),
2315
            i   = 0,
2316
            len = tokenTypes.length;
2317
            
2318
        while(i < len){
2319
            if (tt == tokenTypes[i++]){
2320
                return true;
2321
            }
2322
        }
2323
        this.unget();
2324
        return false;
2325
    },        
2326
    mustMatch: function(tokenTypes, channel){
2327
2328
        var token;
2329
        if (!(tokenTypes instanceof Array)){
2330
            tokenTypes = [tokenTypes];
2331
        }
2332
2333
        if (!this.match.apply(this, arguments)){    
2334
            token = this.LT(1);
2335
            throw new SyntaxError("Expected " + this._tokenData[tokenTypes[0]].name + 
2336
                " at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
2337
        }
2338
    },
2339
    advance: function(tokenTypes, channel){
2340
        
2341
        while(this.LA(0) !== 0 && !this.match(tokenTypes, channel)){
2342
            this.get();
2343
        }
2344
2345
        return this.LA(0);    
2346
    },      
2347
    get: function(channel){
2348
    
2349
        var tokenInfo   = this._tokenData,
2350
            reader      = this._reader,
2351
            value,
2352
            i           =0,
2353
            len         = tokenInfo.length,
2354
            found       = false,
2355
            token,
2356
            info;
2357
        if (this._lt.length && this._ltIndex >= 0 && this._ltIndex < this._lt.length){  
2358
                           
2359
            i++;
2360
            this._token = this._lt[this._ltIndex++];
2361
            info = tokenInfo[this._token.type];
2362
            while((info.channel !== undefined && channel !== info.channel) &&
2363
                    this._ltIndex < this._lt.length){
2364
                this._token = this._lt[this._ltIndex++];
2365
                info = tokenInfo[this._token.type];
2366
                i++;
2367
            }
2368
            if ((info.channel === undefined || channel === info.channel) &&
2369
                    this._ltIndex <= this._lt.length){
2370
                this._ltIndexCache.push(i);
2371
                return this._token.type;
2372
            }
2373
        }
2374
        token = this._getToken();
2375
        if (token.type > -1 && !tokenInfo[token.type].hide){
2376
            token.channel = tokenInfo[token.type].channel;
2377
            this._token = token;
2378
            this._lt.push(token);
2379
            this._ltIndexCache.push(this._lt.length - this._ltIndex + i);  
2380
            if (this._lt.length > 5){
2381
                this._lt.shift();                
2382
            }
2383
            if (this._ltIndexCache.length > 5){
2384
                this._ltIndexCache.shift();
2385
            }
2386
            this._ltIndex = this._lt.length;
2387
        }
2388
        info = tokenInfo[token.type];
2389
        if (info && 
2390
                (info.hide || 
2391
                (info.channel !== undefined && channel !== info.channel))){
2392
            return this.get(channel);
2393
        } else {
2394
            return token.type;
2395
        }
2396
    },
2397
    LA: function(index){
2398
        var total = index,
2399
            tt;
2400
        if (index > 0){
2401
            if (index > 5){
2402
                throw new Error("Too much lookahead.");
2403
            }
2404
            while(total){
2405
                tt = this.get();   
2406
                total--;                            
2407
            }
2408
            while(total < index){
2409
                this.unget();
2410
                total++;
2411
            }
2412
        } else if (index < 0){
2413
        
2414
            if(this._lt[this._ltIndex+index]){
2415
                tt = this._lt[this._ltIndex+index].type;
2416
            } else {
2417
                throw new Error("Too much lookbehind.");
2418
            }
2419
        
2420
        } else {
2421
            tt = this._token.type;
2422
        }
2423
        
2424
        return tt;
2425
    
2426
    },    
2427
    LT: function(index){
2428
        this.LA(index);
2429
        return this._lt[this._ltIndex+index-1];    
2430
    },
2431
    peek: function(){
2432
        return this.LA(1);
2433
    },
2434
    token: function(){
2435
        return this._token;
2436
    },
2437
    tokenName: function(tokenType){
2438
        if (tokenType < 0 || tokenType > this._tokenData.length){
2439
            return "UNKNOWN_TOKEN";
2440
        } else {
2441
            return this._tokenData[tokenType].name;
2442
        }
2443
    },    
2444
    tokenType: function(tokenName){
2445
        return this._tokenData[tokenName] || -1;
2446
    },      
2447
    unget: function(){
2448
        if (this._ltIndexCache.length){
2449
            this._ltIndex -= this._ltIndexCache.pop();//--;
2450
            this._token = this._lt[this._ltIndex - 1];
2451
        } else {
2452
            throw new Error("Too much lookahead.");
2453
        }
2454
    }
2455
2456
};
2457
2458
2459
2460
2461
parserlib.util = {
2462
StringReader: StringReader,
2463
SyntaxError : SyntaxError,
2464
SyntaxUnit  : SyntaxUnit,
2465
EventTarget : EventTarget,
2466
TokenStreamBase : TokenStreamBase
2467
};
2468
})();
2469
(function(){
2470
var EventTarget = parserlib.util.EventTarget,
2471
TokenStreamBase = parserlib.util.TokenStreamBase,
2472
StringReader = parserlib.util.StringReader,
2473
SyntaxError = parserlib.util.SyntaxError,
2474
SyntaxUnit  = parserlib.util.SyntaxUnit;
2475
2476
2477
var Colors = {
2478
    aliceblue       :"#f0f8ff",
2479
    antiquewhite    :"#faebd7",
2480
    aqua            :"#00ffff",
2481
    aquamarine      :"#7fffd4",
2482
    azure           :"#f0ffff",
2483
    beige           :"#f5f5dc",
2484
    bisque          :"#ffe4c4",
2485
    black           :"#000000",
2486
    blanchedalmond  :"#ffebcd",
2487
    blue            :"#0000ff",
2488
    blueviolet      :"#8a2be2",
2489
    brown           :"#a52a2a",
2490
    burlywood       :"#deb887",
2491
    cadetblue       :"#5f9ea0",
2492
    chartreuse      :"#7fff00",
2493
    chocolate       :"#d2691e",
2494
    coral           :"#ff7f50",
2495
    cornflowerblue  :"#6495ed",
2496
    cornsilk        :"#fff8dc",
2497
    crimson         :"#dc143c",
2498
    cyan            :"#00ffff",
2499
    darkblue        :"#00008b",
2500
    darkcyan        :"#008b8b",
2501
    darkgoldenrod   :"#b8860b",
2502
    darkgray        :"#a9a9a9",
2503
    darkgreen       :"#006400",
2504
    darkkhaki       :"#bdb76b",
2505
    darkmagenta     :"#8b008b",
2506
    darkolivegreen  :"#556b2f",
2507
    darkorange      :"#ff8c00",
2508
    darkorchid      :"#9932cc",
2509
    darkred         :"#8b0000",
2510
    darksalmon      :"#e9967a",
2511
    darkseagreen    :"#8fbc8f",
2512
    darkslateblue   :"#483d8b",
2513
    darkslategray   :"#2f4f4f",
2514
    darkturquoise   :"#00ced1",
2515
    darkviolet      :"#9400d3",
2516
    deeppink        :"#ff1493",
2517
    deepskyblue     :"#00bfff",
2518
    dimgray         :"#696969",
2519
    dodgerblue      :"#1e90ff",
2520
    firebrick       :"#b22222",
2521
    floralwhite     :"#fffaf0",
2522
    forestgreen     :"#228b22",
2523
    fuchsia         :"#ff00ff",
2524
    gainsboro       :"#dcdcdc",
2525
    ghostwhite      :"#f8f8ff",
2526
    gold            :"#ffd700",
2527
    goldenrod       :"#daa520",
2528
    gray            :"#808080",
2529
    green           :"#008000",
2530
    greenyellow     :"#adff2f",
2531
    honeydew        :"#f0fff0",
2532
    hotpink         :"#ff69b4",
2533
    indianred       :"#cd5c5c",
2534
    indigo          :"#4b0082",
2535
    ivory           :"#fffff0",
2536
    khaki           :"#f0e68c",
2537
    lavender        :"#e6e6fa",
2538
    lavenderblush   :"#fff0f5",
2539
    lawngreen       :"#7cfc00",
2540
    lemonchiffon    :"#fffacd",
2541
    lightblue       :"#add8e6",
2542
    lightcoral      :"#f08080",
2543
    lightcyan       :"#e0ffff",
2544
    lightgoldenrodyellow  :"#fafad2",
2545
    lightgray       :"#d3d3d3",
2546
    lightgreen      :"#90ee90",
2547
    lightpink       :"#ffb6c1",
2548
    lightsalmon     :"#ffa07a",
2549
    lightseagreen   :"#20b2aa",
2550
    lightskyblue    :"#87cefa",
2551
    lightslategray  :"#778899",
2552
    lightsteelblue  :"#b0c4de",
2553
    lightyellow     :"#ffffe0",
2554
    lime            :"#00ff00",
2555
    limegreen       :"#32cd32",
2556
    linen           :"#faf0e6",
2557
    magenta         :"#ff00ff",
2558
    maroon          :"#800000",
2559
    mediumaquamarine:"#66cdaa",
2560
    mediumblue      :"#0000cd",
2561
    mediumorchid    :"#ba55d3",
2562
    mediumpurple    :"#9370d8",
2563
    mediumseagreen  :"#3cb371",
2564
    mediumslateblue :"#7b68ee",
2565
    mediumspringgreen   :"#00fa9a",
2566
    mediumturquoise :"#48d1cc",
2567
    mediumvioletred :"#c71585",
2568
    midnightblue    :"#191970",
2569
    mintcream       :"#f5fffa",
2570
    mistyrose       :"#ffe4e1",
2571
    moccasin        :"#ffe4b5",
2572
    navajowhite     :"#ffdead",
2573
    navy            :"#000080",
2574
    oldlace         :"#fdf5e6",
2575
    olive           :"#808000",
2576
    olivedrab       :"#6b8e23",
2577
    orange          :"#ffa500",
2578
    orangered       :"#ff4500",
2579
    orchid          :"#da70d6",
2580
    palegoldenrod   :"#eee8aa",
2581
    palegreen       :"#98fb98",
2582
    paleturquoise   :"#afeeee",
2583
    palevioletred   :"#d87093",
2584
    papayawhip      :"#ffefd5",
2585
    peachpuff       :"#ffdab9",
2586
    peru            :"#cd853f",
2587
    pink            :"#ffc0cb",
2588
    plum            :"#dda0dd",
2589
    powderblue      :"#b0e0e6",
2590
    purple          :"#800080",
2591
    red             :"#ff0000",
2592
    rosybrown       :"#bc8f8f",
2593
    royalblue       :"#4169e1",
2594
    saddlebrown     :"#8b4513",
2595
    salmon          :"#fa8072",
2596
    sandybrown      :"#f4a460",
2597
    seagreen        :"#2e8b57",
2598
    seashell        :"#fff5ee",
2599
    sienna          :"#a0522d",
2600
    silver          :"#c0c0c0",
2601
    skyblue         :"#87ceeb",
2602
    slateblue       :"#6a5acd",
2603
    slategray       :"#708090",
2604
    snow            :"#fffafa",
2605
    springgreen     :"#00ff7f",
2606
    steelblue       :"#4682b4",
2607
    tan             :"#d2b48c",
2608
    teal            :"#008080",
2609
    thistle         :"#d8bfd8",
2610
    tomato          :"#ff6347",
2611
    turquoise       :"#40e0d0",
2612
    violet          :"#ee82ee",
2613
    wheat           :"#f5deb3",
2614
    white           :"#ffffff",
2615
    whitesmoke      :"#f5f5f5",
2616
    yellow          :"#ffff00",
2617
    yellowgreen     :"#9acd32"
2618
};
2619
function Combinator(text, line, col){
2620
    
2621
    SyntaxUnit.call(this, text, line, col, Parser.COMBINATOR_TYPE);
2622
    this.type = "unknown";
2623
    if (/^\s+$/.test(text)){
2624
        this.type = "descendant";
2625
    } else if (text == ">"){
2626
        this.type = "child";
2627
    } else if (text == "+"){
2628
        this.type = "adjacent-sibling";
2629
    } else if (text == "~"){
2630
        this.type = "sibling";
2631
    }
2632
2633
}
2634
2635
Combinator.prototype = new SyntaxUnit();
2636
Combinator.prototype.constructor = Combinator;
2637
function MediaFeature(name, value){
2638
    
2639
    SyntaxUnit.call(this, "(" + name + (value !== null ? ":" + value : "") + ")", name.startLine, name.startCol, Parser.MEDIA_FEATURE_TYPE);
2640
    this.name = name;
2641
    this.value = value;
2642
}
2643
2644
MediaFeature.prototype = new SyntaxUnit();
2645
MediaFeature.prototype.constructor = MediaFeature;
2646
function MediaQuery(modifier, mediaType, features, line, col){
2647
    
2648
    SyntaxUnit.call(this, (modifier ? modifier + " ": "") + (mediaType ? mediaType + " " : "") + features.join(" and "), line, col, Parser.MEDIA_QUERY_TYPE);
2649
    this.modifier = modifier;
2650
    this.mediaType = mediaType;
2651
    this.features = features;
2652
2653
}
2654
2655
MediaQuery.prototype = new SyntaxUnit();
2656
MediaQuery.prototype.constructor = MediaQuery;
2657
function Parser(options){
2658
    EventTarget.call(this);
2659
2660
2661
    this.options = options || {};
2662
2663
    this._tokenStream = null;
2664
}
2665
Parser.DEFAULT_TYPE = 0;
2666
Parser.COMBINATOR_TYPE = 1;
2667
Parser.MEDIA_FEATURE_TYPE = 2;
2668
Parser.MEDIA_QUERY_TYPE = 3;
2669
Parser.PROPERTY_NAME_TYPE = 4;
2670
Parser.PROPERTY_VALUE_TYPE = 5;
2671
Parser.PROPERTY_VALUE_PART_TYPE = 6;
2672
Parser.SELECTOR_TYPE = 7;
2673
Parser.SELECTOR_PART_TYPE = 8;
2674
Parser.SELECTOR_SUB_PART_TYPE = 9;
2675
2676
Parser.prototype = function(){
2677
2678
    var proto = new EventTarget(),  //new prototype
2679
        prop,
2680
        additions =  {
2681
            constructor: Parser,
2682
            DEFAULT_TYPE : 0,
2683
            COMBINATOR_TYPE : 1,
2684
            MEDIA_FEATURE_TYPE : 2,
2685
            MEDIA_QUERY_TYPE : 3,
2686
            PROPERTY_NAME_TYPE : 4,
2687
            PROPERTY_VALUE_TYPE : 5,
2688
            PROPERTY_VALUE_PART_TYPE : 6,
2689
            SELECTOR_TYPE : 7,
2690
            SELECTOR_PART_TYPE : 8,
2691
            SELECTOR_SUB_PART_TYPE : 9,            
2692
        
2693
            _stylesheet: function(){ 
2694
               
2695
                var tokenStream = this._tokenStream,
2696
                    charset     = null,
2697
                    count,
2698
                    token,
2699
                    tt;
2700
                    
2701
                this.fire("startstylesheet");
2702
                this._charset();
2703
                
2704
                this._skipCruft();
2705
                while (tokenStream.peek() == Tokens.IMPORT_SYM){
2706
                    this._import();
2707
                    this._skipCruft();
2708
                }
2709
                while (tokenStream.peek() == Tokens.NAMESPACE_SYM){
2710
                    this._namespace();
2711
                    this._skipCruft();
2712
                }
2713
                tt = tokenStream.peek();
2714
                while(tt > Tokens.EOF){
2715
                
2716
                    try {
2717
                
2718
                        switch(tt){
2719
                            case Tokens.MEDIA_SYM:
2720
                                this._media();
2721
                                this._skipCruft();
2722
                                break;
2723
                            case Tokens.PAGE_SYM:
2724
                                this._page(); 
2725
                                this._skipCruft();
2726
                                break;                   
2727
                            case Tokens.FONT_FACE_SYM:
2728
                                this._font_face(); 
2729
                                this._skipCruft();
2730
                                break;  
2731
                            case Tokens.KEYFRAMES_SYM:
2732
                                this._keyframes(); 
2733
                                this._skipCruft();
2734
                                break;                                
2735
                            case Tokens.UNKNOWN_SYM:  //unknown @ rule
2736
                                tokenStream.get();
2737
                                if (!this.options.strict){
2738
                                    this.fire({
2739
                                        type:       "error",
2740
                                        error:      null,
2741
                                        message:    "Unknown @ rule: " + tokenStream.LT(0).value + ".",
2742
                                        line:       tokenStream.LT(0).startLine,
2743
                                        col:        tokenStream.LT(0).startCol
2744
                                    });                          
2745
                                    count=0;
2746
                                    while (tokenStream.advance([Tokens.LBRACE, Tokens.RBRACE]) == Tokens.LBRACE){
2747
                                        count++;    //keep track of nesting depth
2748
                                    }
2749
                                    
2750
                                    while(count){
2751
                                        tokenStream.advance([Tokens.RBRACE]);
2752
                                        count--;
2753
                                    }
2754
                                    
2755
                                } else {
2756
                                    throw new SyntaxError("Unknown @ rule.", tokenStream.LT(0).startLine, tokenStream.LT(0).startCol);
2757
                                }                                
2758
                                break;
2759
                            case Tokens.S:
2760
                                this._readWhitespace();
2761
                                break;
2762
                            default:                            
2763
                                if(!this._ruleset()){
2764
                                    switch(tt){
2765
                                        case Tokens.CHARSET_SYM:
2766
                                            token = tokenStream.LT(1);
2767
                                            this._charset(false);
2768
                                            throw new SyntaxError("@charset not allowed here.", token.startLine, token.startCol);
2769
                                        case Tokens.IMPORT_SYM:
2770
                                            token = tokenStream.LT(1);
2771
                                            this._import(false);
2772
                                            throw new SyntaxError("@import not allowed here.", token.startLine, token.startCol);
2773
                                        case Tokens.NAMESPACE_SYM:
2774
                                            token = tokenStream.LT(1);
2775
                                            this._namespace(false);
2776
                                            throw new SyntaxError("@namespace not allowed here.", token.startLine, token.startCol);
2777
                                        default:
2778
                                            tokenStream.get();  //get the last token
2779
                                            this._unexpectedToken(tokenStream.token());
2780
                                    }
2781
                                
2782
                                }
2783
                        }
2784
                    } catch(ex) {
2785
                        if (ex instanceof SyntaxError && !this.options.strict){
2786
                            this.fire({
2787
                                type:       "error",
2788
                                error:      ex,
2789
                                message:    ex.message,
2790
                                line:       ex.line,
2791
                                col:        ex.col
2792
                            });                     
2793
                        } else {
2794
                            throw ex;
2795
                        }
2796
                    }
2797
                    
2798
                    tt = tokenStream.peek();
2799
                }
2800
                
2801
                if (tt != Tokens.EOF){
2802
                    this._unexpectedToken(tokenStream.token());
2803
                }
2804
            
2805
                this.fire("endstylesheet");
2806
            },
2807
            
2808
            _charset: function(emit){
2809
                var tokenStream = this._tokenStream,
2810
                    charset,
2811
                    token,
2812
                    line,
2813
                    col;
2814
                    
2815
                if (tokenStream.match(Tokens.CHARSET_SYM)){
2816
                    line = tokenStream.token().startLine;
2817
                    col = tokenStream.token().startCol;
2818
                
2819
                    this._readWhitespace();
2820
                    tokenStream.mustMatch(Tokens.STRING);
2821
                    
2822
                    token = tokenStream.token();
2823
                    charset = token.value;
2824
                    
2825
                    this._readWhitespace();
2826
                    tokenStream.mustMatch(Tokens.SEMICOLON);
2827
                    
2828
                    if (emit !== false){
2829
                        this.fire({ 
2830
                            type:   "charset",
2831
                            charset:charset,
2832
                            line:   line,
2833
                            col:    col
2834
                        });
2835
                    }
2836
                }            
2837
            },
2838
            
2839
            _import: function(emit){    
2840
            
2841
                var tokenStream = this._tokenStream,
2842
                    tt,
2843
                    uri,
2844
                    importToken,
2845
                    mediaList   = [];
2846
                tokenStream.mustMatch(Tokens.IMPORT_SYM);
2847
                importToken = tokenStream.token();
2848
                this._readWhitespace();
2849
                
2850
                tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
2851
                uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1");                
2852
2853
                this._readWhitespace();
2854
                
2855
                mediaList = this._media_query_list();
2856
                tokenStream.mustMatch(Tokens.SEMICOLON);
2857
                this._readWhitespace();
2858
                
2859
                if (emit !== false){
2860
                    this.fire({
2861
                        type:   "import",
2862
                        uri:    uri,
2863
                        media:  mediaList,
2864
                        line:   importToken.startLine,
2865
                        col:    importToken.startCol
2866
                    });
2867
                }
2868
        
2869
            },
2870
            
2871
            _namespace: function(emit){    
2872
            
2873
                var tokenStream = this._tokenStream,
2874
                    line,
2875
                    col,
2876
                    prefix,
2877
                    uri;
2878
                tokenStream.mustMatch(Tokens.NAMESPACE_SYM);
2879
                line = tokenStream.token().startLine;
2880
                col = tokenStream.token().startCol;
2881
                this._readWhitespace();
2882
                if (tokenStream.match(Tokens.IDENT)){
2883
                    prefix = tokenStream.token().value;
2884
                    this._readWhitespace();
2885
                }
2886
                
2887
                tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
2888
                uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1");                
2889
2890
                this._readWhitespace();
2891
                tokenStream.mustMatch(Tokens.SEMICOLON);
2892
                this._readWhitespace();
2893
                
2894
                if (emit !== false){
2895
                    this.fire({
2896
                        type:   "namespace",
2897
                        prefix: prefix,
2898
                        uri:    uri,
2899
                        line:   line,
2900
                        col:    col
2901
                    });
2902
                }
2903
        
2904
            },            
2905
                       
2906
            _media: function(){
2907
                var tokenStream     = this._tokenStream,
2908
                    line,
2909
                    col,
2910
                    mediaList;//       = [];
2911
                tokenStream.mustMatch(Tokens.MEDIA_SYM);
2912
                line = tokenStream.token().startLine;
2913
                col = tokenStream.token().startCol;
2914
                
2915
                this._readWhitespace();               
2916
2917
                mediaList = this._media_query_list();
2918
2919
                tokenStream.mustMatch(Tokens.LBRACE);
2920
                this._readWhitespace();
2921
                
2922
                this.fire({
2923
                    type:   "startmedia",
2924
                    media:  mediaList,
2925
                    line:   line,
2926
                    col:    col
2927
                });
2928
                
2929
                while(true) {
2930
                    if (tokenStream.peek() == Tokens.PAGE_SYM){
2931
                        this._page();
2932
                    } else if (!this._ruleset()){
2933
                        break;
2934
                    }                
2935
                }
2936
                
2937
                tokenStream.mustMatch(Tokens.RBRACE);
2938
                this._readWhitespace();
2939
        
2940
                this.fire({
2941
                    type:   "endmedia",
2942
                    media:  mediaList,
2943
                    line:   line,
2944
                    col:    col
2945
                });
2946
            },                           
2947
            _media_query_list: function(){
2948
                var tokenStream = this._tokenStream,
2949
                    mediaList   = [];
2950
                
2951
                
2952
                this._readWhitespace();
2953
                
2954
                if (tokenStream.peek() == Tokens.IDENT || tokenStream.peek() == Tokens.LPAREN){
2955
                    mediaList.push(this._media_query());
2956
                }
2957
                
2958
                while(tokenStream.match(Tokens.COMMA)){
2959
                    this._readWhitespace();
2960
                    mediaList.push(this._media_query());
2961
                }
2962
                
2963
                return mediaList;
2964
            },
2965
            _media_query: function(){
2966
                var tokenStream = this._tokenStream,
2967
                    type        = null,
2968
                    ident       = null,
2969
                    token       = null,
2970
                    expressions = [];
2971
                    
2972
                if (tokenStream.match(Tokens.IDENT)){
2973
                    ident = tokenStream.token().value.toLowerCase();
2974
                    if (ident != "only" && ident != "not"){
2975
                        tokenStream.unget();
2976
                        ident = null;
2977
                    } else {
2978
                        token = tokenStream.token();
2979
                    }
2980
                }
2981
                                
2982
                this._readWhitespace();
2983
                
2984
                if (tokenStream.peek() == Tokens.IDENT){
2985
                    type = this._media_type();
2986
                    if (token === null){
2987
                        token = tokenStream.token();
2988
                    }
2989
                } else if (tokenStream.peek() == Tokens.LPAREN){
2990
                    if (token === null){
2991
                        token = tokenStream.LT(1);
2992
                    }
2993
                    expressions.push(this._media_expression());
2994
                }                               
2995
                
2996
                if (type === null && expressions.length === 0){
2997
                    return null;
2998
                } else {                
2999
                    this._readWhitespace();
3000
                    while (tokenStream.match(Tokens.IDENT)){
3001
                        if (tokenStream.token().value.toLowerCase() != "and"){
3002
                            this._unexpectedToken(tokenStream.token());
3003
                        }
3004
                        
3005
                        this._readWhitespace();
3006
                        expressions.push(this._media_expression());
3007
                    }
3008
                }
3009
3010
                return new MediaQuery(ident, type, expressions, token.startLine, token.startCol);
3011
            },
3012
            _media_type: function(){
3013
                return this._media_feature();           
3014
            },
3015
            _media_expression: function(){
3016
                var tokenStream = this._tokenStream,
3017
                    feature     = null,
3018
                    token,
3019
                    expression  = null;
3020
                
3021
                tokenStream.mustMatch(Tokens.LPAREN);
3022
                
3023
                feature = this._media_feature();
3024
                this._readWhitespace();
3025
                
3026
                if (tokenStream.match(Tokens.COLON)){
3027
                    this._readWhitespace();
3028
                    token = tokenStream.LT(1);
3029
                    expression = this._expression();
3030
                }
3031
                
3032
                tokenStream.mustMatch(Tokens.RPAREN);
3033
                this._readWhitespace();
3034
3035
                return new MediaFeature(feature, (expression ? new SyntaxUnit(expression, token.startLine, token.startCol) : null));            
3036
            },
3037
            _media_feature: function(){
3038
                var tokenStream = this._tokenStream;
3039
                    
3040
                tokenStream.mustMatch(Tokens.IDENT);
3041
                
3042
                return SyntaxUnit.fromToken(tokenStream.token());            
3043
            },
3044
            _page: function(){            
3045
                var tokenStream = this._tokenStream,
3046
                    line,
3047
                    col,
3048
                    identifier  = null,
3049
                    pseudoPage  = null;
3050
                tokenStream.mustMatch(Tokens.PAGE_SYM);
3051
                line = tokenStream.token().startLine;
3052
                col = tokenStream.token().startCol;
3053
                
3054
                this._readWhitespace();
3055
                
3056
                if (tokenStream.match(Tokens.IDENT)){
3057
                    identifier = tokenStream.token().value;
3058
                    if (identifier.toLowerCase() === "auto"){
3059
                        this._unexpectedToken(tokenStream.token());
3060
                    }
3061
                }                
3062
                if (tokenStream.peek() == Tokens.COLON){
3063
                    pseudoPage = this._pseudo_page();
3064
                }
3065
            
3066
                this._readWhitespace();
3067
                
3068
                this.fire({
3069
                    type:   "startpage",
3070
                    id:     identifier,
3071
                    pseudo: pseudoPage,
3072
                    line:   line,
3073
                    col:    col
3074
                });                   
3075
3076
                this._readDeclarations(true, true);                
3077
                
3078
                this.fire({
3079
                    type:   "endpage",
3080
                    id:     identifier,
3081
                    pseudo: pseudoPage,
3082
                    line:   line,
3083
                    col:    col
3084
                });             
3085
            
3086
            },
3087
            _margin: function(){
3088
                var tokenStream = this._tokenStream,
3089
                    line,
3090
                    col,
3091
                    marginSym   = this._margin_sym();
3092
3093
                if (marginSym){
3094
                    line = tokenStream.token().startLine;
3095
                    col = tokenStream.token().startCol;
3096
                
3097
                    this.fire({
3098
                        type: "startpagemargin",
3099
                        margin: marginSym,
3100
                        line:   line,
3101
                        col:    col
3102
                    });    
3103
                    
3104
                    this._readDeclarations(true);
3105
3106
                    this.fire({
3107
                        type: "endpagemargin",
3108
                        margin: marginSym,
3109
                        line:   line,
3110
                        col:    col
3111
                    });    
3112
                    return true;
3113
                } else {
3114
                    return false;
3115
                }
3116
            },
3117
            _margin_sym: function(){
3118
            
3119
                var tokenStream = this._tokenStream;
3120
            
3121
                if(tokenStream.match([Tokens.TOPLEFTCORNER_SYM, Tokens.TOPLEFT_SYM,
3122
                        Tokens.TOPCENTER_SYM, Tokens.TOPRIGHT_SYM, Tokens.TOPRIGHTCORNER_SYM,
3123
                        Tokens.BOTTOMLEFTCORNER_SYM, Tokens.BOTTOMLEFT_SYM, 
3124
                        Tokens.BOTTOMCENTER_SYM, Tokens.BOTTOMRIGHT_SYM,
3125
                        Tokens.BOTTOMRIGHTCORNER_SYM, Tokens.LEFTTOP_SYM, 
3126
                        Tokens.LEFTMIDDLE_SYM, Tokens.LEFTBOTTOM_SYM, Tokens.RIGHTTOP_SYM,
3127
                        Tokens.RIGHTMIDDLE_SYM, Tokens.RIGHTBOTTOM_SYM]))
3128
                {
3129
                    return SyntaxUnit.fromToken(tokenStream.token());                
3130
                } else {
3131
                    return null;
3132
                }
3133
            
3134
            },
3135
            
3136
            _pseudo_page: function(){
3137
        
3138
                var tokenStream = this._tokenStream;
3139
                
3140
                tokenStream.mustMatch(Tokens.COLON);
3141
                tokenStream.mustMatch(Tokens.IDENT);
3142
                
3143
                return tokenStream.token().value;
3144
            },
3145
            
3146
            _font_face: function(){     
3147
                var tokenStream = this._tokenStream,
3148
                    line,
3149
                    col;
3150
                tokenStream.mustMatch(Tokens.FONT_FACE_SYM);
3151
                line = tokenStream.token().startLine;
3152
                col = tokenStream.token().startCol;
3153
                
3154
                this._readWhitespace();
3155
3156
                this.fire({
3157
                    type:   "startfontface",
3158
                    line:   line,
3159
                    col:    col
3160
                });                    
3161
                
3162
                this._readDeclarations(true);
3163
                
3164
                this.fire({
3165
                    type:   "endfontface",
3166
                    line:   line,
3167
                    col:    col
3168
                });              
3169
            },
3170
3171
            _operator: function(){    
3172
                 
3173
                var tokenStream = this._tokenStream,
3174
                    token       = null;
3175
                
3176
                if (tokenStream.match([Tokens.SLASH, Tokens.COMMA])){
3177
                    token =  tokenStream.token();
3178
                    this._readWhitespace();
3179
                } 
3180
                return token ? PropertyValuePart.fromToken(token) : null;
3181
                
3182
            },
3183
            
3184
            _combinator: function(){    
3185
                 
3186
                var tokenStream = this._tokenStream,
3187
                    value       = null,
3188
                    token;
3189
                
3190
                if(tokenStream.match([Tokens.PLUS, Tokens.GREATER, Tokens.TILDE])){                
3191
                    token = tokenStream.token();
3192
                    value = new Combinator(token.value, token.startLine, token.startCol);
3193
                    this._readWhitespace();
3194
                }
3195
                
3196
                return value;
3197
            },
3198
            
3199
            _unary_operator: function(){
3200
                 
3201
                var tokenStream = this._tokenStream;
3202
                
3203
                if (tokenStream.match([Tokens.MINUS, Tokens.PLUS])){
3204
                    return tokenStream.token().value;
3205
                } else {
3206
                    return null;
3207
                }         
3208
            },
3209
            
3210
            _property: function(){
3211
                 
3212
                var tokenStream = this._tokenStream,
3213
                    value       = null,
3214
                    hack        = null,
3215
                    tokenValue,
3216
                    token,
3217
                    line,
3218
                    col;
3219
                if (tokenStream.peek() == Tokens.STAR && this.options.starHack){
3220
                    tokenStream.get();
3221
                    token = tokenStream.token();
3222
                    hack = token.value;
3223
                    line = token.startLine;
3224
                    col = token.startCol;
3225
                }
3226
                
3227
                if(tokenStream.match(Tokens.IDENT)){
3228
                    token = tokenStream.token();
3229
                    tokenValue = token.value;
3230
                    if (tokenValue.charAt(0) == "_" && this.options.underscoreHack){
3231
                        hack = "_";
3232
                        tokenValue = tokenValue.substring(1);
3233
                    }
3234
                    
3235
                    value = new PropertyName(tokenValue, hack, (line||token.startLine), (col||token.startCol));
3236
                    this._readWhitespace();
3237
                }
3238
                
3239
                return value;
3240
            },
3241
            _ruleset: function(){    
3242
                 
3243
                var tokenStream = this._tokenStream,
3244
                    tt,
3245
                    selectors;
3246
                try {
3247
                    selectors = this._selectors_group();
3248
                } catch (ex){
3249
                    if (ex instanceof SyntaxError && !this.options.strict){
3250
                        this.fire({
3251
                            type:       "error",
3252
                            error:      ex,
3253
                            message:    ex.message,
3254
                            line:       ex.line,
3255
                            col:        ex.col
3256
                        });                          
3257
                        tt = tokenStream.advance([Tokens.RBRACE]);
3258
                        if (tt == Tokens.RBRACE){
3259
                        } else {
3260
                            throw ex;
3261
                        }                        
3262
                        
3263
                    } else {
3264
                        throw ex;
3265
                    }                
3266
                    return true;
3267
                }
3268
                if (selectors){ 
3269
                                    
3270
                    this.fire({
3271
                        type:       "startrule",
3272
                        selectors:  selectors,
3273
                        line:       selectors[0].line,
3274
                        col:        selectors[0].col
3275
                    });                
3276
                    
3277
                    this._readDeclarations(true);                
3278
                    
3279
                    this.fire({
3280
                        type:       "endrule",
3281
                        selectors:  selectors,
3282
                        line:       selectors[0].line,
3283
                        col:        selectors[0].col
3284
                    });  
3285
                    
3286
                }
3287
                
3288
                return selectors;
3289
                
3290
            },
3291
            _selectors_group: function(){           
3292
                var tokenStream = this._tokenStream,
3293
                    selectors   = [],
3294
                    selector;
3295
                    
3296
                selector = this._selector();
3297
                if (selector !== null){
3298
                
3299
                    selectors.push(selector);
3300
                    while(tokenStream.match(Tokens.COMMA)){
3301
                        this._readWhitespace();
3302
                        selector = this._selector();
3303
                        if (selector !== null){
3304
                            selectors.push(selector);
3305
                        } else {
3306
                            this._unexpectedToken(tokenStream.LT(1));
3307
                        }
3308
                    }
3309
                }
3310
3311
                return selectors.length ? selectors : null;
3312
            },
3313
            _selector: function(){
3314
                 
3315
                var tokenStream = this._tokenStream,
3316
                    selector    = [],
3317
                    nextSelector = null,
3318
                    combinator  = null,
3319
                    ws          = null;
3320
                nextSelector = this._simple_selector_sequence();
3321
                if (nextSelector === null){
3322
                    return null;
3323
                }
3324
                
3325
                selector.push(nextSelector);
3326
                
3327
                do {
3328
                    combinator = this._combinator();
3329
                    
3330
                    if (combinator !== null){
3331
                        selector.push(combinator);
3332
                        nextSelector = this._simple_selector_sequence();
3333
                        if (nextSelector === null){
3334
                            this._unexpectedToken(tokenStream.LT(1));
3335
                        } else {
3336
                            selector.push(nextSelector);
3337
                        }
3338
                    } else {
3339
                        if (this._readWhitespace()){           
3340
                            ws = new Combinator(tokenStream.token().value, tokenStream.token().startLine, tokenStream.token().startCol);
3341
                            combinator = this._combinator();
3342
                            nextSelector = this._simple_selector_sequence();
3343
                            if (nextSelector === null){                        
3344
                                if (combinator !== null){
3345
                                    this._unexpectedToken(tokenStream.LT(1));
3346
                                }
3347
                            } else {
3348
                                
3349
                                if (combinator !== null){
3350
                                    selector.push(combinator);
3351
                                } else {
3352
                                    selector.push(ws);
3353
                                }
3354
                                
3355
                                selector.push(nextSelector);
3356
                            }     
3357
                        } else {
3358
                            break;
3359
                        }               
3360
                    
3361
                    }
3362
                } while(true);
3363
                
3364
                return new Selector(selector, selector[0].line, selector[0].col);
3365
            },
3366
            _simple_selector_sequence: function(){
3367
                 
3368
                var tokenStream = this._tokenStream,
3369
                    elementName = null,
3370
                    modifiers   = [],
3371
                    selectorText= "",
3372
                    components  = [
3373
                        function(){
3374
                            return tokenStream.match(Tokens.HASH) ?
3375
                                    new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
3376
                                    null;
3377
                        },
3378
                        this._class,
3379
                        this._attrib,
3380
                        this._pseudo,
3381
                        this._negation
3382
                    ],
3383
                    i           = 0,
3384
                    len         = components.length,
3385
                    component   = null,
3386
                    found       = false,
3387
                    line,
3388
                    col;
3389
                line = tokenStream.LT(1).startLine;
3390
                col = tokenStream.LT(1).startCol;
3391
                                        
3392
                elementName = this._type_selector();
3393
                if (!elementName){
3394
                    elementName = this._universal();
3395
                }
3396
                
3397
                if (elementName !== null){
3398
                    selectorText += elementName;
3399
                }                
3400
                
3401
                while(true){
3402
                    if (tokenStream.peek() === Tokens.S){
3403
                        break;
3404
                    }
3405
                    while(i < len && component === null){
3406
                        component = components[i++].call(this);
3407
                    }
3408
        
3409
                    if (component === null){
3410
                        if (selectorText === ""){
3411
                            return null;
3412
                        } else {
3413
                            break;
3414
                        }
3415
                    } else {
3416
                        i = 0;
3417
                        modifiers.push(component);
3418
                        selectorText += component.toString(); 
3419
                        component = null;
3420
                    }
3421
                }
3422
3423
                 
3424
                return selectorText !== "" ?
3425
                        new SelectorPart(elementName, modifiers, selectorText, line, col) :
3426
                        null;
3427
            },            
3428
            _type_selector: function(){
3429
                 
3430
                var tokenStream = this._tokenStream,
3431
                    ns          = this._namespace_prefix(),
3432
                    elementName = this._element_name();
3433
                    
3434
                if (!elementName){                    
3435
                    if (ns){
3436
                        tokenStream.unget();
3437
                        if (ns.length > 1){
3438
                            tokenStream.unget();
3439
                        }
3440
                    }
3441
                
3442
                    return null;
3443
                } else {     
3444
                    if (ns){
3445
                        elementName.text = ns + elementName.text;
3446
                        elementName.col -= ns.length;
3447
                    }
3448
                    return elementName;
3449
                }
3450
            },
3451
            _class: function(){    
3452
                 
3453
                var tokenStream = this._tokenStream,
3454
                    token;
3455
                
3456
                if (tokenStream.match(Tokens.DOT)){
3457
                    tokenStream.mustMatch(Tokens.IDENT);    
3458
                    token = tokenStream.token();
3459
                    return new SelectorSubPart("." + token.value, "class", token.startLine, token.startCol - 1);        
3460
                } else {
3461
                    return null;
3462
                }
3463
        
3464
            },
3465
            _element_name: function(){    
3466
                
3467
                var tokenStream = this._tokenStream,
3468
                    token;
3469
                
3470
                if (tokenStream.match(Tokens.IDENT)){
3471
                    token = tokenStream.token();
3472
                    return new SelectorSubPart(token.value, "elementName", token.startLine, token.startCol);        
3473
                
3474
                } else {
3475
                    return null;
3476
                }
3477
            },
3478
            _namespace_prefix: function(){
3479
                var tokenStream = this._tokenStream,
3480
                    value       = "";
3481
                if (tokenStream.LA(1) === Tokens.PIPE || tokenStream.LA(2) === Tokens.PIPE){
3482
                        
3483
                    if(tokenStream.match([Tokens.IDENT, Tokens.STAR])){
3484
                        value += tokenStream.token().value;
3485
                    }
3486
                    
3487
                    tokenStream.mustMatch(Tokens.PIPE);
3488
                    value += "|";
3489
                    
3490
                }
3491
                
3492
                return value.length ? value : null;                
3493
            },
3494
            _universal: function(){
3495
                var tokenStream = this._tokenStream,
3496
                    value       = "",
3497
                    ns;
3498
                    
3499
                ns = this._namespace_prefix();
3500
                if(ns){
3501
                    value += ns;
3502
                }
3503
                
3504
                if(tokenStream.match(Tokens.STAR)){
3505
                    value += "*";
3506
                }
3507
                
3508
                return value.length ? value : null;
3509
                
3510
           },
3511
            _attrib: function(){
3512
                 
3513
                var tokenStream = this._tokenStream,
3514
                    value       = null,
3515
                    ns,
3516
                    token;
3517
                
3518
                if (tokenStream.match(Tokens.LBRACKET)){
3519
                    token = tokenStream.token();
3520
                    value = token.value;
3521
                    value += this._readWhitespace();
3522
                    
3523
                    ns = this._namespace_prefix();
3524
                    
3525
                    if (ns){
3526
                        value += ns;
3527
                    }
3528
                                        
3529
                    tokenStream.mustMatch(Tokens.IDENT);
3530
                    value += tokenStream.token().value;                    
3531
                    value += this._readWhitespace();
3532
                    
3533
                    if(tokenStream.match([Tokens.PREFIXMATCH, Tokens.SUFFIXMATCH, Tokens.SUBSTRINGMATCH,
3534
                            Tokens.EQUALS, Tokens.INCLUDES, Tokens.DASHMATCH])){
3535
                    
3536
                        value += tokenStream.token().value;                    
3537
                        value += this._readWhitespace();
3538
                        
3539
                        tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
3540
                        value += tokenStream.token().value;                    
3541
                        value += this._readWhitespace();
3542
                    }
3543
                    
3544
                    tokenStream.mustMatch(Tokens.RBRACKET);
3545
                                        
3546
                    return new SelectorSubPart(value + "]", "attribute", token.startLine, token.startCol);
3547
                } else {
3548
                    return null;
3549
                }
3550
            },
3551
            _pseudo: function(){   
3552
            
3553
                var tokenStream = this._tokenStream,
3554
                    pseudo      = null,
3555
                    colons      = ":",
3556
                    line,
3557
                    col;
3558
                
3559
                if (tokenStream.match(Tokens.COLON)){
3560
                
3561
                    if (tokenStream.match(Tokens.COLON)){
3562
                        colons += ":";
3563
                    }
3564
                
3565
                    if (tokenStream.match(Tokens.IDENT)){
3566
                        pseudo = tokenStream.token().value;
3567
                        line = tokenStream.token().startLine;
3568
                        col = tokenStream.token().startCol - colons.length;
3569
                    } else if (tokenStream.peek() == Tokens.FUNCTION){
3570
                        line = tokenStream.LT(1).startLine;
3571
                        col = tokenStream.LT(1).startCol - colons.length;
3572
                        pseudo = this._functional_pseudo();
3573
                    }
3574
                    
3575
                    if (pseudo){
3576
                        pseudo = new SelectorSubPart(colons + pseudo, "pseudo", line, col);
3577
                    }
3578
                }
3579
        
3580
                return pseudo;
3581
            },
3582
            _functional_pseudo: function(){            
3583
                
3584
                var tokenStream = this._tokenStream,
3585
                    value = null;
3586
                
3587
                if(tokenStream.match(Tokens.FUNCTION)){
3588
                    value = tokenStream.token().value;
3589
                    value += this._readWhitespace();
3590
                    value += this._expression();
3591
                    tokenStream.mustMatch(Tokens.RPAREN);
3592
                    value += ")";
3593
                }
3594
                
3595
                return value;
3596
            },
3597
            _expression: function(){
3598
                 
3599
                var tokenStream = this._tokenStream,
3600
                    value       = "";
3601
                    
3602
                while(tokenStream.match([Tokens.PLUS, Tokens.MINUS, Tokens.DIMENSION,
3603
                        Tokens.NUMBER, Tokens.STRING, Tokens.IDENT, Tokens.LENGTH,
3604
                        Tokens.FREQ, Tokens.ANGLE, Tokens.TIME,
3605
                        Tokens.RESOLUTION])){
3606
                    
3607
                    value += tokenStream.token().value;
3608
                    value += this._readWhitespace();                        
3609
                }
3610
                
3611
                return value.length ? value : null;
3612
                
3613
            },
3614
            _negation: function(){
3615
3616
                var tokenStream = this._tokenStream,
3617
                    line,
3618
                    col,
3619
                    value       = "",
3620
                    arg,
3621
                    subpart     = null;
3622
                    
3623
                if (tokenStream.match(Tokens.NOT)){
3624
                    value = tokenStream.token().value;
3625
                    line = tokenStream.token().startLine;
3626
                    col = tokenStream.token().startCol;
3627
                    value += this._readWhitespace();
3628
                    arg = this._negation_arg();
3629
                    value += arg;
3630
                    value += this._readWhitespace();
3631
                    tokenStream.match(Tokens.RPAREN);
3632
                    value += tokenStream.token().value;
3633
                    
3634
                    subpart = new SelectorSubPart(value, "not", line, col);
3635
                    subpart.args.push(arg);
3636
                }
3637
                
3638
                return subpart;
3639
            },
3640
            _negation_arg: function(){                       
3641
                 
3642
                var tokenStream = this._tokenStream,
3643
                    args        = [
3644
                        this._type_selector,
3645
                        this._universal,
3646
                        function(){
3647
                            return tokenStream.match(Tokens.HASH) ?
3648
                                    new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
3649
                                    null;                        
3650
                        },
3651
                        this._class,
3652
                        this._attrib,
3653
                        this._pseudo                    
3654
                    ],
3655
                    arg         = null,
3656
                    i           = 0,
3657
                    len         = args.length,
3658
                    elementName,
3659
                    line,
3660
                    col,
3661
                    part;
3662
                    
3663
                line = tokenStream.LT(1).startLine;
3664
                col = tokenStream.LT(1).startCol;
3665
                
3666
                while(i < len && arg === null){
3667
                    
3668
                    arg = args[i].call(this);
3669
                    i++;
3670
                }
3671
                if (arg === null){
3672
                    this._unexpectedToken(tokenStream.LT(1));
3673
                }
3674
                if (arg.type == "elementName"){
3675
                    part = new SelectorPart(arg, [], arg.toString(), line, col);
3676
                } else {
3677
                    part = new SelectorPart(null, [arg], arg.toString(), line, col);
3678
                }
3679
                
3680
                return part;                
3681
            },
3682
            
3683
            _declaration: function(){    
3684
            
3685
                var tokenStream = this._tokenStream,
3686
                    property    = null,
3687
                    expr        = null,
3688
                    prio        = null,
3689
                    error       = null,
3690
                    invalid     = null,
3691
                    propertyName= "";
3692
                
3693
                property = this._property();
3694
                if (property !== null){
3695
3696
                    tokenStream.mustMatch(Tokens.COLON);
3697
                    this._readWhitespace();
3698
                    
3699
                    expr = this._expr();
3700
                    if (!expr || expr.length === 0){
3701
                        this._unexpectedToken(tokenStream.LT(1));
3702
                    }
3703
                    
3704
                    prio = this._prio();
3705
                    propertyName = property.toString();
3706
                    if (this.options.starHack && property.hack == "*" ||
3707
                            this.options.underscoreHack && property.hack == "_") {
3708
                         
3709
                        propertyName = property.text;
3710
                    }
3711
                    
3712
                    try {
3713
                        this._validateProperty(propertyName, expr);
3714
                    } catch (ex) {
3715
                        invalid = ex;
3716
                    }
3717
                    
3718
                    this.fire({
3719
                        type:       "property",
3720
                        property:   property,
3721
                        value:      expr,
3722
                        important:  prio,
3723
                        line:       property.line,
3724
                        col:        property.col,
3725
                        invalid:    invalid
3726
                    });                      
3727
                    
3728
                    return true;
3729
                } else {
3730
                    return false;
3731
                }
3732
            },
3733
            
3734
            _prio: function(){
3735
                 
3736
                var tokenStream = this._tokenStream,
3737
                    result      = tokenStream.match(Tokens.IMPORTANT_SYM);
3738
                    
3739
                this._readWhitespace();
3740
                return result;
3741
            },
3742
            
3743
            _expr: function(){
3744
        
3745
                var tokenStream = this._tokenStream,
3746
                    values      = [],
3747
                    value       = null,
3748
                    operator    = null;
3749
                    
3750
                value = this._term();
3751
                if (value !== null){
3752
                
3753
                    values.push(value);
3754
                    
3755
                    do {
3756
                        operator = this._operator();
3757
                        if (operator){
3758
                            values.push(operator);
3759
                        } /*else {
3760
							values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
3761
							valueParts = [];
3762
						}*/
3763
                        
3764
                        value = this._term();
3765
                        
3766
                        if (value === null){
3767
                            break;
3768
                        } else {
3769
                            values.push(value);
3770
                        }
3771
                    } while(true);
3772
                }
3773
        
3774
                return values.length > 0 ? new PropertyValue(values, values[0].line, values[0].col) : null;
3775
            },
3776
            
3777
            _term: function(){                           
3778
        
3779
                var tokenStream = this._tokenStream,
3780
                    unary       = null,
3781
                    value       = null,
3782
                    token,
3783
                    line,
3784
                    col;
3785
                unary = this._unary_operator();
3786
                if (unary !== null){
3787
                    line = tokenStream.token().startLine;
3788
                    col = tokenStream.token().startCol;
3789
                }                
3790
                if (tokenStream.peek() == Tokens.IE_FUNCTION && this.options.ieFilters){
3791
                
3792
                    value = this._ie_function();
3793
                    if (unary === null){
3794
                        line = tokenStream.token().startLine;
3795
                        col = tokenStream.token().startCol;
3796
                    }
3797
                } else if (tokenStream.match([Tokens.NUMBER, Tokens.PERCENTAGE, Tokens.LENGTH,
3798
                        Tokens.ANGLE, Tokens.TIME,
3799
                        Tokens.FREQ, Tokens.STRING, Tokens.IDENT, Tokens.URI, Tokens.UNICODE_RANGE])){
3800
                 
3801
                    value = tokenStream.token().value;
3802
                    if (unary === null){
3803
                        line = tokenStream.token().startLine;
3804
                        col = tokenStream.token().startCol;
3805
                    }
3806
                    this._readWhitespace();
3807
                } else {
3808
                    token = this._hexcolor();
3809
                    if (token === null){
3810
                        if (unary === null){
3811
                            line = tokenStream.LT(1).startLine;
3812
                            col = tokenStream.LT(1).startCol;
3813
                        }                    
3814
                        if (value === null){
3815
                            if (tokenStream.LA(3) == Tokens.EQUALS && this.options.ieFilters){
3816
                                value = this._ie_function();
3817
                            } else {
3818
                                value = this._function();
3819
                            }
3820
                        }
3821
                    
3822
                    } else {
3823
                        value = token.value;
3824
                        if (unary === null){
3825
                            line = token.startLine;
3826
                            col = token.startCol;
3827
                        }                    
3828
                    }
3829
                
3830
                }                
3831
                
3832
                return value !== null ?
3833
                        new PropertyValuePart(unary !== null ? unary + value : value, line, col) :
3834
                        null;
3835
        
3836
            },
3837
            
3838
            _function: function(){
3839
                 
3840
                var tokenStream = this._tokenStream,
3841
                    functionText = null,
3842
                    expr        = null,
3843
                    lt;
3844
                    
3845
                if (tokenStream.match(Tokens.FUNCTION)){
3846
                    functionText = tokenStream.token().value;
3847
                    this._readWhitespace();
3848
                    expr = this._expr();
3849
                    functionText += expr;
3850
                    if (this.options.ieFilters && tokenStream.peek() == Tokens.EQUALS){
3851
                        do {
3852
                        
3853
                            if (this._readWhitespace()){
3854
                                functionText += tokenStream.token().value;
3855
                            }
3856
                            if (tokenStream.LA(0) == Tokens.COMMA){
3857
                                functionText += tokenStream.token().value;
3858
                            }
3859
                        
3860
                            tokenStream.match(Tokens.IDENT);
3861
                            functionText += tokenStream.token().value;
3862
                            
3863
                            tokenStream.match(Tokens.EQUALS);
3864
                            functionText += tokenStream.token().value;
3865
                            lt = tokenStream.peek();
3866
                            while(lt != Tokens.COMMA && lt != Tokens.S && lt != Tokens.RPAREN){
3867
                                tokenStream.get();
3868
                                functionText += tokenStream.token().value;
3869
                                lt = tokenStream.peek();
3870
                            }
3871
                        } while(tokenStream.match([Tokens.COMMA, Tokens.S]));
3872
                    }
3873
                    
3874
                    tokenStream.match(Tokens.RPAREN);    
3875
                    functionText += ")";
3876
                    this._readWhitespace();
3877
                }                
3878
                
3879
                return functionText;
3880
            }, 
3881
            
3882
            _ie_function: function(){
3883
                 
3884
                var tokenStream = this._tokenStream,
3885
                    functionText = null,
3886
                    expr        = null,
3887
                    lt;
3888
                if (tokenStream.match([Tokens.IE_FUNCTION, Tokens.FUNCTION])){
3889
                    functionText = tokenStream.token().value;
3890
                    
3891
                    do {
3892
                    
3893
                        if (this._readWhitespace()){
3894
                            functionText += tokenStream.token().value;
3895
                        }
3896
                        if (tokenStream.LA(0) == Tokens.COMMA){
3897
                            functionText += tokenStream.token().value;
3898
                        }
3899
                    
3900
                        tokenStream.match(Tokens.IDENT);
3901
                        functionText += tokenStream.token().value;
3902
                        
3903
                        tokenStream.match(Tokens.EQUALS);
3904
                        functionText += tokenStream.token().value;
3905
                        lt = tokenStream.peek();
3906
                        while(lt != Tokens.COMMA && lt != Tokens.S && lt != Tokens.RPAREN){
3907
                            tokenStream.get();
3908
                            functionText += tokenStream.token().value;
3909
                            lt = tokenStream.peek();
3910
                        }
3911
                    } while(tokenStream.match([Tokens.COMMA, Tokens.S]));                    
3912
                    
3913
                    tokenStream.match(Tokens.RPAREN);    
3914
                    functionText += ")";
3915
                    this._readWhitespace();
3916
                }                
3917
                
3918
                return functionText;
3919
            }, 
3920
            
3921
            _hexcolor: function(){
3922
                 
3923
                var tokenStream = this._tokenStream,
3924
                    token = null,
3925
                    color;
3926
                    
3927
                if(tokenStream.match(Tokens.HASH)){
3928
                    
3929
                    token = tokenStream.token();
3930
                    color = token.value;
3931
                    if (!/#[a-f0-9]{3,6}/i.test(color)){
3932
                        throw new SyntaxError("Expected a hex color but found '" + color + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
3933
                    }
3934
                    this._readWhitespace();
3935
                }
3936
                
3937
                return token;
3938
            },
3939
            
3940
            _keyframes: function(){
3941
                var tokenStream = this._tokenStream,
3942
                    token,
3943
                    tt,
3944
                    name,
3945
                    prefix = "";            
3946
                    
3947
                tokenStream.mustMatch(Tokens.KEYFRAMES_SYM);
3948
                token = tokenStream.token();
3949
                if (/^@\-([^\-]+)\-/.test(token.value)) {
3950
                    prefix = RegExp.$1;
3951
                }
3952
                
3953
                this._readWhitespace();
3954
                name = this._keyframe_name();
3955
                
3956
                this._readWhitespace();
3957
                tokenStream.mustMatch(Tokens.LBRACE);
3958
                    
3959
                this.fire({
3960
                    type:   "startkeyframes",
3961
                    name:   name,
3962
                    prefix: prefix,
3963
                    line:   token.startLine,
3964
                    col:    token.startCol
3965
                });                
3966
                
3967
                this._readWhitespace();
3968
                tt = tokenStream.peek();
3969
                while(tt == Tokens.IDENT || tt == Tokens.PERCENTAGE) {
3970
                    this._keyframe_rule();
3971
                    this._readWhitespace();
3972
                    tt = tokenStream.peek();
3973
                }           
3974
                
3975
                this.fire({
3976
                    type:   "endkeyframes",
3977
                    name:   name,
3978
                    prefix: prefix,
3979
                    line:   token.startLine,
3980
                    col:    token.startCol
3981
                });                      
3982
                    
3983
                this._readWhitespace();
3984
                tokenStream.mustMatch(Tokens.RBRACE);                    
3985
                
3986
            },
3987
            
3988
            _keyframe_name: function(){
3989
                var tokenStream = this._tokenStream,
3990
                    token;
3991
3992
                tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
3993
                return SyntaxUnit.fromToken(tokenStream.token());            
3994
            },
3995
            
3996
            _keyframe_rule: function(){
3997
                var tokenStream = this._tokenStream,
3998
                    token,
3999
                    keyList = this._key_list();
4000
                                    
4001
                this.fire({
4002
                    type:   "startkeyframerule",
4003
                    keys:   keyList,
4004
                    line:   keyList[0].line,
4005
                    col:    keyList[0].col
4006
                });                
4007
                
4008
                this._readDeclarations(true);                
4009
                
4010
                this.fire({
4011
                    type:   "endkeyframerule",
4012
                    keys:   keyList,
4013
                    line:   keyList[0].line,
4014
                    col:    keyList[0].col
4015
                });  
4016
                
4017
            },
4018
            
4019
            _key_list: function(){
4020
                var tokenStream = this._tokenStream,
4021
                    token,
4022
                    key,
4023
                    keyList = [];
4024
                keyList.push(this._key());
4025
                    
4026
                this._readWhitespace();
4027
                    
4028
                while(tokenStream.match(Tokens.COMMA)){
4029
                    this._readWhitespace();
4030
                    keyList.push(this._key());
4031
                    this._readWhitespace();
4032
                }
4033
4034
                return keyList;
4035
            },
4036
                        
4037
            _key: function(){
4038
                 
4039
                var tokenStream = this._tokenStream,
4040
                    token;
4041
                    
4042
                if (tokenStream.match(Tokens.PERCENTAGE)){
4043
                    return SyntaxUnit.fromToken(tokenStream.token());
4044
                } else if (tokenStream.match(Tokens.IDENT)){
4045
                    token = tokenStream.token();                    
4046
                    
4047
                    if (/from|to/i.test(token.value)){
4048
                        return SyntaxUnit.fromToken(token);
4049
                    }
4050
                    
4051
                    tokenStream.unget();
4052
                }
4053
                this._unexpectedToken(tokenStream.LT(1));
4054
            },
4055
            _skipCruft: function(){
4056
                while(this._tokenStream.match([Tokens.S, Tokens.CDO, Tokens.CDC])){
4057
                }
4058
            },
4059
            _readDeclarations: function(checkStart, readMargins){
4060
                var tokenStream = this._tokenStream,
4061
                    tt;
4062
                       
4063
4064
                this._readWhitespace();
4065
                
4066
                if (checkStart){
4067
                    tokenStream.mustMatch(Tokens.LBRACE);            
4068
                }
4069
                
4070
                this._readWhitespace();
4071
4072
                try {
4073
                    
4074
                    while(true){
4075
                    
4076
                        if (tokenStream.match(Tokens.SEMICOLON) || (readMargins && this._margin())){
4077
                        } else if (this._declaration()){
4078
                            if (!tokenStream.match(Tokens.SEMICOLON)){
4079
                                break;
4080
                            }
4081
                        } else {
4082
                            break;
4083
                        }
4084
                        this._readWhitespace();
4085
                    }
4086
                    
4087
                    tokenStream.mustMatch(Tokens.RBRACE);
4088
                    this._readWhitespace();
4089
                    
4090
                } catch (ex) {
4091
                    if (ex instanceof SyntaxError && !this.options.strict){
4092
                        this.fire({
4093
                            type:       "error",
4094
                            error:      ex,
4095
                            message:    ex.message,
4096
                            line:       ex.line,
4097
                            col:        ex.col
4098
                        });                          
4099
                        tt = tokenStream.advance([Tokens.SEMICOLON, Tokens.RBRACE]);
4100
                        if (tt == Tokens.SEMICOLON){
4101
                            this._readDeclarations(false, readMargins);                            
4102
                        } else if (tt != Tokens.RBRACE){
4103
                            throw ex;
4104
                        }                        
4105
                        
4106
                    } else {
4107
                        throw ex;
4108
                    }
4109
                }    
4110
            
4111
            },      
4112
            _readWhitespace: function(){
4113
            
4114
                var tokenStream = this._tokenStream,
4115
                    ws = "";
4116
                    
4117
                while(tokenStream.match(Tokens.S)){
4118
                    ws += tokenStream.token().value;
4119
                }
4120
                
4121
                return ws;
4122
            },
4123
            _unexpectedToken: function(token){
4124
                throw new SyntaxError("Unexpected token '" + token.value + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
4125
            },
4126
            _verifyEnd: function(){
4127
                if (this._tokenStream.LA(1) != Tokens.EOF){
4128
                    this._unexpectedToken(this._tokenStream.LT(1));
4129
                }            
4130
            },
4131
            _validateProperty: function(property, value){
4132
                Validation.validate(property, value);
4133
            },
4134
            
4135
            parse: function(input){    
4136
                this._tokenStream = new TokenStream(input, Tokens);
4137
                this._stylesheet();
4138
            },
4139
            
4140
            parseStyleSheet: function(input){
4141
                return this.parse(input);
4142
            },
4143
            
4144
            parseMediaQuery: function(input){
4145
                this._tokenStream = new TokenStream(input, Tokens);
4146
                var result = this._media_query();
4147
                this._verifyEnd();
4148
                return result;            
4149
            },             
4150
            parsePropertyValue: function(input){
4151
            
4152
                this._tokenStream = new TokenStream(input, Tokens);
4153
                this._readWhitespace();
4154
                
4155
                var result = this._expr();
4156
                this._readWhitespace();
4157
                this._verifyEnd();
4158
                return result;
4159
            },
4160
            parseRule: function(input){
4161
                this._tokenStream = new TokenStream(input, Tokens);
4162
                this._readWhitespace();
4163
                
4164
                var result = this._ruleset();
4165
                this._readWhitespace();
4166
                this._verifyEnd();
4167
                return result;            
4168
            },
4169
            parseSelector: function(input){
4170
            
4171
                this._tokenStream = new TokenStream(input, Tokens);
4172
                this._readWhitespace();
4173
                
4174
                var result = this._selector();
4175
                this._readWhitespace();
4176
                this._verifyEnd();
4177
                return result;
4178
            },
4179
            parseStyleAttribute: function(input){
4180
                input += "}"; // for error recovery in _readDeclarations()
4181
                this._tokenStream = new TokenStream(input, Tokens);
4182
                this._readDeclarations();
4183
            }
4184
        };
4185
    for (prop in additions){
4186
        if (additions.hasOwnProperty(prop)){
4187
            proto[prop] = additions[prop];
4188
        }
4189
    }   
4190
    
4191
    return proto;
4192
}();
4193
var Properties = {
4194
    "alignment-adjust"              : "auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | <percentage> | <length>",
4195
    "alignment-baseline"            : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
4196
    "animation"                     : 1,
4197
    "animation-delay"               : { multi: "<time>", comma: true },
4198
    "animation-direction"           : { multi: "normal | alternate", comma: true },
4199
    "animation-duration"            : { multi: "<time>", comma: true },
4200
    "animation-iteration-count"     : { multi: "<number> | infinite", comma: true },
4201
    "animation-name"                : { multi: "none | <ident>", comma: true },
4202
    "animation-play-state"          : { multi: "running | paused", comma: true },
4203
    "animation-timing-function"     : 1,
4204
    "-moz-animation-delay"               : { multi: "<time>", comma: true },
4205
    "-moz-animation-direction"           : { multi: "normal | alternate", comma: true },
4206
    "-moz-animation-duration"            : { multi: "<time>", comma: true },
4207
    "-moz-animation-iteration-count"     : { multi: "<number> | infinite", comma: true },
4208
    "-moz-animation-name"                : { multi: "none | <ident>", comma: true },
4209
    "-moz-animation-play-state"          : { multi: "running | paused", comma: true },
4210
    
4211
    "-ms-animation-delay"               : { multi: "<time>", comma: true },
4212
    "-ms-animation-direction"           : { multi: "normal | alternate", comma: true },
4213
    "-ms-animation-duration"            : { multi: "<time>", comma: true },
4214
    "-ms-animation-iteration-count"     : { multi: "<number> | infinite", comma: true },
4215
    "-ms-animation-name"                : { multi: "none | <ident>", comma: true },
4216
    "-ms-animation-play-state"          : { multi: "running | paused", comma: true },
4217
    
4218
    "-webkit-animation-delay"               : { multi: "<time>", comma: true },
4219
    "-webkit-animation-direction"           : { multi: "normal | alternate", comma: true },
4220
    "-webkit-animation-duration"            : { multi: "<time>", comma: true },
4221
    "-webkit-animation-iteration-count"     : { multi: "<number> | infinite", comma: true },
4222
    "-webkit-animation-name"                : { multi: "none | <ident>", comma: true },
4223
    "-webkit-animation-play-state"          : { multi: "running | paused", comma: true },
4224
    
4225
    "-o-animation-delay"               : { multi: "<time>", comma: true },
4226
    "-o-animation-direction"           : { multi: "normal | alternate", comma: true },
4227
    "-o-animation-duration"            : { multi: "<time>", comma: true },
4228
    "-o-animation-iteration-count"     : { multi: "<number> | infinite", comma: true },
4229
    "-o-animation-name"                : { multi: "none | <ident>", comma: true },
4230
    "-o-animation-play-state"          : { multi: "running | paused", comma: true },        
4231
    
4232
    "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 | inherit",
4233
    "azimuth"                       : function (expression) {
4234
        var simple      = "<angle> | leftwards | rightwards | inherit",
4235
            direction   = "left-side | far-left | left | center-left | center | center-right | right | far-right | right-side",
4236
            behind      = false,
4237
            valid       = false,
4238
            part;
4239
        
4240
        if (!ValidationTypes.isAny(expression, simple)) {
4241
            if (ValidationTypes.isAny(expression, "behind")) {
4242
                behind = true;
4243
                valid = true;
4244
            }
4245
            
4246
            if (ValidationTypes.isAny(expression, direction)) {
4247
                valid = true;
4248
                if (!behind) {
4249
                    ValidationTypes.isAny(expression, "behind");
4250
                }
4251
            }
4252
        }
4253
        
4254
        if (expression.hasNext()) {
4255
            part = expression.next();
4256
            if (valid) {
4257
                throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
4258
            } else {
4259
                throw new ValidationError("Expected (<'azimuth'>) but found '" + part + "'.", part.line, part.col);
4260
            }
4261
        }        
4262
    },
4263
    "backface-visibility"           : "visible | hidden",
4264
    "background"                    : 1,
4265
    "background-attachment"         : { multi: "<attachment>", comma: true },
4266
    "background-clip"               : { multi: "<box>", comma: true },
4267
    "background-color"              : "<color> | inherit",
4268
    "background-image"              : { multi: "<bg-image>", comma: true },
4269
    "background-origin"             : { multi: "<box>", comma: true },
4270
    "background-position"           : { multi: "<bg-position>", comma: true },
4271
    "background-repeat"             : { multi: "<repeat-style>" },
4272
    "background-size"               : { multi: "<bg-size>", comma: true },
4273
    "baseline-shift"                : "baseline | sub | super | <percentage> | <length>",
4274
    "behavior"                      : 1,
4275
    "binding"                       : 1,
4276
    "bleed"                         : "<length>",
4277
    "bookmark-label"                : "<content> | <attr> | <string>",
4278
    "bookmark-level"                : "none | <integer>",
4279
    "bookmark-state"                : "open | closed",
4280
    "bookmark-target"               : "none | <uri> | <attr>",
4281
    "border"                        : "<border-width> || <border-style> || <color>",
4282
    "border-bottom"                 : "<border-width> || <border-style> || <color>",
4283
    "border-bottom-color"           : "<color>",
4284
    "border-bottom-left-radius"     :  "<x-one-radius>",
4285
    "border-bottom-right-radius"    :  "<x-one-radius>",
4286
    "border-bottom-style"           : "<border-style>",
4287
    "border-bottom-width"           : "<border-width>",
4288
    "border-collapse"               : "collapse | separate | inherit",
4289
    "border-color"                  : { multi: "<color> | inherit", max: 4 },
4290
    "border-image"                  : 1,
4291
    "border-image-outset"           : { multi: "<length> | <number>", max: 4 },
4292
    "border-image-repeat"           : { multi: "stretch | repeat | round", max: 2 },
4293
    "border-image-slice"            : function(expression) {
4294
        
4295
        var valid   = false,
4296
            numeric = "<number> | <percentage>",
4297
            fill    = false,
4298
            count   = 0,
4299
            max     = 4,
4300
            part;
4301
        
4302
        if (ValidationTypes.isAny(expression, "fill")) {
4303
            fill = true;
4304
            valid = true;
4305
        }
4306
        
4307
        while (expression.hasNext() && count < max) {
4308
            valid = ValidationTypes.isAny(expression, numeric);
4309
            if (!valid) {
4310
                break;
4311
            }
4312
            count++;
4313
        }
4314
        
4315
        
4316
        if (!fill) {
4317
            ValidationTypes.isAny(expression, "fill");
4318
        } else {
4319
            valid = true;
4320
        }
4321
        
4322
        if (expression.hasNext()) {
4323
            part = expression.next();
4324
            if (valid) {
4325
                throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
4326
            } else {
4327
                throw new ValidationError("Expected ([<number> | <percentage>]{1,4} && fill?) but found '" + part + "'.", part.line, part.col);
4328
            }
4329
        }         
4330
    },
4331
    "border-image-source"           : "<image> | none",
4332
    "border-image-width"            : { multi: "<length> | <percentage> | <number> | auto", max: 4 },
4333
    "border-left"                   : "<border-width> || <border-style> || <color>",
4334
    "border-left-color"             : "<color> | inherit",
4335
    "border-left-style"             : "<border-style>",
4336
    "border-left-width"             : "<border-width>",
4337
    "border-radius"                 : function(expression) {
4338
        
4339
        var valid   = false,
4340
            numeric = "<length> | <percentage>",
4341
            slash   = false,
4342
            fill    = false,
4343
            count   = 0,
4344
            max     = 8,
4345
            part;
4346
4347
        while (expression.hasNext() && count < max) {
4348
            valid = ValidationTypes.isAny(expression, numeric);
4349
            if (!valid) {
4350
            
4351
                if (expression.peek() == "/" && count > 1 && !slash) {
4352
                    slash = true;
4353
                    max = count + 5;
4354
                    expression.next();
4355
                } else {
4356
                    break;
4357
                }
4358
            }
4359
            count++;
4360
        }
4361
        
4362
        if (expression.hasNext()) {
4363
            part = expression.next();
4364
            if (valid) {
4365
                throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
4366
            } else {
4367
                throw new ValidationError("Expected (<'border-radius'>) but found '" + part + "'.", part.line, part.col);
4368
            }
4369
        }         
4370
    },
4371
    "border-right"                  : "<border-width> || <border-style> || <color>",
4372
    "border-right-color"            : "<color> | inherit",
4373
    "border-right-style"            : "<border-style>",
4374
    "border-right-width"            : "<border-width>",
4375
    "border-spacing"                : { multi: "<length> | inherit", max: 2 },
4376
    "border-style"                  : { multi: "<border-style>", max: 4 },
4377
    "border-top"                    : "<border-width> || <border-style> || <color>",
4378
    "border-top-color"              : "<color> | inherit",
4379
    "border-top-left-radius"        : "<x-one-radius>",
4380
    "border-top-right-radius"       : "<x-one-radius>",
4381
    "border-top-style"              : "<border-style>",
4382
    "border-top-width"              : "<border-width>",
4383
    "border-width"                  : { multi: "<border-width>", max: 4 },
4384
    "bottom"                        : "<margin-width> | inherit", 
4385
    "box-align"                     : "start | end | center | baseline | stretch",        //http://www.w3.org/TR/2009/WD-css3-flexbox-20090723/
4386
    "box-decoration-break"          : "slice |clone",
4387
    "box-direction"                 : "normal | reverse | inherit",
4388
    "box-flex"                      : "<number>",
4389
    "box-flex-group"                : "<integer>",
4390
    "box-lines"                     : "single | multiple",
4391
    "box-ordinal-group"             : "<integer>",
4392
    "box-orient"                    : "horizontal | vertical | inline-axis | block-axis | inherit",
4393
    "box-pack"                      : "start | end | center | justify",
4394
    "box-shadow"                    : function (expression) {
4395
        var result      = false,
4396
            part;
4397
4398
        if (!ValidationTypes.isAny(expression, "none")) {
4399
            Validation.multiProperty("<shadow>", expression, true, Infinity);                       
4400
        } else {
4401
            if (expression.hasNext()) {
4402
                part = expression.next();
4403
                throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
4404
            }   
4405
        }
4406
    },
4407
    "box-sizing"                    : "content-box | border-box | inherit",
4408
    "break-after"                   : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
4409
    "break-before"                  : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
4410
    "break-inside"                  : "auto | avoid | avoid-page | avoid-column",
4411
    "caption-side"                  : "top | bottom | inherit",
4412
    "clear"                         : "none | right | left | both | inherit",
4413
    "clip"                          : 1,
4414
    "color"                         : "<color> | inherit",
4415
    "color-profile"                 : 1,
4416
    "column-count"                  : "<integer> | auto",                      //http://www.w3.org/TR/css3-multicol/
4417
    "column-fill"                   : "auto | balance",
4418
    "column-gap"                    : "<length> | normal",
4419
    "column-rule"                   : "<border-width> || <border-style> || <color>",
4420
    "column-rule-color"             : "<color>",
4421
    "column-rule-style"             : "<border-style>",
4422
    "column-rule-width"             : "<border-width>",
4423
    "column-span"                   : "none | all",
4424
    "column-width"                  : "<length> | auto",
4425
    "columns"                       : 1,
4426
    "content"                       : 1,
4427
    "counter-increment"             : 1,
4428
    "counter-reset"                 : 1,
4429
    "crop"                          : "<shape> | auto",
4430
    "cue"                           : "cue-after | cue-before | inherit",
4431
    "cue-after"                     : 1,
4432
    "cue-before"                    : 1,
4433
    "cursor"                        : 1,
4434
    "direction"                     : "ltr | rtl | inherit",
4435
    "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 | box | inline-box | grid | inline-grid | none | inherit",
4436
    "dominant-baseline"             : 1,
4437
    "drop-initial-after-adjust"     : "central | middle | after-edge | text-after-edge | ideographic | alphabetic | mathematical | <percentage> | <length>",
4438
    "drop-initial-after-align"      : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
4439
    "drop-initial-before-adjust"    : "before-edge | text-before-edge | central | middle | hanging | mathematical | <percentage> | <length>",
4440
    "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",
4441
    "drop-initial-size"             : "auto | line | <length> | <percentage>",
4442
    "drop-initial-value"            : "initial | <integer>",
4443
    "elevation"                     : "<angle> | below | level | above | higher | lower | inherit",
4444
    "empty-cells"                   : "show | hide | inherit",
4445
    "filter"                        : 1,
4446
    "fit"                           : "fill | hidden | meet | slice",
4447
    "fit-position"                  : 1,
4448
    "float"                         : "left | right | none | inherit",    
4449
    "float-offset"                  : 1,
4450
    "font"                          : 1,
4451
    "font-family"                   : 1,
4452
    "font-size"                     : "<absolute-size> | <relative-size> | <length> | <percentage> | inherit",
4453
    "font-size-adjust"              : "<number> | none | inherit",
4454
    "font-stretch"                  : "normal | ultra-condensed | extra-condensed | condensed | semi-condensed | semi-expanded | expanded | extra-expanded | ultra-expanded | inherit",
4455
    "font-style"                    : "normal | italic | oblique | inherit",
4456
    "font-variant"                  : "normal | small-caps | inherit",
4457
    "font-weight"                   : "normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | inherit",
4458
    "grid-cell-stacking"            : "columns | rows | layer",
4459
    "grid-column"                   : 1,
4460
    "grid-columns"                  : 1,
4461
    "grid-column-align"             : "start | end | center | stretch",
4462
    "grid-column-sizing"            : 1,
4463
    "grid-column-span"              : "<integer>",
4464
    "grid-flow"                     : "none | rows | columns",
4465
    "grid-layer"                    : "<integer>",
4466
    "grid-row"                      : 1,
4467
    "grid-rows"                     : 1,
4468
    "grid-row-align"                : "start | end | center | stretch",
4469
    "grid-row-span"                 : "<integer>",
4470
    "grid-row-sizing"               : 1,
4471
    "hanging-punctuation"           : 1,
4472
    "height"                        : "<margin-width> | inherit",
4473
    "hyphenate-after"               : "<integer> | auto",
4474
    "hyphenate-before"              : "<integer> | auto",
4475
    "hyphenate-character"           : "<string> | auto",
4476
    "hyphenate-lines"               : "no-limit | <integer>",
4477
    "hyphenate-resource"            : 1,
4478
    "hyphens"                       : "none | manual | auto",
4479
    "icon"                          : 1,
4480
    "image-orientation"             : "angle | auto",
4481
    "image-rendering"               : 1,
4482
    "image-resolution"              : 1,
4483
    "inline-box-align"              : "initial | last | <integer>",
4484
    "left"                          : "<margin-width> | inherit",
4485
    "letter-spacing"                : "<length> | normal | inherit",
4486
    "line-height"                   : "<number> | <length> | <percentage> | normal | inherit",
4487
    "line-break"                    : "auto | loose | normal | strict",
4488
    "line-stacking"                 : 1,
4489
    "line-stacking-ruby"            : "exclude-ruby | include-ruby",
4490
    "line-stacking-shift"           : "consider-shifts | disregard-shifts",
4491
    "line-stacking-strategy"        : "inline-line-height | block-line-height | max-height | grid-height",
4492
    "list-style"                    : 1,
4493
    "list-style-image"              : "<uri> | none | inherit",
4494
    "list-style-position"           : "inside | outside | inherit",
4495
    "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",
4496
    "margin"                        : { multi: "<margin-width> | inherit", max: 4 },
4497
    "margin-bottom"                 : "<margin-width> | inherit",
4498
    "margin-left"                   : "<margin-width> | inherit",
4499
    "margin-right"                  : "<margin-width> | inherit",
4500
    "margin-top"                    : "<margin-width> | inherit",
4501
    "mark"                          : 1,
4502
    "mark-after"                    : 1,
4503
    "mark-before"                   : 1,
4504
    "marks"                         : 1,
4505
    "marquee-direction"             : 1,
4506
    "marquee-play-count"            : 1,
4507
    "marquee-speed"                 : 1,
4508
    "marquee-style"                 : 1,
4509
    "max-height"                    : "<length> | <percentage> | none | inherit",
4510
    "max-width"                     : "<length> | <percentage> | none | inherit",
4511
    "min-height"                    : "<length> | <percentage> | inherit",
4512
    "min-width"                     : "<length> | <percentage> | inherit",
4513
    "move-to"                       : 1,
4514
    "nav-down"                      : 1,
4515
    "nav-index"                     : 1,
4516
    "nav-left"                      : 1,
4517
    "nav-right"                     : 1,
4518
    "nav-up"                        : 1,
4519
    "opacity"                       : "<number> | inherit",
4520
    "orphans"                       : "<integer> | inherit",
4521
    "outline"                       : 1,
4522
    "outline-color"                 : "<color> | invert | inherit",
4523
    "outline-offset"                : 1,
4524
    "outline-style"                 : "<border-style> | inherit",
4525
    "outline-width"                 : "<border-width> | inherit",
4526
    "overflow"                      : "visible | hidden | scroll | auto | inherit",
4527
    "overflow-style"                : 1,
4528
    "overflow-x"                    : 1,
4529
    "overflow-y"                    : 1,
4530
    "padding"                       : { multi: "<padding-width> | inherit", max: 4 },
4531
    "padding-bottom"                : "<padding-width> | inherit",
4532
    "padding-left"                  : "<padding-width> | inherit",
4533
    "padding-right"                 : "<padding-width> | inherit",
4534
    "padding-top"                   : "<padding-width> | inherit",
4535
    "page"                          : 1,
4536
    "page-break-after"              : "auto | always | avoid | left | right | inherit",
4537
    "page-break-before"             : "auto | always | avoid | left | right | inherit",
4538
    "page-break-inside"             : "auto | avoid | inherit",
4539
    "page-policy"                   : 1,
4540
    "pause"                         : 1,
4541
    "pause-after"                   : 1,
4542
    "pause-before"                  : 1,
4543
    "perspective"                   : 1,
4544
    "perspective-origin"            : 1,
4545
    "phonemes"                      : 1,
4546
    "pitch"                         : 1,
4547
    "pitch-range"                   : 1,
4548
    "play-during"                   : 1,
4549
    "pointer-events"                : "auto | none | visiblePainted | visibleFill | visibleStroke | visible | painted | fill | stroke | all | inherit",
4550
    "position"                      : "static | relative | absolute | fixed | inherit",
4551
    "presentation-level"            : 1,
4552
    "punctuation-trim"              : 1,
4553
    "quotes"                        : 1,
4554
    "rendering-intent"              : 1,
4555
    "resize"                        : 1,
4556
    "rest"                          : 1,
4557
    "rest-after"                    : 1,
4558
    "rest-before"                   : 1,
4559
    "richness"                      : 1,
4560
    "right"                         : "<margin-width> | inherit",
4561
    "rotation"                      : 1,
4562
    "rotation-point"                : 1,
4563
    "ruby-align"                    : 1,
4564
    "ruby-overhang"                 : 1,
4565
    "ruby-position"                 : 1,
4566
    "ruby-span"                     : 1,
4567
    "size"                          : 1,
4568
    "speak"                         : "normal | none | spell-out | inherit",
4569
    "speak-header"                  : "once | always | inherit",
4570
    "speak-numeral"                 : "digits | continuous | inherit",
4571
    "speak-punctuation"             : "code | none | inherit",
4572
    "speech-rate"                   : 1,
4573
    "src"                           : 1,
4574
    "stress"                        : 1,
4575
    "string-set"                    : 1,
4576
    
4577
    "table-layout"                  : "auto | fixed | inherit",
4578
    "tab-size"                      : "<integer> | <length>",
4579
    "target"                        : 1,
4580
    "target-name"                   : 1,
4581
    "target-new"                    : 1,
4582
    "target-position"               : 1,
4583
    "text-align"                    : "left | right | center | justify | inherit" ,
4584
    "text-align-last"               : 1,
4585
    "text-decoration"               : 1,
4586
    "text-emphasis"                 : 1,
4587
    "text-height"                   : 1,
4588
    "text-indent"                   : "<length> | <percentage> | inherit",
4589
    "text-justify"                  : "auto | none | inter-word | inter-ideograph | inter-cluster | distribute | kashida",
4590
    "text-outline"                  : 1,
4591
    "text-overflow"                 : 1,
4592
    "text-rendering"                : "auto | optimizeSpeed | optimizeLegibility | geometricPrecision | inherit",
4593
    "text-shadow"                   : 1,
4594
    "text-transform"                : "capitalize | uppercase | lowercase | none | inherit",
4595
    "text-wrap"                     : "normal | none | avoid",
4596
    "top"                           : "<margin-width> | inherit",
4597
    "transform"                     : 1,
4598
    "transform-origin"              : 1,
4599
    "transform-style"               : 1,
4600
    "transition"                    : 1,
4601
    "transition-delay"              : 1,
4602
    "transition-duration"           : 1,
4603
    "transition-property"           : 1,
4604
    "transition-timing-function"    : 1,
4605
    "unicode-bidi"                  : "normal | embed | bidi-override | inherit",
4606
    "user-modify"                   : "read-only | read-write | write-only | inherit",
4607
    "user-select"                   : "none | text | toggle | element | elements | all | inherit",
4608
    "vertical-align"                : "<percentage> | <length> | baseline | sub | super | top | text-top | middle | bottom | text-bottom | inherit",
4609
    "visibility"                    : "visible | hidden | collapse | inherit",
4610
    "voice-balance"                 : 1,
4611
    "voice-duration"                : 1,
4612
    "voice-family"                  : 1,
4613
    "voice-pitch"                   : 1,
4614
    "voice-pitch-range"             : 1,
4615
    "voice-rate"                    : 1,
4616
    "voice-stress"                  : 1,
4617
    "voice-volume"                  : 1,
4618
    "volume"                        : 1,
4619
    "white-space"                   : "normal | pre | nowrap | pre-wrap | pre-line | inherit",
4620
    "white-space-collapse"          : 1,
4621
    "widows"                        : "<integer> | inherit",
4622
    "width"                         : "<length> | <percentage> | auto | inherit" ,
4623
    "word-break"                    : "normal | keep-all | break-all",
4624
    "word-spacing"                  : "<length> | normal | inherit",
4625
    "word-wrap"                     : 1,
4626
    "z-index"                       : "<integer> | auto | inherit",
4627
    "zoom"                          : "<number> | <percentage> | normal"
4628
};
4629
function PropertyName(text, hack, line, col){
4630
    
4631
    SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_NAME_TYPE);
4632
    this.hack = hack;
4633
4634
}
4635
4636
PropertyName.prototype = new SyntaxUnit();
4637
PropertyName.prototype.constructor = PropertyName;
4638
PropertyName.prototype.toString = function(){
4639
    return (this.hack ? this.hack : "") + this.text;
4640
};
4641
function PropertyValue(parts, line, col){
4642
4643
    SyntaxUnit.call(this, parts.join(" "), line, col, Parser.PROPERTY_VALUE_TYPE);
4644
    this.parts = parts;
4645
    
4646
}
4647
4648
PropertyValue.prototype = new SyntaxUnit();
4649
PropertyValue.prototype.constructor = PropertyValue;
4650
function PropertyValueIterator(value){
4651
    this._i = 0;
4652
    this._parts = value.parts;
4653
    this._marks = [];
4654
    this.value = value;
4655
    
4656
}
4657
PropertyValueIterator.prototype.count = function(){
4658
    return this._parts.length;
4659
};
4660
PropertyValueIterator.prototype.isFirst = function(){
4661
    return this._i === 0;
4662
};
4663
PropertyValueIterator.prototype.hasNext = function(){
4664
    return (this._i < this._parts.length);
4665
};
4666
PropertyValueIterator.prototype.mark = function(){
4667
    this._marks.push(this._i);
4668
};
4669
PropertyValueIterator.prototype.peek = function(count){
4670
    return this.hasNext() ? this._parts[this._i + (count || 0)] : null;
4671
};
4672
PropertyValueIterator.prototype.next = function(){
4673
    return this.hasNext() ? this._parts[this._i++] : null;
4674
};
4675
PropertyValueIterator.prototype.previous = function(){
4676
    return this._i > 0 ? this._parts[--this._i] : null;
4677
};
4678
PropertyValueIterator.prototype.restore = function(){
4679
    if (this._marks.length){
4680
        this._i = this._marks.pop();
4681
    }
4682
};
4683
function PropertyValuePart(text, line, col){
4684
4685
    SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_VALUE_PART_TYPE);
4686
    this.type = "unknown";
4687
    
4688
    var temp;
4689
    if (/^([+\-]?[\d\.]+)([a-z]+)$/i.test(text)){  //dimension
4690
        this.type = "dimension";
4691
        this.value = +RegExp.$1;
4692
        this.units = RegExp.$2;
4693
        switch(this.units.toLowerCase()){
4694
        
4695
            case "em":
4696
            case "rem":
4697
            case "ex":
4698
            case "px":
4699
            case "cm":
4700
            case "mm":
4701
            case "in":
4702
            case "pt":
4703
            case "pc":
4704
            case "ch":
4705
                this.type = "length";
4706
                break;
4707
                
4708
            case "deg":
4709
            case "rad":
4710
            case "grad":
4711
                this.type = "angle";
4712
                break;
4713
            
4714
            case "ms":
4715
            case "s":
4716
                this.type = "time";
4717
                break;
4718
            
4719
            case "hz":
4720
            case "khz":
4721
                this.type = "frequency";
4722
                break;
4723
            
4724
            case "dpi":
4725
            case "dpcm":
4726
                this.type = "resolution";
4727
                break;
4728
                
4729
        }
4730
        
4731
    } else if (/^([+\-]?[\d\.]+)%$/i.test(text)){  //percentage
4732
        this.type = "percentage";
4733
        this.value = +RegExp.$1;
4734
    } else if (/^([+\-]?[\d\.]+)%$/i.test(text)){  //percentage
4735
        this.type = "percentage";
4736
        this.value = +RegExp.$1;
4737
    } else if (/^([+\-]?\d+)$/i.test(text)){  //integer
4738
        this.type = "integer";
4739
        this.value = +RegExp.$1;
4740
    } else if (/^([+\-]?[\d\.]+)$/i.test(text)){  //number
4741
        this.type = "number";
4742
        this.value = +RegExp.$1;
4743
    
4744
    } else if (/^#([a-f0-9]{3,6})/i.test(text)){  //hexcolor
4745
        this.type = "color";
4746
        temp = RegExp.$1;
4747
        if (temp.length == 3){
4748
            this.red    = parseInt(temp.charAt(0)+temp.charAt(0),16);
4749
            this.green  = parseInt(temp.charAt(1)+temp.charAt(1),16);
4750
            this.blue   = parseInt(temp.charAt(2)+temp.charAt(2),16);            
4751
        } else {
4752
            this.red    = parseInt(temp.substring(0,2),16);
4753
            this.green  = parseInt(temp.substring(2,4),16);
4754
            this.blue   = parseInt(temp.substring(4,6),16);            
4755
        }
4756
    } else if (/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i.test(text)){ //rgb() color with absolute numbers
4757
        this.type   = "color";
4758
        this.red    = +RegExp.$1;
4759
        this.green  = +RegExp.$2;
4760
        this.blue   = +RegExp.$3;
4761
    } else if (/^rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)){ //rgb() color with percentages
4762
        this.type   = "color";
4763
        this.red    = +RegExp.$1 * 255 / 100;
4764
        this.green  = +RegExp.$2 * 255 / 100;
4765
        this.blue   = +RegExp.$3 * 255 / 100;
4766
    } else if (/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\s*\)/i.test(text)){ //rgba() color with absolute numbers
4767
        this.type   = "color";
4768
        this.red    = +RegExp.$1;
4769
        this.green  = +RegExp.$2;
4770
        this.blue   = +RegExp.$3;
4771
        this.alpha  = +RegExp.$4;
4772
    } else if (/^rgba\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)){ //rgba() color with percentages
4773
        this.type   = "color";
4774
        this.red    = +RegExp.$1 * 255 / 100;
4775
        this.green  = +RegExp.$2 * 255 / 100;
4776
        this.blue   = +RegExp.$3 * 255 / 100;
4777
        this.alpha  = +RegExp.$4;        
4778
    } else if (/^hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)){ //hsl()
4779
        this.type   = "color";
4780
        this.hue    = +RegExp.$1;
4781
        this.saturation = +RegExp.$2 / 100;
4782
        this.lightness  = +RegExp.$3 / 100;        
4783
    } else if (/^hsla\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)){ //hsla() color with percentages
4784
        this.type   = "color";
4785
        this.hue    = +RegExp.$1;
4786
        this.saturation = +RegExp.$2 / 100;
4787
        this.lightness  = +RegExp.$3 / 100;        
4788
        this.alpha  = +RegExp.$4;        
4789
    } else if (/^url\(["']?([^\)"']+)["']?\)/i.test(text)){ //URI
4790
        this.type   = "uri";
4791
        this.uri    = RegExp.$1;
4792
    } else if (/^([^\(]+)\(/i.test(text)){
4793
        this.type   = "function";
4794
        this.name   = RegExp.$1;
4795
        this.value  = text;
4796
    } else if (/^["'][^"']*["']/.test(text)){    //string
4797
        this.type   = "string";
4798
        this.value  = eval(text);
4799
    } else if (Colors[text.toLowerCase()]){  //named color
4800
        this.type   = "color";
4801
        temp        = Colors[text.toLowerCase()].substring(1);
4802
        this.red    = parseInt(temp.substring(0,2),16);
4803
        this.green  = parseInt(temp.substring(2,4),16);
4804
        this.blue   = parseInt(temp.substring(4,6),16);         
4805
    } else if (/^[\,\/]$/.test(text)){
4806
        this.type   = "operator";
4807
        this.value  = text;
4808
    } else if (/^[a-z\-\u0080-\uFFFF][a-z0-9\-\u0080-\uFFFF]*$/i.test(text)){
4809
        this.type   = "identifier";
4810
        this.value  = text;
4811
    }
4812
4813
}
4814
4815
PropertyValuePart.prototype = new SyntaxUnit();
4816
PropertyValuePart.prototype.constructor = PropertyValuePart;
4817
PropertyValuePart.fromToken = function(token){
4818
    return new PropertyValuePart(token.value, token.startLine, token.startCol);
4819
};
4820
var Pseudos = {
4821
    ":first-letter": 1,
4822
    ":first-line":   1,
4823
    ":before":       1,
4824
    ":after":        1
4825
};
4826
4827
Pseudos.ELEMENT = 1;
4828
Pseudos.CLASS = 2;
4829
4830
Pseudos.isElement = function(pseudo){
4831
    return pseudo.indexOf("::") === 0 || Pseudos[pseudo.toLowerCase()] == Pseudos.ELEMENT;
4832
};
4833
function Selector(parts, line, col){
4834
    
4835
    SyntaxUnit.call(this, parts.join(" "), line, col, Parser.SELECTOR_TYPE);
4836
    this.parts = parts;
4837
    this.specificity = Specificity.calculate(this);
4838
4839
}
4840
4841
Selector.prototype = new SyntaxUnit();
4842
Selector.prototype.constructor = Selector;
4843
function SelectorPart(elementName, modifiers, text, line, col){
4844
    
4845
    SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_PART_TYPE);
4846
    this.elementName = elementName;
4847
    this.modifiers = modifiers;
4848
4849
}
4850
4851
SelectorPart.prototype = new SyntaxUnit();
4852
SelectorPart.prototype.constructor = SelectorPart;
4853
function SelectorSubPart(text, type, line, col){
4854
    
4855
    SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_SUB_PART_TYPE);
4856
    this.type = type;
4857
    this.args = [];
4858
4859
}
4860
4861
SelectorSubPart.prototype = new SyntaxUnit();
4862
SelectorSubPart.prototype.constructor = SelectorSubPart;
4863
function Specificity(a, b, c, d){
4864
    this.a = a;
4865
    this.b = b;
4866
    this.c = c;
4867
    this.d = d;
4868
}
4869
4870
Specificity.prototype = {
4871
    constructor: Specificity,
4872
    compare: function(other){
4873
        var comps = ["a", "b", "c", "d"],
4874
            i, len;
4875
            
4876
        for (i=0, len=comps.length; i < len; i++){
4877
            if (this[comps[i]] < other[comps[i]]){
4878
                return -1;
4879
            } else if (this[comps[i]] > other[comps[i]]){
4880
                return 1;
4881
            }
4882
        }
4883
        
4884
        return 0;
4885
    },
4886
    valueOf: function(){
4887
        return (this.a * 1000) + (this.b * 100) + (this.c * 10) + this.d;
4888
    },
4889
    toString: function(){
4890
        return this.a + "," + this.b + "," + this.c + "," + this.d;
4891
    }
4892
4893
};
4894
Specificity.calculate = function(selector){
4895
4896
    var i, len,
4897
        part,
4898
        b=0, c=0, d=0;
4899
        
4900
    function updateValues(part){
4901
    
4902
        var i, j, len, num,
4903
            elementName = part.elementName ? part.elementName.text : "",
4904
            modifier;
4905
    
4906
        if (elementName && elementName.charAt(elementName.length-1) != "*") {
4907
            d++;
4908
        }    
4909
    
4910
        for (i=0, len=part.modifiers.length; i < len; i++){
4911
            modifier = part.modifiers[i];
4912
            switch(modifier.type){
4913
                case "class":
4914
                case "attribute":
4915
                    c++;
4916
                    break;
4917
                    
4918
                case "id":
4919
                    b++;
4920
                    break;
4921
                    
4922
                case "pseudo":
4923
                    if (Pseudos.isElement(modifier.text)){
4924
                        d++;
4925
                    } else {
4926
                        c++;
4927
                    }                    
4928
                    break;
4929
                    
4930
                case "not":
4931
                    for (j=0, num=modifier.args.length; j < num; j++){
4932
                        updateValues(modifier.args[j]);
4933
                    }
4934
            }    
4935
         }
4936
    }
4937
    
4938
    for (i=0, len=selector.parts.length; i < len; i++){
4939
        part = selector.parts[i];
4940
        
4941
        if (part instanceof SelectorPart){
4942
            updateValues(part);                
4943
        }
4944
    }
4945
    
4946
    return new Specificity(0, b, c, d);
4947
};
4948
4949
var h = /^[0-9a-fA-F]$/,
4950
    nonascii = /^[\u0080-\uFFFF]$/,
4951
    nl = /\n|\r\n|\r|\f/;
4952
4953
4954
function isHexDigit(c){
4955
    return c !== null && h.test(c);
4956
}
4957
4958
function isDigit(c){
4959
    return c !== null && /\d/.test(c);
4960
}
4961
4962
function isWhitespace(c){
4963
    return c !== null && /\s/.test(c);
4964
}
4965
4966
function isNewLine(c){
4967
    return c !== null && nl.test(c);
4968
}
4969
4970
function isNameStart(c){
4971
    return c !== null && (/[a-z_\u0080-\uFFFF\\]/i.test(c));
4972
}
4973
4974
function isNameChar(c){
4975
    return c !== null && (isNameStart(c) || /[0-9\-\\]/.test(c));
4976
}
4977
4978
function isIdentStart(c){
4979
    return c !== null && (isNameStart(c) || /\-\\/.test(c));
4980
}
4981
4982
function mix(receiver, supplier){
4983
	for (var prop in supplier){
4984
		if (supplier.hasOwnProperty(prop)){
4985
			receiver[prop] = supplier[prop];
4986
		}
4987
	}
4988
	return receiver;
4989
}
4990
function TokenStream(input){
4991
	TokenStreamBase.call(this, input, Tokens);
4992
}
4993
4994
TokenStream.prototype = mix(new TokenStreamBase(), {
4995
    _getToken: function(channel){
4996
4997
        var c,
4998
            reader = this._reader,
4999
            token   = null,
5000
            startLine   = reader.getLine(),
5001
            startCol    = reader.getCol();
5002
5003
        c = reader.read();
5004
5005
5006
        while(c){
5007
            switch(c){
5008
                case "/":
5009
5010
                    if(reader.peek() == "*"){
5011
                        token = this.commentToken(c, startLine, startCol);
5012
                    } else {
5013
                        token = this.charToken(c, startLine, startCol);
5014
                    }
5015
                    break;
5016
                case "|":
5017
                case "~":
5018
                case "^":
5019
                case "$":
5020
                case "*":
5021
                    if(reader.peek() == "="){
5022
                        token = this.comparisonToken(c, startLine, startCol);
5023
                    } else {
5024
                        token = this.charToken(c, startLine, startCol);
5025
                    }
5026
                    break;
5027
                case "\"":
5028
                case "'":
5029
                    token = this.stringToken(c, startLine, startCol);
5030
                    break;
5031
                case "#":
5032
                    if (isNameChar(reader.peek())){
5033
                        token = this.hashToken(c, startLine, startCol);
5034
                    } else {
5035
                        token = this.charToken(c, startLine, startCol);
5036
                    }
5037
                    break;
5038
                case ".":
5039
                    if (isDigit(reader.peek())){
5040
                        token = this.numberToken(c, startLine, startCol);
5041
                    } else {
5042
                        token = this.charToken(c, startLine, startCol);
5043
                    }
5044
                    break;
5045
                case "-":
5046
                    if (reader.peek() == "-"){  //could be closing HTML-style comment
5047
                        token = this.htmlCommentEndToken(c, startLine, startCol);
5048
                    } else if (isNameStart(reader.peek())){
5049
                        token = this.identOrFunctionToken(c, startLine, startCol);
5050
                    } else {
5051
                        token = this.charToken(c, startLine, startCol);
5052
                    }
5053
                    break;
5054
                case "!":
5055
                    token = this.importantToken(c, startLine, startCol);
5056
                    break;
5057
                case "@":
5058
                    token = this.atRuleToken(c, startLine, startCol);
5059
                    break;
5060
                case ":":
5061
                    token = this.notToken(c, startLine, startCol);
5062
                    break;
5063
                case "<":
5064
                    token = this.htmlCommentStartToken(c, startLine, startCol);
5065
                    break;
5066
                case "U":
5067
                case "u":
5068
                    if (reader.peek() == "+"){
5069
                        token = this.unicodeRangeToken(c, startLine, startCol);
5070
                        break;
5071
                    }
5072
                default:
5073
                    if (isDigit(c)){
5074
                        token = this.numberToken(c, startLine, startCol);
5075
                    } else
5076
                    if (isWhitespace(c)){
5077
                        token = this.whitespaceToken(c, startLine, startCol);
5078
                    } else
5079
                    if (isIdentStart(c)){
5080
                        token = this.identOrFunctionToken(c, startLine, startCol);
5081
                    } else
5082
                    {
5083
                        token = this.charToken(c, startLine, startCol);
5084
                    }
5085
5086
5087
5088
5089
5090
5091
            }
5092
            break;
5093
        }
5094
5095
        if (!token && c === null){
5096
            token = this.createToken(Tokens.EOF,null,startLine,startCol);
5097
        }
5098
5099
        return token;
5100
    },
5101
    createToken: function(tt, value, startLine, startCol, options){
5102
        var reader = this._reader;
5103
        options = options || {};
5104
5105
        return {
5106
            value:      value,
5107
            type:       tt,
5108
            channel:    options.channel,
5109
            hide:       options.hide || false,
5110
            startLine:  startLine,
5111
            startCol:   startCol,
5112
            endLine:    reader.getLine(),
5113
            endCol:     reader.getCol()
5114
        };
5115
    },
5116
    atRuleToken: function(first, startLine, startCol){
5117
        var rule    = first,
5118
            reader  = this._reader,
5119
            tt      = Tokens.CHAR,
5120
            valid   = false,
5121
            ident,
5122
            c;
5123
        reader.mark();
5124
        ident = this.readName();
5125
        rule = first + ident;
5126
        tt = Tokens.type(rule.toLowerCase());
5127
        if (tt == Tokens.CHAR || tt == Tokens.UNKNOWN){
5128
            if (rule.length > 1){
5129
                tt = Tokens.UNKNOWN_SYM;                
5130
            } else {
5131
                tt = Tokens.CHAR;
5132
                rule = first;
5133
                reader.reset();
5134
            }
5135
        }
5136
5137
        return this.createToken(tt, rule, startLine, startCol);
5138
    },
5139
    charToken: function(c, startLine, startCol){
5140
        var tt = Tokens.type(c);
5141
5142
        if (tt == -1){
5143
            tt = Tokens.CHAR;
5144
        }
5145
5146
        return this.createToken(tt, c, startLine, startCol);
5147
    },
5148
    commentToken: function(first, startLine, startCol){
5149
        var reader  = this._reader,
5150
            comment = this.readComment(first);
5151
5152
        return this.createToken(Tokens.COMMENT, comment, startLine, startCol);
5153
    },
5154
    comparisonToken: function(c, startLine, startCol){
5155
        var reader  = this._reader,
5156
            comparison  = c + reader.read(),
5157
            tt      = Tokens.type(comparison) || Tokens.CHAR;
5158
5159
        return this.createToken(tt, comparison, startLine, startCol);
5160
    },
5161
    hashToken: function(first, startLine, startCol){
5162
        var reader  = this._reader,
5163
            name    = this.readName(first);
5164
5165
        return this.createToken(Tokens.HASH, name, startLine, startCol);
5166
    },
5167
    htmlCommentStartToken: function(first, startLine, startCol){
5168
        var reader      = this._reader,
5169
            text        = first;
5170
5171
        reader.mark();
5172
        text += reader.readCount(3);
5173
5174
        if (text == "<!--"){
5175
            return this.createToken(Tokens.CDO, text, startLine, startCol);
5176
        } else {
5177
            reader.reset();
5178
            return this.charToken(first, startLine, startCol);
5179
        }
5180
    },
5181
    htmlCommentEndToken: function(first, startLine, startCol){
5182
        var reader      = this._reader,
5183
            text        = first;
5184
5185
        reader.mark();
5186
        text += reader.readCount(2);
5187
5188
        if (text == "-->"){
5189
            return this.createToken(Tokens.CDC, text, startLine, startCol);
5190
        } else {
5191
            reader.reset();
5192
            return this.charToken(first, startLine, startCol);
5193
        }
5194
    },
5195
    identOrFunctionToken: function(first, startLine, startCol){
5196
        var reader  = this._reader,
5197
            ident   = this.readName(first),
5198
            tt      = Tokens.IDENT;
5199
        if (reader.peek() == "("){
5200
            ident += reader.read();
5201
            if (ident.toLowerCase() == "url("){
5202
                tt = Tokens.URI;
5203
                ident = this.readURI(ident);
5204
                if (ident.toLowerCase() == "url("){
5205
                    tt = Tokens.FUNCTION;
5206
                }
5207
            } else {
5208
                tt = Tokens.FUNCTION;
5209
            }
5210
        } else if (reader.peek() == ":"){  //might be an IE function
5211
            if (ident.toLowerCase() == "progid"){
5212
                ident += reader.readTo("(");
5213
                tt = Tokens.IE_FUNCTION;
5214
            }
5215
        }
5216
5217
        return this.createToken(tt, ident, startLine, startCol);
5218
    },
5219
    importantToken: function(first, startLine, startCol){
5220
        var reader      = this._reader,
5221
            important   = first,
5222
            tt          = Tokens.CHAR,
5223
            temp,
5224
            c;
5225
5226
        reader.mark();
5227
        c = reader.read();
5228
5229
        while(c){
5230
            if (c == "/"){
5231
                if (reader.peek() != "*"){
5232
                    break;
5233
                } else {
5234
                    temp = this.readComment(c);
5235
                    if (temp === ""){    //broken!
5236
                        break;
5237
                    }
5238
                }
5239
            } else if (isWhitespace(c)){
5240
                important += c + this.readWhitespace();
5241
            } else if (/i/i.test(c)){
5242
                temp = reader.readCount(8);
5243
                if (/mportant/i.test(temp)){
5244
                    important += c + temp;
5245
                    tt = Tokens.IMPORTANT_SYM;
5246
5247
                }
5248
                break;  //we're done
5249
            } else {
5250
                break;
5251
            }
5252
5253
            c = reader.read();
5254
        }
5255
5256
        if (tt == Tokens.CHAR){
5257
            reader.reset();
5258
            return this.charToken(first, startLine, startCol);
5259
        } else {
5260
            return this.createToken(tt, important, startLine, startCol);
5261
        }
5262
5263
5264
    },
5265
    notToken: function(first, startLine, startCol){
5266
        var reader      = this._reader,
5267
            text        = first;
5268
5269
        reader.mark();
5270
        text += reader.readCount(4);
5271
5272
        if (text.toLowerCase() == ":not("){
5273
            return this.createToken(Tokens.NOT, text, startLine, startCol);
5274
        } else {
5275
            reader.reset();
5276
            return this.charToken(first, startLine, startCol);
5277
        }
5278
    },
5279
    numberToken: function(first, startLine, startCol){
5280
        var reader  = this._reader,
5281
            value   = this.readNumber(first),
5282
            ident,
5283
            tt      = Tokens.NUMBER,
5284
            c       = reader.peek();
5285
5286
        if (isIdentStart(c)){
5287
            ident = this.readName(reader.read());
5288
            value += ident;
5289
5290
            if (/^em$|^ex$|^px$|^gd$|^rem$|^vw$|^vh$|^vm$|^ch$|^cm$|^mm$|^in$|^pt$|^pc$/i.test(ident)){
5291
                tt = Tokens.LENGTH;
5292
            } else if (/^deg|^rad$|^grad$/i.test(ident)){
5293
                tt = Tokens.ANGLE;
5294
            } else if (/^ms$|^s$/i.test(ident)){
5295
                tt = Tokens.TIME;
5296
            } else if (/^hz$|^khz$/i.test(ident)){
5297
                tt = Tokens.FREQ;
5298
            } else if (/^dpi$|^dpcm$/i.test(ident)){
5299
                tt = Tokens.RESOLUTION;
5300
            } else {
5301
                tt = Tokens.DIMENSION;
5302
            }
5303
5304
        } else if (c == "%"){
5305
            value += reader.read();
5306
            tt = Tokens.PERCENTAGE;
5307
        }
5308
5309
        return this.createToken(tt, value, startLine, startCol);
5310
    },
5311
    stringToken: function(first, startLine, startCol){
5312
        var delim   = first,
5313
            string  = first,
5314
            reader  = this._reader,
5315
            prev    = first,
5316
            tt      = Tokens.STRING,
5317
            c       = reader.read();
5318
5319
        while(c){
5320
            string += c;
5321
            if (c == delim && prev != "\\"){
5322
                break;
5323
            }
5324
            if (isNewLine(reader.peek()) && c != "\\"){
5325
                tt = Tokens.INVALID;
5326
                break;
5327
            }
5328
            prev = c;
5329
            c = reader.read();
5330
        }
5331
        if (c === null){
5332
            tt = Tokens.INVALID;
5333
        }
5334
5335
        return this.createToken(tt, string, startLine, startCol);
5336
    },
5337
5338
    unicodeRangeToken: function(first, startLine, startCol){
5339
        var reader  = this._reader,
5340
            value   = first,
5341
            temp,
5342
            tt      = Tokens.CHAR;
5343
        if (reader.peek() == "+"){
5344
            reader.mark();
5345
            value += reader.read();
5346
            value += this.readUnicodeRangePart(true);
5347
            if (value.length == 2){
5348
                reader.reset();
5349
            } else {
5350
5351
                tt = Tokens.UNICODE_RANGE;
5352
                if (value.indexOf("?") == -1){
5353
5354
                    if (reader.peek() == "-"){
5355
                        reader.mark();
5356
                        temp = reader.read();
5357
                        temp += this.readUnicodeRangePart(false);
5358
                        if (temp.length == 1){
5359
                            reader.reset();
5360
                        } else {
5361
                            value += temp;
5362
                        }
5363
                    }
5364
5365
                }
5366
            }
5367
        }
5368
5369
        return this.createToken(tt, value, startLine, startCol);
5370
    },
5371
    whitespaceToken: function(first, startLine, startCol){
5372
        var reader  = this._reader,
5373
            value   = first + this.readWhitespace();
5374
        return this.createToken(Tokens.S, value, startLine, startCol);
5375
    },
5376
5377
    readUnicodeRangePart: function(allowQuestionMark){
5378
        var reader  = this._reader,
5379
            part = "",
5380
            c       = reader.peek();
5381
        while(isHexDigit(c) && part.length < 6){
5382
            reader.read();
5383
            part += c;
5384
            c = reader.peek();
5385
        }
5386
        if (allowQuestionMark){
5387
            while(c == "?" && part.length < 6){
5388
                reader.read();
5389
                part += c;
5390
                c = reader.peek();
5391
            }
5392
        }
5393
5394
        return part;
5395
    },
5396
5397
    readWhitespace: function(){
5398
        var reader  = this._reader,
5399
            whitespace = "",
5400
            c       = reader.peek();
5401
5402
        while(isWhitespace(c)){
5403
            reader.read();
5404
            whitespace += c;
5405
            c = reader.peek();
5406
        }
5407
5408
        return whitespace;
5409
    },
5410
    readNumber: function(first){
5411
        var reader  = this._reader,
5412
            number  = first,
5413
            hasDot  = (first == "."),
5414
            c       = reader.peek();
5415
5416
5417
        while(c){
5418
            if (isDigit(c)){
5419
                number += reader.read();
5420
            } else if (c == "."){
5421
                if (hasDot){
5422
                    break;
5423
                } else {
5424
                    hasDot = true;
5425
                    number += reader.read();
5426
                }
5427
            } else {
5428
                break;
5429
            }
5430
5431
            c = reader.peek();
5432
        }
5433
5434
        return number;
5435
    },
5436
    readString: function(){
5437
        var reader  = this._reader,
5438
            delim   = reader.read(),
5439
            string  = delim,
5440
            prev    = delim,
5441
            c       = reader.peek();
5442
5443
        while(c){
5444
            c = reader.read();
5445
            string += c;
5446
            if (c == delim && prev != "\\"){
5447
                break;
5448
            }
5449
            if (isNewLine(reader.peek()) && c != "\\"){
5450
                string = "";
5451
                break;
5452
            }
5453
            prev = c;
5454
            c = reader.peek();
5455
        }
5456
        if (c === null){
5457
            string = "";
5458
        }
5459
5460
        return string;
5461
    },
5462
    readURI: function(first){
5463
        var reader  = this._reader,
5464
            uri     = first,
5465
            inner   = "",
5466
            c       = reader.peek();
5467
5468
        reader.mark();
5469
        while(c && isWhitespace(c)){
5470
            reader.read();
5471
            c = reader.peek();
5472
        }
5473
        if (c == "'" || c == "\""){
5474
            inner = this.readString();
5475
        } else {
5476
            inner = this.readURL();
5477
        }
5478
5479
        c = reader.peek();
5480
        while(c && isWhitespace(c)){
5481
            reader.read();
5482
            c = reader.peek();
5483
        }
5484
        if (inner === "" || c != ")"){
5485
            uri = first;
5486
            reader.reset();
5487
        } else {
5488
            uri += inner + reader.read();
5489
        }
5490
5491
        return uri;
5492
    },
5493
    readURL: function(){
5494
        var reader  = this._reader,
5495
            url     = "",
5496
            c       = reader.peek();
5497
        while (/^[!#$%&\\*-~]$/.test(c)){
5498
            url += reader.read();
5499
            c = reader.peek();
5500
        }
5501
5502
        return url;
5503
5504
    },
5505
    readName: function(first){
5506
        var reader  = this._reader,
5507
            ident   = first || "",
5508
            c       = reader.peek();
5509
5510
        while(true){
5511
            if (c == "\\"){
5512
                ident += this.readEscape(reader.read());
5513
                c = reader.peek();
5514
            } else if(c && isNameChar(c)){
5515
                ident += reader.read();
5516
                c = reader.peek();
5517
            } else {
5518
                break;
5519
            }
5520
        }
5521
5522
        return ident;
5523
    },
5524
    
5525
    readEscape: function(first){
5526
        var reader  = this._reader,
5527
            cssEscape = first || "",
5528
            i       = 0,
5529
            c       = reader.peek();    
5530
    
5531
        if (isHexDigit(c)){
5532
            do {
5533
                cssEscape += reader.read();
5534
                c = reader.peek();
5535
            } while(c && isHexDigit(c) && ++i < 6);
5536
        }
5537
        
5538
        if (cssEscape.length == 3 && /\s/.test(c) ||
5539
            cssEscape.length == 7 || cssEscape.length == 1){
5540
                reader.read();
5541
        } else {
5542
            c = "";
5543
        }
5544
        
5545
        return cssEscape + c;
5546
    },
5547
    
5548
    readComment: function(first){
5549
        var reader  = this._reader,
5550
            comment = first || "",
5551
            c       = reader.read();
5552
5553
        if (c == "*"){
5554
            while(c){
5555
                comment += c;
5556
                if (comment.length > 2 && c == "*" && reader.peek() == "/"){
5557
                    comment += reader.read();
5558
                    break;
5559
                }
5560
5561
                c = reader.read();
5562
            }
5563
5564
            return comment;
5565
        } else {
5566
            return "";
5567
        }
5568
5569
    }
5570
});
5571
5572
5573
var Tokens  = [
5574
    { name: "CDO"},
5575
    { name: "CDC"},
5576
    { name: "S", whitespace: true/*, channel: "ws"*/},
5577
    { name: "COMMENT", comment: true, hide: true, channel: "comment" },
5578
    { name: "INCLUDES", text: "~="},
5579
    { name: "DASHMATCH", text: "|="},
5580
    { name: "PREFIXMATCH", text: "^="},
5581
    { name: "SUFFIXMATCH", text: "$="},
5582
    { name: "SUBSTRINGMATCH", text: "*="},
5583
    { name: "STRING"},     
5584
    { name: "IDENT"},
5585
    { name: "HASH"},
5586
    { name: "IMPORT_SYM", text: "@import"},
5587
    { name: "PAGE_SYM", text: "@page"},
5588
    { name: "MEDIA_SYM", text: "@media"},
5589
    { name: "FONT_FACE_SYM", text: "@font-face"},
5590
    { name: "CHARSET_SYM", text: "@charset"},
5591
    { name: "NAMESPACE_SYM", text: "@namespace"},
5592
    { name: "UNKNOWN_SYM" },
5593
    { name: "KEYFRAMES_SYM", text: [ "@keyframes", "@-webkit-keyframes", "@-moz-keyframes", "@-o-keyframes" ] },
5594
    { name: "IMPORTANT_SYM"},
5595
    { name: "LENGTH"},
5596
    { name: "ANGLE"},
5597
    { name: "TIME"},
5598
    { name: "FREQ"},
5599
    { name: "DIMENSION"},
5600
    { name: "PERCENTAGE"},
5601
    { name: "NUMBER"},
5602
    { name: "URI"},
5603
    { name: "FUNCTION"},
5604
    { name: "UNICODE_RANGE"},    
5605
    { name: "INVALID"},
5606
    { name: "PLUS", text: "+" },
5607
    { name: "GREATER", text: ">"},
5608
    { name: "COMMA", text: ","},
5609
    { name: "TILDE", text: "~"},
5610
    { name: "NOT"},        
5611
    { name: "TOPLEFTCORNER_SYM", text: "@top-left-corner"},
5612
    { name: "TOPLEFT_SYM", text: "@top-left"},
5613
    { name: "TOPCENTER_SYM", text: "@top-center"},
5614
    { name: "TOPRIGHT_SYM", text: "@top-right"},
5615
    { name: "TOPRIGHTCORNER_SYM", text: "@top-right-corner"},
5616
    { name: "BOTTOMLEFTCORNER_SYM", text: "@bottom-left-corner"},
5617
    { name: "BOTTOMLEFT_SYM", text: "@bottom-left"},
5618
    { name: "BOTTOMCENTER_SYM", text: "@bottom-center"},
5619
    { name: "BOTTOMRIGHT_SYM", text: "@bottom-right"},
5620
    { name: "BOTTOMRIGHTCORNER_SYM", text: "@bottom-right-corner"},
5621
    { name: "LEFTTOP_SYM", text: "@left-top"},
5622
    { name: "LEFTMIDDLE_SYM", text: "@left-middle"},
5623
    { name: "LEFTBOTTOM_SYM", text: "@left-bottom"},
5624
    { name: "RIGHTTOP_SYM", text: "@right-top"},
5625
    { name: "RIGHTMIDDLE_SYM", text: "@right-middle"},
5626
    { name: "RIGHTBOTTOM_SYM", text: "@right-bottom"},
5627
    { name: "RESOLUTION", state: "media"},
5628
    { name: "IE_FUNCTION" },
5629
    { name: "CHAR" },
5630
    {
5631
        name: "PIPE",
5632
        text: "|"
5633
    },
5634
    {
5635
        name: "SLASH",
5636
        text: "/"
5637
    },
5638
    {
5639
        name: "MINUS",
5640
        text: "-"
5641
    },
5642
    {
5643
        name: "STAR",
5644
        text: "*"
5645
    },
5646
5647
    {
5648
        name: "LBRACE",
5649
        text: "{"
5650
    },   
5651
    {
5652
        name: "RBRACE",
5653
        text: "}"
5654
    },      
5655
    {
5656
        name: "LBRACKET",
5657
        text: "["
5658
    },   
5659
    {
5660
        name: "RBRACKET",
5661
        text: "]"
5662
    },    
5663
    {
5664
        name: "EQUALS",
5665
        text: "="
5666
    },
5667
    {
5668
        name: "COLON",
5669
        text: ":"
5670
    },    
5671
    {
5672
        name: "SEMICOLON",
5673
        text: ";"
5674
    },    
5675
 
5676
    {
5677
        name: "LPAREN",
5678
        text: "("
5679
    },   
5680
    {
5681
        name: "RPAREN",
5682
        text: ")"
5683
    },     
5684
    {
5685
        name: "DOT",
5686
        text: "."
5687
    }
5688
];
5689
5690
(function(){
5691
5692
    var nameMap = [],
5693
        typeMap = {};
5694
    
5695
    Tokens.UNKNOWN = -1;
5696
    Tokens.unshift({name:"EOF"});
5697
    for (var i=0, len = Tokens.length; i < len; i++){
5698
        nameMap.push(Tokens[i].name);
5699
        Tokens[Tokens[i].name] = i;
5700
        if (Tokens[i].text){
5701
            if (Tokens[i].text instanceof Array){
5702
                for (var j=0; j < Tokens[i].text.length; j++){
5703
                    typeMap[Tokens[i].text[j]] = i;
5704
                }
5705
            } else {
5706
                typeMap[Tokens[i].text] = i;
5707
            }
5708
        }
5709
    }
5710
    
5711
    Tokens.name = function(tt){
5712
        return nameMap[tt];
5713
    };
5714
    
5715
    Tokens.type = function(c){
5716
        return typeMap[c] || -1;
5717
    };
5718
5719
})();
5720
var Validation = {
5721
5722
    validate: function(property, value){
5723
        var name        = property.toString().toLowerCase(),
5724
            parts       = value.parts,
5725
            expression  = new PropertyValueIterator(value),
5726
            spec        = Properties[name],
5727
            part,
5728
            valid,            
5729
            j, count,
5730
            msg,
5731
            types,
5732
            last,
5733
            literals,
5734
            max, multi, group;
5735
            
5736
        if (!spec) {
5737
            if (name.indexOf("-") !== 0){    //vendor prefixed are ok
5738
                throw new ValidationError("Unknown property '" + property + "'.", property.line, property.col);
5739
            }
5740
        } else if (typeof spec != "number"){
5741
            if (typeof spec == "string"){
5742
                if (spec.indexOf("||") > -1) {
5743
                    this.groupProperty(spec, expression);
5744
                } else {
5745
                    this.singleProperty(spec, expression, 1);
5746
                }
5747
5748
            } else if (spec.multi) {
5749
                this.multiProperty(spec.multi, expression, spec.comma, spec.max || Infinity);
5750
            } else if (typeof spec == "function") {
5751
                spec(expression);
5752
            }
5753
5754
        }
5755
5756
    },
5757
    
5758
    singleProperty: function(types, expression, max, partial) {
5759
5760
        var result      = false,
5761
            value       = expression.value,
5762
            count       = 0,
5763
            part;
5764
         
5765
        while (expression.hasNext() && count < max) {
5766
            result = ValidationTypes.isAny(expression, types);
5767
            if (!result) {
5768
                break;
5769
            }
5770
            count++;
5771
        }
5772
        
5773
        if (!result) {
5774
            if (expression.hasNext() && !expression.isFirst()) {
5775
                part = expression.peek();
5776
                throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5777
            } else {
5778
                 throw new ValidationError("Expected (" + types + ") but found '" + value + "'.", value.line, value.col);
5779
            }        
5780
        } else if (expression.hasNext()) {
5781
            part = expression.next();
5782
            throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5783
        }          
5784
                 
5785
    },    
5786
    
5787
    multiProperty: function (types, expression, comma, max) {
5788
5789
        var result      = false,
5790
            value       = expression.value,
5791
            count       = 0,
5792
            sep         = false,
5793
            part;
5794
            
5795
        while(expression.hasNext() && !result && count < max) {
5796
            if (ValidationTypes.isAny(expression, types)) {
5797
                count++;
5798
                if (!expression.hasNext()) {
5799
                    result = true;
5800
5801
                } else if (comma) {
5802
                    if (expression.peek() == ",") {
5803
                        part = expression.next();
5804
                    } else {
5805
                        break;
5806
                    }
5807
                }
5808
            } else {
5809
                break;
5810
5811
            }
5812
        }
5813
        
5814
        if (!result) {
5815
            if (expression.hasNext() && !expression.isFirst()) {
5816
                part = expression.peek();
5817
                throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5818
            } else {
5819
                part = expression.previous();
5820
                if (comma && part == ",") {
5821
                    throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col); 
5822
                } else {
5823
                    throw new ValidationError("Expected (" + types + ") but found '" + value + "'.", value.line, value.col);
5824
                }
5825
            }
5826
        
5827
        } else if (expression.hasNext()) {
5828
            part = expression.next();
5829
            throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5830
        }  
5831
5832
    },
5833
    
5834
    groupProperty: function (types, expression, comma) {
5835
5836
        var result      = false,
5837
            value       = expression.value,
5838
            typeCount   = types.split("||").length,
5839
            groups      = { count: 0 },
5840
            partial     = false,
5841
            name,
5842
            part;
5843
            
5844
        while(expression.hasNext() && !result) {
5845
            name = ValidationTypes.isAnyOfGroup(expression, types);
5846
            if (name) {
5847
                if (groups[name]) {
5848
                    break;
5849
                } else {
5850
                    groups[name] = 1;
5851
                    groups.count++;
5852
                    partial = true;
5853
                    
5854
                    if (groups.count == typeCount || !expression.hasNext()) {
5855
                        result = true;
5856
                    }
5857
                }
5858
            } else {
5859
                break;
5860
            }
5861
        }
5862
        
5863
        if (!result) {        
5864
            if (partial && expression.hasNext()) {
5865
                    part = expression.peek();
5866
                    throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5867
            } else {
5868
                throw new ValidationError("Expected (" + types + ") but found '" + value + "'.", value.line, value.col);
5869
            }
5870
        } else if (expression.hasNext()) {
5871
            part = expression.next();
5872
            throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5873
        }           
5874
    }
5875
5876
    
5877
5878
};
5879
function ValidationError(message, line, col){
5880
    this.col = col;
5881
    this.line = line;
5882
    this.message = message;
5883
5884
}
5885
ValidationError.prototype = new Error();
5886
var ValidationTypes = {
5887
5888
    isLiteral: function (part, literals) {
5889
        var text = part.text.toString().toLowerCase(),
5890
            args = literals.split(" | "),
5891
            i, len, found = false;
5892
        
5893
        for (i=0,len=args.length; i < len && !found; i++){
5894
            if (text == args[i].toLowerCase()){
5895
                found = true;
5896
            }
5897
        }
5898
        
5899
        return found;    
5900
    },
5901
    
5902
    isSimple: function(type) {
5903
        return !!this.simple[type];
5904
    },
5905
    
5906
    isComplex: function(type) {
5907
        return !!this.complex[type];
5908
    },
5909
    isAny: function (expression, types) {
5910
        var args = types.split(" | "),
5911
            i, len, found = false;
5912
        
5913
        for (i=0,len=args.length; i < len && !found && expression.hasNext(); i++){
5914
            found = this.isType(expression, args[i]);
5915
        }
5916
        
5917
        return found;    
5918
    },
5919
    isAnyOfGroup: function(expression, types) {
5920
        var args = types.split(" || "),
5921
            i, len, found = false;
5922
        
5923
        for (i=0,len=args.length; i < len && !found; i++){
5924
            found = this.isType(expression, args[i]);
5925
        }
5926
        
5927
        return found ? args[i-1] : false;
5928
    },
5929
    isType: function (expression, type) {
5930
        var part = expression.peek(),
5931
            result = false;
5932
            
5933
        if (type.charAt(0) != "<") {
5934
            result = this.isLiteral(part, type);
5935
            if (result) {
5936
                expression.next();
5937
            }
5938
        } else if (this.simple[type]) {
5939
            result = this.simple[type](part);
5940
            if (result) {
5941
                expression.next();
5942
            }
5943
        } else {
5944
            result = this.complex[type](expression);
5945
        }
5946
        
5947
        return result;
5948
    },
5949
    
5950
    
5951
    
5952
    simple: {
5953
5954
        "<absolute-size>": function(part){
5955
            return ValidationTypes.isLiteral(part, "xx-small | x-small | small | medium | large | x-large | xx-large");
5956
        },
5957
        
5958
        "<attachment>": function(part){
5959
            return ValidationTypes.isLiteral(part, "scroll | fixed | local");
5960
        },
5961
        
5962
        "<attr>": function(part){
5963
            return part.type == "function" && part.name == "attr";
5964
        },
5965
                
5966
        "<bg-image>": function(part){
5967
            return this["<image>"](part) || this["<gradient>"](part) ||  part == "none";
5968
        },        
5969
        
5970
        "<gradient>": function(part) {
5971
            return part.type == "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?(?:repeating\-)?(?:radial\-|linear\-)?gradient/i.test(part);
5972
        },
5973
        
5974
        "<box>": function(part){
5975
            return ValidationTypes.isLiteral(part, "padding-box | border-box | content-box");
5976
        },
5977
        
5978
        "<content>": function(part){
5979
            return part.type == "function" && part.name == "content";
5980
        },        
5981
        
5982
        "<relative-size>": function(part){
5983
            return ValidationTypes.isLiteral(part, "smaller | larger");
5984
        },
5985
        "<ident>": function(part){
5986
            return part.type == "identifier";
5987
        },
5988
        
5989
        "<length>": function(part){
5990
            return part.type == "length" || part.type == "number" || part.type == "integer" || part == "0";
5991
        },
5992
        
5993
        "<color>": function(part){
5994
            return part.type == "color" || part == "transparent";
5995
        },
5996
        
5997
        "<number>": function(part){
5998
            return part.type == "number" || this["<integer>"](part);
5999
        },
6000
        
6001
        "<integer>": function(part){
6002
            return part.type == "integer";
6003
        },
6004
        
6005
        "<line>": function(part){
6006
            return part.type == "integer";
6007
        },
6008
        
6009
        "<angle>": function(part){
6010
            return part.type == "angle";
6011
        },        
6012
        
6013
        "<uri>": function(part){
6014
            return part.type == "uri";
6015
        },
6016
        
6017
        "<image>": function(part){
6018
            return this["<uri>"](part);
6019
        },
6020
        
6021
        "<percentage>": function(part){
6022
            return part.type == "percentage" || part == "0";
6023
        },
6024
6025
        "<border-width>": function(part){
6026
            return this["<length>"](part) || ValidationTypes.isLiteral(part, "thin | medium | thick");
6027
        },
6028
        
6029
        "<border-style>": function(part){
6030
            return ValidationTypes.isLiteral(part, "none | hidden | dotted | dashed | solid | double | groove | ridge | inset | outset");
6031
        },
6032
        
6033
        "<margin-width>": function(part){
6034
            return this["<length>"](part) || this["<percentage>"](part) || ValidationTypes.isLiteral(part, "auto");
6035
        },
6036
        
6037
        "<padding-width>": function(part){
6038
            return this["<length>"](part) || this["<percentage>"](part);
6039
        },
6040
        
6041
        "<shape>": function(part){
6042
            return part.type == "function" && (part.name == "rect" || part.name == "inset-rect");
6043
        },
6044
        
6045
        "<time>": function(part) {
6046
            return part.type == "time";
6047
        }
6048
    },
6049
    
6050
    complex: {
6051
6052
        "<bg-position>": function(expression){
6053
            var types   = this,
6054
                result  = false,
6055
                numeric = "<percentage> | <length>",
6056
                xDir    = "left | center | right",
6057
                yDir    = "top | center | bottom",
6058
                part,
6059
                i, len;            
6060
                
6061
            if (ValidationTypes.isAny(expression, "top | bottom")) {
6062
                result = true;
6063
            } else {
6064
                if (ValidationTypes.isAny(expression, numeric)){
6065
                    if (expression.hasNext()){
6066
                        result = ValidationTypes.isAny(expression, numeric + " | " + yDir);
6067
                    }
6068
                } else if (ValidationTypes.isAny(expression, xDir)){
6069
                    if (expression.hasNext()){
6070
                        if (ValidationTypes.isAny(expression, yDir)){
6071
                            result = true;
6072
                      
6073
                            ValidationTypes.isAny(expression, numeric);
6074
                            
6075
                        } else if (ValidationTypes.isAny(expression, numeric)){
6076
                            if (ValidationTypes.isAny(expression, yDir)){                                    
6077
                                ValidationTypes.isAny(expression, numeric);                               
6078
                            }
6079
                            
6080
                            result = true;
6081
                        }
6082
                    }
6083
                }                                 
6084
            }            
6085
6086
            
6087
            return result;
6088
        },
6089
6090
        "<bg-size>": function(expression){
6091
            var types   = this,
6092
                result  = false,
6093
                numeric = "<percentage> | <length> | auto",
6094
                part,
6095
                i, len;      
6096
      
6097
            if (ValidationTypes.isAny(expression, "cover | contain")) {
6098
                result = true;
6099
            } else if (ValidationTypes.isAny(expression, numeric)) {
6100
                result = true;                
6101
                ValidationTypes.isAny(expression, numeric);
6102
            }
6103
            
6104
            return result;
6105
        },
6106
        
6107
        "<repeat-style>": function(expression){
6108
            var result  = false,
6109
                values  = "repeat | space | round | no-repeat",
6110
                part;
6111
            
6112
            if (expression.hasNext()){
6113
                part = expression.next();
6114
                
6115
                if (ValidationTypes.isLiteral(part, "repeat-x | repeat-y")) {
6116
                    result = true;                    
6117
                } else if (ValidationTypes.isLiteral(part, values)) {
6118
                    result = true;
6119
6120
                    if (expression.hasNext() && ValidationTypes.isLiteral(expression.peek(), values)) {
6121
                        expression.next();
6122
                    }
6123
                }
6124
            }
6125
            
6126
            return result;
6127
            
6128
        },
6129
        
6130
        "<shadow>": function(expression) {
6131
            var result  = false,
6132
                count   = 0,
6133
                inset   = false,
6134
                color   = false,
6135
                part;
6136
                
6137
            if (expression.hasNext()) {            
6138
                
6139
                if (ValidationTypes.isAny(expression, "inset")){
6140
                    inset = true;
6141
                }
6142
                
6143
                if (ValidationTypes.isAny(expression, "<color>")) {
6144
                    color = true;
6145
                }                
6146
                
6147
                while (ValidationTypes.isAny(expression, "<length>") && count < 4) {
6148
                    count++;
6149
                }
6150
                
6151
                
6152
                if (expression.hasNext()) {
6153
                    if (!color) {
6154
                        ValidationTypes.isAny(expression, "<color>");
6155
                    }
6156
                    
6157
                    if (!inset) {
6158
                        ValidationTypes.isAny(expression, "inset");
6159
                    }
6160
6161
                }
6162
                
6163
                result = (count >= 2 && count <= 4);
6164
            
6165
            }
6166
            
6167
            return result;
6168
        },
6169
        
6170
        "<x-one-radius>": function(expression) {
6171
            var result  = false,
6172
                count   = 0,
6173
                numeric = "<length> | <percentage>",
6174
                part;
6175
                
6176
            if (ValidationTypes.isAny(expression, numeric)){
6177
                result = true;
6178
                
6179
                ValidationTypes.isAny(expression, numeric);
6180
            }                
6181
            
6182
            return result;
6183
        }
6184
    }
6185
};
6186
6187
6188
parserlib.css = {
6189
Colors              :Colors,    
6190
Combinator          :Combinator,                
6191
Parser              :Parser,
6192
PropertyName        :PropertyName,
6193
PropertyValue       :PropertyValue,
6194
PropertyValuePart   :PropertyValuePart,
6195
MediaFeature        :MediaFeature,
6196
MediaQuery          :MediaQuery,
6197
Selector            :Selector,
6198
SelectorPart        :SelectorPart,
6199
SelectorSubPart     :SelectorSubPart,
6200
Specificity         :Specificity,
6201
TokenStream         :TokenStream,
6202
Tokens              :Tokens,
6203
ValidationError     :ValidationError
6204
};
6205
})();
6206
var CSSLint = (function(){
6207
6208
    var rules      = [],
6209
        formatters = [],
6210
        api        = new parserlib.util.EventTarget();
6211
        
6212
    api.version = "0.9.9";
6213
    api.addRule = function(rule){
6214
        rules.push(rule);
6215
        rules[rule.id] = rule;
6216
    };
6217
    api.clearRules = function(){
6218
        rules = [];
6219
    };
6220
    api.getRules = function(){
6221
        return [].concat(rules).sort(function(a,b){ 
6222
            return a.id > b.id ? 1 : 0;
6223
        });
6224
    };
6225
    api.getRuleset = function() {
6226
        var ruleset = {},
6227
            i = 0,
6228
            len = rules.length;
6229
        
6230
        while (i < len){
6231
            ruleset[rules[i++].id] = 1;    //by default, everything is a warning
6232
        }
6233
        
6234
        return ruleset;
6235
    };
6236
    api.addFormatter = function(formatter) {
6237
        formatters[formatter.id] = formatter;
6238
    };
6239
    api.getFormatter = function(formatId){
6240
        return formatters[formatId];
6241
    };
6242
    api.format = function(results, filename, formatId, options) {
6243
        var formatter = this.getFormatter(formatId),
6244
            result = null;
6245
            
6246
        if (formatter){
6247
            result = formatter.startFormat();
6248
            result += formatter.formatResults(results, filename, options || {});
6249
            result += formatter.endFormat();
6250
        }
6251
        
6252
        return result;
6253
    };
6254
    api.hasFormat = function(formatId){
6255
        return formatters.hasOwnProperty(formatId);
6256
    };
6257
    api.verify = function(text, ruleset){
6258
6259
        var i       = 0,
6260
            len     = rules.length,
6261
            reporter,
6262
            lines,
6263
            report,
6264
            parser = new parserlib.css.Parser({ starHack: true, ieFilters: true,
6265
                                                underscoreHack: true, strict: false });
6266
        lines = text.replace(/\n\r?/g, "$split$").split('$split$');
6267
        
6268
        if (!ruleset){
6269
            ruleset = this.getRuleset();
6270
        }
6271
        
6272
        reporter = new Reporter(lines, ruleset);
6273
        
6274
        ruleset.errors = 2;       //always report parsing errors as errors
6275
        for (i in ruleset){
6276
            if(ruleset.hasOwnProperty(i)){
6277
                if (rules[i]){
6278
                    rules[i].init(parser, reporter);
6279
                }
6280
            }
6281
        }
6282
        try {
6283
            parser.parse(text);
6284
        } catch (ex) {
6285
            reporter.error("Fatal error, cannot continue: " + ex.message, ex.line, ex.col, {});
6286
        }
6287
6288
        report = {
6289
            messages    : reporter.messages,
6290
            stats       : reporter.stats
6291
        };
6292
        report.messages.sort(function (a, b){
6293
            if (a.rollup && !b.rollup){
6294
                return 1;
6295
            } else if (!a.rollup && b.rollup){
6296
                return -1;
6297
            } else {
6298
                return a.line - b.line;
6299
            }
6300
        });        
6301
        
6302
        return report;
6303
    };
6304
6305
    return api;
6306
6307
})();
6308
function Reporter(lines, ruleset){
6309
    this.messages = [];
6310
    this.stats = [];
6311
    this.lines = lines;
6312
    this.ruleset = ruleset;
6313
}
6314
6315
Reporter.prototype = {
6316
    constructor: Reporter,
6317
    error: function(message, line, col, rule){
6318
        this.messages.push({
6319
            type    : "error",
6320
            line    : line,
6321
            col     : col,
6322
            message : message,
6323
            evidence: this.lines[line-1],
6324
            rule    : rule || {}
6325
        });
6326
    },
6327
    warn: function(message, line, col, rule){
6328
        this.report(message, line, col, rule);
6329
    },
6330
    report: function(message, line, col, rule){
6331
        this.messages.push({
6332
            type    : this.ruleset[rule.id] == 2 ? "error" : "warning",
6333
            line    : line,
6334
            col     : col,
6335
            message : message,
6336
            evidence: this.lines[line-1],
6337
            rule    : rule
6338
        });
6339
    },
6340
    info: function(message, line, col, rule){
6341
        this.messages.push({
6342
            type    : "info",
6343
            line    : line,
6344
            col     : col,
6345
            message : message,
6346
            evidence: this.lines[line-1],
6347
            rule    : rule
6348
        });
6349
    },
6350
    rollupError: function(message, rule){
6351
        this.messages.push({
6352
            type    : "error",
6353
            rollup  : true,
6354
            message : message,
6355
            rule    : rule
6356
        });
6357
    },
6358
    rollupWarn: function(message, rule){
6359
        this.messages.push({
6360
            type    : "warning",
6361
            rollup  : true,
6362
            message : message,
6363
            rule    : rule
6364
        });
6365
    },
6366
    stat: function(name, value){
6367
        this.stats[name] = value;
6368
    }
6369
};
6370
CSSLint._Reporter = Reporter;
6371
CSSLint.Util = {
6372
    mix: function(receiver, supplier){
6373
        var prop;
6374
6375
        for (prop in supplier){
6376
            if (supplier.hasOwnProperty(prop)){
6377
                receiver[prop] = supplier[prop];
6378
            }
6379
        }
6380
6381
        return prop;
6382
    },
6383
    indexOf: function(values, value){
6384
        if (values.indexOf){
6385
            return values.indexOf(value);
6386
        } else {
6387
            for (var i=0, len=values.length; i < len; i++){
6388
                if (values[i] === value){
6389
                    return i;
6390
                }
6391
            }
6392
            return -1;
6393
        }
6394
    },
6395
    forEach: function(values, func) {
6396
        if (values.forEach){
6397
            return values.forEach(func);
6398
        } else {
6399
            for (var i=0, len=values.length; i < len; i++){
6400
                func(values[i], i, values);
6401
            }
6402
        }
6403
    }
6404
};
6405
CSSLint.addRule({
6406
    id: "adjoining-classes",
6407
    name: "Disallow adjoining classes",
6408
    desc: "Don't use adjoining classes.",
6409
    browsers: "IE6",
6410
    init: function(parser, reporter){
6411
        var rule = this;
6412
        parser.addListener("startrule", function(event){
6413
            var selectors = event.selectors,
6414
                selector,
6415
                part,
6416
                modifier,
6417
                classCount,
6418
                i, j, k;
6419
6420
            for (i=0; i < selectors.length; i++){
6421
                selector = selectors[i];
6422
                for (j=0; j < selector.parts.length; j++){
6423
                    part = selector.parts[j];
6424
                    if (part.type == parser.SELECTOR_PART_TYPE){
6425
                        classCount = 0;
6426
                        for (k=0; k < part.modifiers.length; k++){
6427
                            modifier = part.modifiers[k];
6428
                            if (modifier.type == "class"){
6429
                                classCount++;
6430
                            }
6431
                            if (classCount > 1){
6432
                                reporter.report("Don't use adjoining classes.", part.line, part.col, rule);
6433
                            }
6434
                        }
6435
                    }
6436
                }
6437
            }
6438
        });
6439
    }
6440
6441
});
6442
CSSLint.addRule({
6443
    id: "box-model",
6444
    name: "Beware of broken box size",
6445
    desc: "Don't use width or height when using padding or border.",
6446
    browsers: "All",
6447
    init: function(parser, reporter){
6448
        var rule = this,
6449
            widthProperties = {
6450
                border: 1,
6451
                "border-left": 1,
6452
                "border-right": 1,
6453
                padding: 1,
6454
                "padding-left": 1,
6455
                "padding-right": 1
6456
            },
6457
            heightProperties = {
6458
                border: 1,
6459
                "border-bottom": 1,
6460
                "border-top": 1,
6461
                padding: 1,
6462
                "padding-bottom": 1,
6463
                "padding-top": 1
6464
            },
6465
            properties,
6466
            boxSizing = false;
6467
6468
        function startRule(){
6469
            properties = {};
6470
            boxSizing = false;
6471
        }
6472
6473
        function endRule(){
6474
            var prop, value;
6475
            
6476
            if (!boxSizing) {
6477
                if (properties.height){
6478
                    for (prop in heightProperties){
6479
                        if (heightProperties.hasOwnProperty(prop) && properties[prop]){
6480
                            value = properties[prop].value;
6481
                            if (!(prop == "padding" && value.parts.length === 2 && value.parts[0].value === 0)){
6482
                                reporter.report("Using height with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
6483
                            }
6484
                        }
6485
                    }
6486
                }
6487
6488
                if (properties.width){
6489
                    for (prop in widthProperties){
6490
                        if (widthProperties.hasOwnProperty(prop) && properties[prop]){
6491
                            value = properties[prop].value;
6492
                            
6493
                            if (!(prop == "padding" && value.parts.length === 2 && value.parts[1].value === 0)){
6494
                                reporter.report("Using width with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
6495
                            }
6496
                        }
6497
                    }
6498
                }   
6499
            }     
6500
        }
6501
6502
        parser.addListener("startrule", startRule);
6503
        parser.addListener("startfontface", startRule);
6504
        parser.addListener("startpage", startRule);
6505
        parser.addListener("startpagemargin", startRule);
6506
        parser.addListener("startkeyframerule", startRule); 
6507
6508
        parser.addListener("property", function(event){
6509
            var name = event.property.text.toLowerCase();
6510
            
6511
            if (heightProperties[name] || widthProperties[name]){
6512
                if (!/^0\S*$/.test(event.value) && !(name == "border" && event.value == "none")){
6513
                    properties[name] = { line: event.property.line, col: event.property.col, value: event.value };
6514
                }
6515
            } else {
6516
                if (/^(width|height)/i.test(name) && /^(length|percentage)/.test(event.value.parts[0].type)){
6517
                    properties[name] = 1;
6518
                } else if (name == "box-sizing") {
6519
                    boxSizing = true;
6520
                }
6521
            }
6522
            
6523
        });
6524
6525
        parser.addListener("endrule", endRule);
6526
        parser.addListener("endfontface", endRule);
6527
        parser.addListener("endpage", endRule);
6528
        parser.addListener("endpagemargin", endRule);
6529
        parser.addListener("endkeyframerule", endRule);         
6530
    }
6531
6532
});
6533
CSSLint.addRule({
6534
    id: "box-sizing",
6535
    name: "Disallow use of box-sizing",
6536
    desc: "The box-sizing properties isn't supported in IE6 and IE7.",
6537
    browsers: "IE6, IE7",
6538
    tags: ["Compatibility"],
6539
    init: function(parser, reporter){
6540
        var rule = this;
6541
6542
        parser.addListener("property", function(event){
6543
            var name = event.property.text.toLowerCase();
6544
   
6545
            if (name == "box-sizing"){
6546
                reporter.report("The box-sizing property isn't supported in IE6 and IE7.", event.line, event.col, rule);
6547
            }
6548
        });       
6549
    }
6550
6551
}); 
6552
CSSLint.addRule({
6553
    id: "compatible-vendor-prefixes",
6554
    name: "Require compatible vendor prefixes",
6555
    desc: "Include all compatible vendor prefixes to reach a wider range of users.",
6556
    browsers: "All",
6557
    init: function (parser, reporter) {
6558
        var rule = this,
6559
            compatiblePrefixes,
6560
            properties,
6561
            prop,
6562
            variations,
6563
            prefixed,
6564
            i,
6565
            len,
6566
            inKeyFrame = false,
6567
            arrayPush = Array.prototype.push,
6568
            applyTo = [];
6569
        compatiblePrefixes = {
6570
            "animation"                  : "webkit moz",
6571
            "animation-delay"            : "webkit moz",
6572
            "animation-direction"        : "webkit moz",
6573
            "animation-duration"         : "webkit moz",
6574
            "animation-fill-mode"        : "webkit moz",
6575
            "animation-iteration-count"  : "webkit moz",
6576
            "animation-name"             : "webkit moz",
6577
            "animation-play-state"       : "webkit moz",
6578
            "animation-timing-function"  : "webkit moz",
6579
            "appearance"                 : "webkit moz",
6580
            "border-end"                 : "webkit moz",
6581
            "border-end-color"           : "webkit moz",
6582
            "border-end-style"           : "webkit moz",
6583
            "border-end-width"           : "webkit moz",
6584
            "border-image"               : "webkit moz o",
6585
            "border-radius"              : "webkit moz",
6586
            "border-start"               : "webkit moz",
6587
            "border-start-color"         : "webkit moz",
6588
            "border-start-style"         : "webkit moz",
6589
            "border-start-width"         : "webkit moz",
6590
            "box-align"                  : "webkit moz ms",
6591
            "box-direction"              : "webkit moz ms",
6592
            "box-flex"                   : "webkit moz ms",
6593
            "box-lines"                  : "webkit ms",
6594
            "box-ordinal-group"          : "webkit moz ms",
6595
            "box-orient"                 : "webkit moz ms",
6596
            "box-pack"                   : "webkit moz ms",
6597
            "box-sizing"                 : "webkit moz",
6598
            "box-shadow"                 : "webkit moz",
6599
            "column-count"               : "webkit moz ms",
6600
            "column-gap"                 : "webkit moz ms",
6601
            "column-rule"                : "webkit moz ms",
6602
            "column-rule-color"          : "webkit moz ms",
6603
            "column-rule-style"          : "webkit moz ms",
6604
            "column-rule-width"          : "webkit moz ms",
6605
            "column-width"               : "webkit moz ms",
6606
            "hyphens"                    : "epub moz",
6607
            "line-break"                 : "webkit ms",
6608
            "margin-end"                 : "webkit moz",
6609
            "margin-start"               : "webkit moz",
6610
            "marquee-speed"              : "webkit wap",
6611
            "marquee-style"              : "webkit wap",
6612
            "padding-end"                : "webkit moz",
6613
            "padding-start"              : "webkit moz",
6614
            "tab-size"                   : "moz o",
6615
            "text-size-adjust"           : "webkit ms",
6616
            "transform"                  : "webkit moz ms o",
6617
            "transform-origin"           : "webkit moz ms o",
6618
            "transition"                 : "webkit moz o",
6619
            "transition-delay"           : "webkit moz o",
6620
            "transition-duration"        : "webkit moz o",
6621
            "transition-property"        : "webkit moz o",
6622
            "transition-timing-function" : "webkit moz o",
6623
            "user-modify"                : "webkit moz",
6624
            "user-select"                : "webkit moz ms",
6625
            "word-break"                 : "epub ms",
6626
            "writing-mode"               : "epub ms"
6627
        };
6628
6629
6630
        for (prop in compatiblePrefixes) {
6631
            if (compatiblePrefixes.hasOwnProperty(prop)) {
6632
                variations = [];
6633
                prefixed = compatiblePrefixes[prop].split(' ');
6634
                for (i = 0, len = prefixed.length; i < len; i++) {
6635
                    variations.push('-' + prefixed[i] + '-' + prop);
6636
                }
6637
                compatiblePrefixes[prop] = variations;
6638
                arrayPush.apply(applyTo, variations);
6639
            }
6640
        }
6641
                
6642
        parser.addListener("startrule", function () {
6643
            properties = [];
6644
        });
6645
6646
        parser.addListener("startkeyframes", function (event) {
6647
            inKeyFrame = event.prefix || true;
6648
        });
6649
6650
        parser.addListener("endkeyframes", function (event) {
6651
            inKeyFrame = false;
6652
        });
6653
6654
        parser.addListener("property", function (event) {
6655
            var name = event.property;
6656
            if (CSSLint.Util.indexOf(applyTo, name.text) > -1) {
6657
                if (!inKeyFrame || typeof inKeyFrame != "string" || 
6658
                        name.text.indexOf("-" + inKeyFrame + "-") !== 0) {
6659
                    properties.push(name);
6660
                }
6661
            }
6662
        });
6663
6664
        parser.addListener("endrule", function (event) {
6665
            if (!properties.length) {
6666
                return;
6667
            }
6668
6669
            var propertyGroups = {},
6670
                i,
6671
                len,
6672
                name,
6673
                prop,
6674
                variations,
6675
                value,
6676
                full,
6677
                actual,
6678
                item,
6679
                propertiesSpecified;
6680
6681
            for (i = 0, len = properties.length; i < len; i++) {
6682
                name = properties[i];
6683
6684
                for (prop in compatiblePrefixes) {
6685
                    if (compatiblePrefixes.hasOwnProperty(prop)) {
6686
                        variations = compatiblePrefixes[prop];
6687
                        if (CSSLint.Util.indexOf(variations, name.text) > -1) {
6688
                            if (!propertyGroups[prop]) {
6689
                                propertyGroups[prop] = {
6690
                                    full : variations.slice(0),
6691
                                    actual : [],
6692
                                    actualNodes: []
6693
                                };
6694
                            }
6695
                            if (CSSLint.Util.indexOf(propertyGroups[prop].actual, name.text) === -1) {
6696
                                propertyGroups[prop].actual.push(name.text);
6697
                                propertyGroups[prop].actualNodes.push(name);
6698
                            }
6699
                        }
6700
                    }
6701
                }
6702
            }
6703
6704
            for (prop in propertyGroups) {
6705
                if (propertyGroups.hasOwnProperty(prop)) {
6706
                    value = propertyGroups[prop];
6707
                    full = value.full;
6708
                    actual = value.actual;
6709
6710
                    if (full.length > actual.length) {
6711
                        for (i = 0, len = full.length; i < len; i++) {
6712
                            item = full[i];
6713
                            if (CSSLint.Util.indexOf(actual, item) === -1) {
6714
                                propertiesSpecified = (actual.length === 1) ? actual[0] : (actual.length == 2) ? actual.join(" and ") : actual.join(", ");
6715
                                reporter.report("The property " + item + " is compatible with " + propertiesSpecified + " and should be included as well.", value.actualNodes[0].line, value.actualNodes[0].col, rule); 
6716
                            }
6717
                        }
6718
6719
                    }
6720
                }
6721
            }
6722
        });
6723
    }
6724
});
6725
CSSLint.addRule({
6726
    id: "display-property-grouping",
6727
    name: "Require properties appropriate for display",
6728
    desc: "Certain properties shouldn't be used with certain display property values.",
6729
    browsers: "All",
6730
    init: function(parser, reporter){
6731
        var rule = this;
6732
6733
        var propertiesToCheck = {
6734
                display: 1,
6735
                "float": "none",
6736
                height: 1,
6737
                width: 1,
6738
                margin: 1,
6739
                "margin-left": 1,
6740
                "margin-right": 1,
6741
                "margin-bottom": 1,
6742
                "margin-top": 1,
6743
                padding: 1,
6744
                "padding-left": 1,
6745
                "padding-right": 1,
6746
                "padding-bottom": 1,
6747
                "padding-top": 1,
6748
                "vertical-align": 1
6749
            },
6750
            properties;
6751
6752
        function reportProperty(name, display, msg){
6753
            if (properties[name]){
6754
                if (typeof propertiesToCheck[name] != "string" || properties[name].value.toLowerCase() != propertiesToCheck[name]){
6755
                    reporter.report(msg || name + " can't be used with display: " + display + ".", properties[name].line, properties[name].col, rule);
6756
                }
6757
            }
6758
        }
6759
        
6760
        function startRule(){
6761
            properties = {};
6762
        }
6763
6764
        function endRule(){
6765
6766
            var display = properties.display ? properties.display.value : null;
6767
            if (display){
6768
                switch(display){
6769
6770
                    case "inline":
6771
                        reportProperty("height", display);
6772
                        reportProperty("width", display);
6773
                        reportProperty("margin", display);
6774
                        reportProperty("margin-top", display);
6775
                        reportProperty("margin-bottom", display);              
6776
                        reportProperty("float", display, "display:inline has no effect on floated elements (but may be used to fix the IE6 double-margin bug).");
6777
                        break;
6778
6779
                    case "block":
6780
                        reportProperty("vertical-align", display);
6781
                        break;
6782
6783
                    case "inline-block":
6784
                        reportProperty("float", display);
6785
                        break;
6786
6787
                    default:
6788
                        if (display.indexOf("table-") === 0){
6789
                            reportProperty("margin", display);
6790
                            reportProperty("margin-left", display);
6791
                            reportProperty("margin-right", display);
6792
                            reportProperty("margin-top", display);
6793
                            reportProperty("margin-bottom", display);
6794
                            reportProperty("float", display);
6795
                        }
6796
                }
6797
            }
6798
          
6799
        }
6800
6801
        parser.addListener("startrule", startRule);
6802
        parser.addListener("startfontface", startRule);
6803
        parser.addListener("startkeyframerule", startRule);
6804
        parser.addListener("startpagemargin", startRule);
6805
        parser.addListener("startpage", startRule);
6806
6807
        parser.addListener("property", function(event){
6808
            var name = event.property.text.toLowerCase();
6809
6810
            if (propertiesToCheck[name]){
6811
                properties[name] = { value: event.value.text, line: event.property.line, col: event.property.col };                    
6812
            }
6813
        });
6814
6815
        parser.addListener("endrule", endRule);
6816
        parser.addListener("endfontface", endRule);
6817
        parser.addListener("endkeyframerule", endRule);
6818
        parser.addListener("endpagemargin", endRule);
6819
        parser.addListener("endpage", endRule);
6820
6821
    }
6822
6823
});
6824
CSSLint.addRule({
6825
    id: "duplicate-background-images",
6826
    name: "Disallow duplicate background images",
6827
    desc: "Every background-image should be unique. Use a common class for e.g. sprites.",
6828
    browsers: "All",
6829
    init: function(parser, reporter){
6830
        var rule = this,
6831
            stack = {};
6832
6833
        parser.addListener("property", function(event){
6834
            var name = event.property.text,
6835
                value = event.value,
6836
                i, len;
6837
6838
            if (name.match(/background/i)) {
6839
                for (i=0, len=value.parts.length; i < len; i++) {
6840
                    if (value.parts[i].type == 'uri') {
6841
                        if (typeof stack[value.parts[i].uri] === 'undefined') {
6842
                            stack[value.parts[i].uri] = event;
6843
                        }
6844
                        else {
6845
                            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);
6846
                        }
6847
                    }
6848
                }
6849
            }
6850
        });
6851
    }
6852
});
6853
CSSLint.addRule({
6854
    id: "duplicate-properties",
6855
    name: "Disallow duplicate properties",
6856
    desc: "Duplicate properties must appear one after the other.",
6857
    browsers: "All",
6858
    init: function(parser, reporter){
6859
        var rule = this,
6860
            properties,
6861
            lastProperty;            
6862
            
6863
        function startRule(event){
6864
            properties = {};        
6865
        }
6866
        
6867
        parser.addListener("startrule", startRule);
6868
        parser.addListener("startfontface", startRule);
6869
        parser.addListener("startpage", startRule);
6870
        parser.addListener("startpagemargin", startRule);
6871
        parser.addListener("startkeyframerule", startRule);        
6872
        
6873
        parser.addListener("property", function(event){
6874
            var property = event.property,
6875
                name = property.text.toLowerCase();
6876
            
6877
            if (properties[name] && (lastProperty != name || properties[name] == event.value.text)){
6878
                reporter.report("Duplicate property '" + event.property + "' found.", event.line, event.col, rule);
6879
            }
6880
            
6881
            properties[name] = event.value.text;
6882
            lastProperty = name;
6883
                        
6884
        });
6885
            
6886
        
6887
    }
6888
6889
});
6890
CSSLint.addRule({
6891
    id: "empty-rules",
6892
    name: "Disallow empty rules",
6893
    desc: "Rules without any properties specified should be removed.",
6894
    browsers: "All",
6895
    init: function(parser, reporter){
6896
        var rule = this,
6897
            count = 0;
6898
6899
        parser.addListener("startrule", function(){
6900
            count=0;
6901
        });
6902
6903
        parser.addListener("property", function(){
6904
            count++;
6905
        });
6906
6907
        parser.addListener("endrule", function(event){
6908
            var selectors = event.selectors;
6909
            if (count === 0){
6910
                reporter.report("Rule is empty.", selectors[0].line, selectors[0].col, rule);
6911
            }
6912
        });
6913
    }
6914
6915
});
6916
CSSLint.addRule({
6917
    id: "errors",
6918
    name: "Parsing Errors",
6919
    desc: "This rule looks for recoverable syntax errors.",
6920
    browsers: "All",
6921
    init: function(parser, reporter){
6922
        var rule = this;
6923
6924
        parser.addListener("error", function(event){
6925
            reporter.error(event.message, event.line, event.col, rule);
6926
        });
6927
6928
    }
6929
6930
});
6931
CSSLint.addRule({
6932
    id: "fallback-colors",
6933
    name: "Require fallback colors",
6934
    desc: "For older browsers that don't support RGBA, HSL, or HSLA, provide a fallback color.",
6935
    browsers: "IE6,IE7,IE8",
6936
    init: function(parser, reporter){
6937
        var rule = this,
6938
            lastProperty,
6939
            propertiesToCheck = {
6940
                color: 1,
6941
                background: 1,
6942
                "background-color": 1                
6943
            },
6944
            properties;
6945
        
6946
        function startRule(event){
6947
            properties = {};    
6948
            lastProperty = null;    
6949
        }
6950
        
6951
        parser.addListener("startrule", startRule);
6952
        parser.addListener("startfontface", startRule);
6953
        parser.addListener("startpage", startRule);
6954
        parser.addListener("startpagemargin", startRule);
6955
        parser.addListener("startkeyframerule", startRule);        
6956
        
6957
        parser.addListener("property", function(event){
6958
            var property = event.property,
6959
                name = property.text.toLowerCase(),
6960
                parts = event.value.parts,
6961
                i = 0, 
6962
                colorType = "",
6963
                len = parts.length;                
6964
                        
6965
            if(propertiesToCheck[name]){
6966
                while(i < len){
6967
                    if (parts[i].type == "color"){
6968
                        if ("alpha" in parts[i] || "hue" in parts[i]){
6969
                            
6970
                            if (/([^\)]+)\(/.test(parts[i])){
6971
                                colorType = RegExp.$1.toUpperCase();
6972
                            }
6973
                            
6974
                            if (!lastProperty || (lastProperty.property.text.toLowerCase() != name || lastProperty.colorType != "compat")){
6975
                                reporter.report("Fallback " + name + " (hex or RGB) should precede " + colorType + " " + name + ".", event.line, event.col, rule);
6976
                            }
6977
                        } else {
6978
                            event.colorType = "compat";
6979
                        }
6980
                    }
6981
                    
6982
                    i++;
6983
                }
6984
            }
6985
6986
            lastProperty = event;
6987
        });        
6988
         
6989
    }
6990
6991
});
6992
CSSLint.addRule({
6993
    id: "floats",
6994
    name: "Disallow too many floats",
6995
    desc: "This rule tests if the float property is used too many times",
6996
    browsers: "All",
6997
    init: function(parser, reporter){
6998
        var rule = this;
6999
        var count = 0;
7000
        parser.addListener("property", function(event){
7001
            if (event.property.text.toLowerCase() == "float" &&
7002
                    event.value.text.toLowerCase() != "none"){
7003
                count++;
7004
            }
7005
        });
7006
        parser.addListener("endstylesheet", function(){
7007
            reporter.stat("floats", count);
7008
            if (count >= 10){
7009
                reporter.rollupWarn("Too many floats (" + count + "), you're probably using them for layout. Consider using a grid system instead.", rule);
7010
            }
7011
        });
7012
    }
7013
7014
});
7015
CSSLint.addRule({
7016
    id: "font-faces",
7017
    name: "Don't use too many web fonts",
7018
    desc: "Too many different web fonts in the same stylesheet.",
7019
    browsers: "All",
7020
    init: function(parser, reporter){
7021
        var rule = this,
7022
            count = 0;
7023
7024
7025
        parser.addListener("startfontface", function(){
7026
            count++;
7027
        });
7028
7029
        parser.addListener("endstylesheet", function(){
7030
            if (count > 5){
7031
                reporter.rollupWarn("Too many @font-face declarations (" + count + ").", rule);
7032
            }
7033
        });
7034
    }
7035
7036
});
7037
CSSLint.addRule({
7038
    id: "font-sizes",
7039
    name: "Disallow too many font sizes",
7040
    desc: "Checks the number of font-size declarations.",
7041
    browsers: "All",
7042
    init: function(parser, reporter){
7043
        var rule = this,
7044
            count = 0;
7045
        parser.addListener("property", function(event){
7046
            if (event.property == "font-size"){
7047
                count++;
7048
            }
7049
        });
7050
        parser.addListener("endstylesheet", function(){
7051
            reporter.stat("font-sizes", count);
7052
            if (count >= 10){
7053
                reporter.rollupWarn("Too many font-size declarations (" + count + "), abstraction needed.", rule);
7054
            }
7055
        });
7056
    }
7057
7058
});
7059
CSSLint.addRule({
7060
    id: "gradients",
7061
    name: "Require all gradient definitions",
7062
    desc: "When using a vendor-prefixed gradient, make sure to use them all.",
7063
    browsers: "All",
7064
    init: function(parser, reporter){
7065
        var rule = this,
7066
            gradients;
7067
7068
        parser.addListener("startrule", function(){
7069
            gradients = {
7070
                moz: 0,
7071
                webkit: 0,
7072
                oldWebkit: 0,
7073
                ms: 0,
7074
                o: 0
7075
            };
7076
        });
7077
7078
        parser.addListener("property", function(event){
7079
7080
            if (/\-(moz|ms|o|webkit)(?:\-(?:linear|radial))\-gradient/i.test(event.value)){
7081
                gradients[RegExp.$1] = 1;
7082
            } else if (/\-webkit\-gradient/i.test(event.value)){
7083
                gradients.oldWebkit = 1;
7084
            }
7085
7086
        });
7087
7088
        parser.addListener("endrule", function(event){
7089
            var missing = [];
7090
7091
            if (!gradients.moz){
7092
                missing.push("Firefox 3.6+");
7093
            }
7094
7095
            if (!gradients.webkit){
7096
                missing.push("Webkit (Safari 5+, Chrome)");
7097
            }
7098
            
7099
            if (!gradients.oldWebkit){
7100
                missing.push("Old Webkit (Safari 4+, Chrome)");
7101
            }
7102
7103
            if (!gradients.ms){
7104
                missing.push("Internet Explorer 10+");
7105
            }
7106
7107
            if (!gradients.o){
7108
                missing.push("Opera 11.1+");
7109
            }
7110
7111
            if (missing.length && missing.length < 5){            
7112
                reporter.report("Missing vendor-prefixed CSS gradients for " + missing.join(", ") + ".", event.selectors[0].line, event.selectors[0].col, rule); 
7113
            }
7114
7115
        });
7116
7117
    }
7118
7119
});
7120
CSSLint.addRule({
7121
    id: "ids",
7122
    name: "Disallow IDs in selectors",
7123
    desc: "Selectors should not contain IDs.",
7124
    browsers: "All",
7125
    init: function(parser, reporter){
7126
        var rule = this;
7127
        parser.addListener("startrule", function(event){
7128
            var selectors = event.selectors,
7129
                selector,
7130
                part,
7131
                modifier,
7132
                idCount,
7133
                i, j, k;
7134
7135
            for (i=0; i < selectors.length; i++){
7136
                selector = selectors[i];
7137
                idCount = 0;
7138
7139
                for (j=0; j < selector.parts.length; j++){
7140
                    part = selector.parts[j];
7141
                    if (part.type == parser.SELECTOR_PART_TYPE){
7142
                        for (k=0; k < part.modifiers.length; k++){
7143
                            modifier = part.modifiers[k];
7144
                            if (modifier.type == "id"){
7145
                                idCount++;
7146
                            }
7147
                        }
7148
                    }
7149
                }
7150
7151
                if (idCount == 1){
7152
                    reporter.report("Don't use IDs in selectors.", selector.line, selector.col, rule);
7153
                } else if (idCount > 1){
7154
                    reporter.report(idCount + " IDs in the selector, really?", selector.line, selector.col, rule);
7155
                }
7156
            }
7157
7158
        });
7159
    }
7160
7161
});
7162
CSSLint.addRule({
7163
    id: "import",
7164
    name: "Disallow @import",
7165
    desc: "Don't use @import, use <link> instead.",
7166
    browsers: "All",
7167
    init: function(parser, reporter){
7168
        var rule = this;
7169
        
7170
        parser.addListener("import", function(event){        
7171
            reporter.report("@import prevents parallel downloads, use <link> instead.", event.line, event.col, rule);
7172
        });
7173
7174
    }
7175
7176
});
7177
CSSLint.addRule({
7178
    id: "important",
7179
    name: "Disallow !important",
7180
    desc: "Be careful when using !important declaration",
7181
    browsers: "All",
7182
    init: function(parser, reporter){
7183
        var rule = this,
7184
            count = 0;
7185
        parser.addListener("property", function(event){
7186
            if (event.important === true){
7187
                count++;
7188
                reporter.report("Use of !important", event.line, event.col, rule);
7189
            }
7190
        });
7191
        parser.addListener("endstylesheet", function(){
7192
            reporter.stat("important", count);
7193
            if (count >= 10){
7194
                reporter.rollupWarn("Too many !important declarations (" + count + "), try to use less than 10 to avoid specificity issues.", rule);
7195
            }
7196
        });
7197
    }
7198
7199
});
7200
CSSLint.addRule({
7201
    id: "known-properties",
7202
    name: "Require use of known properties",
7203
    desc: "Properties should be known (listed in CSS3 specification) or be a vendor-prefixed property.",
7204
    browsers: "All",
7205
    init: function(parser, reporter){
7206
        var rule = this;
7207
7208
        parser.addListener("property", function(event){
7209
            var name = event.property.text.toLowerCase();
7210
            if (event.invalid) {
7211
                reporter.report(event.invalid.message, event.line, event.col, rule);
7212
            }
7213
7214
        });
7215
    }
7216
7217
});
7218
CSSLint.addRule({
7219
    id: "outline-none",
7220
    name: "Disallow outline: none",
7221
    desc: "Use of outline: none or outline: 0 should be limited to :focus rules.",
7222
    browsers: "All",
7223
    tags: ["Accessibility"],
7224
    init: function(parser, reporter){
7225
        var rule = this,
7226
            lastRule;
7227
7228
        function startRule(event){
7229
            if (event.selectors){
7230
                lastRule = {
7231
                    line: event.line,
7232
                    col: event.col,
7233
                    selectors: event.selectors,
7234
                    propCount: 0,
7235
                    outline: false
7236
                };
7237
            } else {
7238
                lastRule = null;
7239
            }
7240
        }
7241
        
7242
        function endRule(event){
7243
            if (lastRule){
7244
                if (lastRule.outline){
7245
                    if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") == -1){
7246
                        reporter.report("Outlines should only be modified using :focus.", lastRule.line, lastRule.col, rule);
7247
                    } else if (lastRule.propCount == 1) {
7248
                        reporter.report("Outlines shouldn't be hidden unless other visual changes are made.", lastRule.line, lastRule.col, rule);                        
7249
                    }
7250
                }
7251
            }
7252
        }
7253
7254
        parser.addListener("startrule", startRule);
7255
        parser.addListener("startfontface", startRule);
7256
        parser.addListener("startpage", startRule);
7257
        parser.addListener("startpagemargin", startRule);
7258
        parser.addListener("startkeyframerule", startRule); 
7259
7260
        parser.addListener("property", function(event){
7261
            var name = event.property.text.toLowerCase(),
7262
                value = event.value;                
7263
                
7264
            if (lastRule){
7265
                lastRule.propCount++;
7266
                if (name == "outline" && (value == "none" || value == "0")){
7267
                    lastRule.outline = true;
7268
                }            
7269
            }
7270
            
7271
        });
7272
        
7273
        parser.addListener("endrule", endRule);
7274
        parser.addListener("endfontface", endRule);
7275
        parser.addListener("endpage", endRule);
7276
        parser.addListener("endpagemargin", endRule);
7277
        parser.addListener("endkeyframerule", endRule); 
7278
7279
    }
7280
7281
});
7282
CSSLint.addRule({
7283
    id: "overqualified-elements",
7284
    name: "Disallow overqualified elements",
7285
    desc: "Don't use classes or IDs with elements (a.foo or a#foo).",
7286
    browsers: "All",
7287
    init: function(parser, reporter){
7288
        var rule = this,
7289
            classes = {};
7290
            
7291
        parser.addListener("startrule", function(event){
7292
            var selectors = event.selectors,
7293
                selector,
7294
                part,
7295
                modifier,
7296
                i, j, k;
7297
7298
            for (i=0; i < selectors.length; i++){
7299
                selector = selectors[i];
7300
7301
                for (j=0; j < selector.parts.length; j++){
7302
                    part = selector.parts[j];
7303
                    if (part.type == parser.SELECTOR_PART_TYPE){
7304
                        for (k=0; k < part.modifiers.length; k++){
7305
                            modifier = part.modifiers[k];
7306
                            if (part.elementName && modifier.type == "id"){
7307
                                reporter.report("Element (" + part + ") is overqualified, just use " + modifier + " without element name.", part.line, part.col, rule);
7308
                            } else if (modifier.type == "class"){
7309
                                
7310
                                if (!classes[modifier]){
7311
                                    classes[modifier] = [];
7312
                                }
7313
                                classes[modifier].push({ modifier: modifier, part: part });
7314
                            }
7315
                        }
7316
                    }
7317
                }
7318
            }
7319
        });
7320
        
7321
        parser.addListener("endstylesheet", function(){
7322
        
7323
            var prop;
7324
            for (prop in classes){
7325
                if (classes.hasOwnProperty(prop)){
7326
                    if (classes[prop].length == 1 && classes[prop][0].part.elementName){
7327
                        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);
7328
                    }
7329
                }
7330
            }        
7331
        });
7332
    }
7333
7334
});
7335
CSSLint.addRule({
7336
    id: "qualified-headings",
7337
    name: "Disallow qualified headings",
7338
    desc: "Headings should not be qualified (namespaced).",
7339
    browsers: "All",
7340
    init: function(parser, reporter){
7341
        var rule = this;
7342
7343
        parser.addListener("startrule", function(event){
7344
            var selectors = event.selectors,
7345
                selector,
7346
                part,
7347
                i, j;
7348
7349
            for (i=0; i < selectors.length; i++){
7350
                selector = selectors[i];
7351
7352
                for (j=0; j < selector.parts.length; j++){
7353
                    part = selector.parts[j];
7354
                    if (part.type == parser.SELECTOR_PART_TYPE){
7355
                        if (part.elementName && /h[1-6]/.test(part.elementName.toString()) && j > 0){
7356
                            reporter.report("Heading (" + part.elementName + ") should not be qualified.", part.line, part.col, rule);
7357
                        }
7358
                    }
7359
                }
7360
            }
7361
        });
7362
    }
7363
7364
});
7365
CSSLint.addRule({
7366
    id: "regex-selectors",
7367
    name: "Disallow selectors that look like regexs",
7368
    desc: "Selectors that look like regular expressions are slow and should be avoided.",
7369
    browsers: "All",
7370
    init: function(parser, reporter){
7371
        var rule = this;
7372
7373
        parser.addListener("startrule", function(event){
7374
            var selectors = event.selectors,
7375
                selector,
7376
                part,
7377
                modifier,
7378
                i, j, k;
7379
7380
            for (i=0; i < selectors.length; i++){
7381
                selector = selectors[i];
7382
                for (j=0; j < selector.parts.length; j++){
7383
                    part = selector.parts[j];
7384
                    if (part.type == parser.SELECTOR_PART_TYPE){
7385
                        for (k=0; k < part.modifiers.length; k++){
7386
                            modifier = part.modifiers[k];
7387
                            if (modifier.type == "attribute"){
7388
                                if (/([\~\|\^\$\*]=)/.test(modifier)){
7389
                                    reporter.report("Attribute selectors with " + RegExp.$1 + " are slow!", modifier.line, modifier.col, rule);
7390
                                }
7391
                            }
7392
7393
                        }
7394
                    }
7395
                }
7396
            }
7397
        });
7398
    }
7399
7400
});
7401
CSSLint.addRule({
7402
    id: "rules-count",
7403
    name: "Rules Count",
7404
    desc: "Track how many rules there are.",
7405
    browsers: "All",
7406
    init: function(parser, reporter){
7407
        var rule = this,
7408
            count = 0;
7409
        parser.addListener("startrule", function(){
7410
            count++;
7411
        });
7412
7413
        parser.addListener("endstylesheet", function(){
7414
            reporter.stat("rule-count", count);
7415
        });
7416
    }
7417
7418
});
7419
CSSLint.addRule({
7420
    id: "shorthand",
7421
    name: "Require shorthand properties",
7422
    desc: "Use shorthand properties where possible.",
7423
    browsers: "All",
7424
    init: function(parser, reporter){
7425
        var rule = this,
7426
            prop, i, len,
7427
            propertiesToCheck = {},
7428
            properties,
7429
            mapping = {
7430
                "margin": [
7431
                    "margin-top",
7432
                    "margin-bottom",
7433
                    "margin-left",
7434
                    "margin-right"
7435
                ],
7436
                "padding": [
7437
                    "padding-top",
7438
                    "padding-bottom",
7439
                    "padding-left",
7440
                    "padding-right"
7441
                ]              
7442
            };
7443
        for (prop in mapping){
7444
            if (mapping.hasOwnProperty(prop)){
7445
                for (i=0, len=mapping[prop].length; i < len; i++){
7446
                    propertiesToCheck[mapping[prop][i]] = prop;
7447
                }
7448
            }
7449
        }
7450
            
7451
        function startRule(event){
7452
            properties = {};
7453
        }
7454
        function endRule(event){
7455
            
7456
            var prop, i, len, total;
7457
            for (prop in mapping){
7458
                if (mapping.hasOwnProperty(prop)){
7459
                    total=0;
7460
                    
7461
                    for (i=0, len=mapping[prop].length; i < len; i++){
7462
                        total += properties[mapping[prop][i]] ? 1 : 0;
7463
                    }
7464
                    
7465
                    if (total == mapping[prop].length){
7466
                        reporter.report("The properties " + mapping[prop].join(", ") + " can be replaced by " + prop + ".", event.line, event.col, rule);
7467
                    }
7468
                }
7469
            }
7470
        }        
7471
        
7472
        parser.addListener("startrule", startRule);
7473
        parser.addListener("startfontface", startRule);
7474
        parser.addListener("property", function(event){
7475
            var name = event.property.toString().toLowerCase(),
7476
                value = event.value.parts[0].value;
7477
7478
            if (propertiesToCheck[name]){
7479
                properties[name] = 1;
7480
            }
7481
        });
7482
7483
        parser.addListener("endrule", endRule);
7484
        parser.addListener("endfontface", endRule);     
7485
7486
    }
7487
7488
});
7489
CSSLint.addRule({
7490
    id: "star-property-hack",
7491
    name: "Disallow properties with a star prefix",
7492
    desc: "Checks for the star property hack (targets IE6/7)",
7493
    browsers: "All",
7494
    init: function(parser, reporter){
7495
        var rule = this;
7496
        parser.addListener("property", function(event){
7497
            var property = event.property;
7498
7499
            if (property.hack == "*") {
7500
                reporter.report("Property with star prefix found.", event.property.line, event.property.col, rule);
7501
            }
7502
        });
7503
    }
7504
});
7505
CSSLint.addRule({
7506
    id: "text-indent",
7507
    name: "Disallow negative text-indent",
7508
    desc: "Checks for text indent less than -99px",
7509
    browsers: "All",
7510
    init: function(parser, reporter){
7511
        var rule = this,
7512
            textIndent,
7513
            direction;
7514
7515
7516
        function startRule(event){
7517
            textIndent = false;
7518
            direction = "inherit";
7519
        }
7520
        function endRule(event){
7521
            if (textIndent && direction != "ltr"){
7522
                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);
7523
            }
7524
        }
7525
7526
        parser.addListener("startrule", startRule);
7527
        parser.addListener("startfontface", startRule);
7528
        parser.addListener("property", function(event){
7529
            var name = event.property.toString().toLowerCase(),
7530
                value = event.value;
7531
7532
            if (name == "text-indent" && value.parts[0].value < -99){
7533
                textIndent = event.property;
7534
            } else if (name == "direction" && value == "ltr"){
7535
                direction = "ltr";
7536
            }
7537
        });
7538
7539
        parser.addListener("endrule", endRule);
7540
        parser.addListener("endfontface", endRule);
7541
7542
    }
7543
7544
});
7545
CSSLint.addRule({
7546
    id: "underscore-property-hack",
7547
    name: "Disallow properties with an underscore prefix",
7548
    desc: "Checks for the underscore property hack (targets IE6)",
7549
    browsers: "All",
7550
    init: function(parser, reporter){
7551
        var rule = this;
7552
        parser.addListener("property", function(event){
7553
            var property = event.property;
7554
7555
            if (property.hack == "_") {
7556
                reporter.report("Property with underscore prefix found.", event.property.line, event.property.col, rule);
7557
            }
7558
        });
7559
    }
7560
});
7561
CSSLint.addRule({
7562
    id: "unique-headings",
7563
    name: "Headings should only be defined once",
7564
    desc: "Headings should be defined only once.",
7565
    browsers: "All",
7566
    init: function(parser, reporter){
7567
        var rule = this;
7568
7569
        var headings =  {
7570
                h1: 0,
7571
                h2: 0,
7572
                h3: 0,
7573
                h4: 0,
7574
                h5: 0,
7575
                h6: 0
7576
            };
7577
7578
        parser.addListener("startrule", function(event){
7579
            var selectors = event.selectors,
7580
                selector,
7581
                part,
7582
                pseudo,
7583
                i, j;
7584
7585
            for (i=0; i < selectors.length; i++){
7586
                selector = selectors[i];
7587
                part = selector.parts[selector.parts.length-1];
7588
7589
                if (part.elementName && /(h[1-6])/i.test(part.elementName.toString())){
7590
                    
7591
                    for (j=0; j < part.modifiers.length; j++){
7592
                        if (part.modifiers[j].type == "pseudo"){
7593
                            pseudo = true;
7594
                            break;
7595
                        }
7596
                    }
7597
                
7598
                    if (!pseudo){
7599
                        headings[RegExp.$1]++;
7600
                        if (headings[RegExp.$1] > 1) {
7601
                            reporter.report("Heading (" + part.elementName + ") has already been defined.", part.line, part.col, rule);
7602
                        }
7603
                    }
7604
                }
7605
            }
7606
        });
7607
        
7608
        parser.addListener("endstylesheet", function(event){
7609
            var prop,
7610
                messages = [];
7611
                
7612
            for (prop in headings){
7613
                if (headings.hasOwnProperty(prop)){
7614
                    if (headings[prop] > 1){
7615
                        messages.push(headings[prop] + " " + prop + "s");
7616
                    }
7617
                }
7618
            }
7619
            
7620
            if (messages.length){
7621
                reporter.rollupWarn("You have " + messages.join(", ") + " defined in this stylesheet.", rule);
7622
            }
7623
        });        
7624
    }
7625
7626
});
7627
CSSLint.addRule({
7628
    id: "universal-selector",
7629
    name: "Disallow universal selector",
7630
    desc: "The universal selector (*) is known to be slow.",
7631
    browsers: "All",
7632
    init: function(parser, reporter){
7633
        var rule = this;
7634
7635
        parser.addListener("startrule", function(event){
7636
            var selectors = event.selectors,
7637
                selector,
7638
                part,
7639
                modifier,
7640
                i, j, k;
7641
7642
            for (i=0; i < selectors.length; i++){
7643
                selector = selectors[i];
7644
                
7645
                part = selector.parts[selector.parts.length-1];
7646
                if (part.elementName == "*"){
7647
                    reporter.report(rule.desc, part.line, part.col, rule);
7648
                }
7649
            }
7650
        });
7651
    }
7652
7653
});
7654
CSSLint.addRule({
7655
    id: "unqualified-attributes",
7656
    name: "Disallow unqualified attribute selectors",
7657
    desc: "Unqualified attribute selectors are known to be slow.",
7658
    browsers: "All",
7659
    init: function(parser, reporter){
7660
        var rule = this;
7661
7662
        parser.addListener("startrule", function(event){
7663
            
7664
            var selectors = event.selectors,
7665
                selector,
7666
                part,
7667
                modifier,
7668
                i, j, k;
7669
7670
            for (i=0; i < selectors.length; i++){
7671
                selector = selectors[i];
7672
                
7673
                part = selector.parts[selector.parts.length-1];
7674
                if (part.type == parser.SELECTOR_PART_TYPE){
7675
                    for (k=0; k < part.modifiers.length; k++){
7676
                        modifier = part.modifiers[k];
7677
                        if (modifier.type == "attribute" && (!part.elementName || part.elementName == "*")){
7678
                            reporter.report(rule.desc, part.line, part.col, rule);                               
7679
                        }
7680
                    }
7681
                }
7682
                
7683
            }            
7684
        });
7685
    }
7686
7687
});
7688
CSSLint.addRule({
7689
    id: "vendor-prefix",
7690
    name: "Require standard property with vendor prefix",
7691
    desc: "When using a vendor-prefixed property, make sure to include the standard one.",
7692
    browsers: "All",
7693
    init: function(parser, reporter){
7694
        var rule = this,
7695
            properties,
7696
            num,
7697
            propertiesToCheck = {
7698
                "-webkit-border-radius": "border-radius",
7699
                "-webkit-border-top-left-radius": "border-top-left-radius",
7700
                "-webkit-border-top-right-radius": "border-top-right-radius",
7701
                "-webkit-border-bottom-left-radius": "border-bottom-left-radius",
7702
                "-webkit-border-bottom-right-radius": "border-bottom-right-radius",
7703
                
7704
                "-o-border-radius": "border-radius",
7705
                "-o-border-top-left-radius": "border-top-left-radius",
7706
                "-o-border-top-right-radius": "border-top-right-radius",
7707
                "-o-border-bottom-left-radius": "border-bottom-left-radius",
7708
                "-o-border-bottom-right-radius": "border-bottom-right-radius",
7709
                
7710
                "-moz-border-radius": "border-radius",
7711
                "-moz-border-radius-topleft": "border-top-left-radius",
7712
                "-moz-border-radius-topright": "border-top-right-radius",
7713
                "-moz-border-radius-bottomleft": "border-bottom-left-radius",
7714
                "-moz-border-radius-bottomright": "border-bottom-right-radius",                
7715
                
7716
                "-moz-column-count": "column-count",
7717
                "-webkit-column-count": "column-count",
7718
                
7719
                "-moz-column-gap": "column-gap",
7720
                "-webkit-column-gap": "column-gap",
7721
                
7722
                "-moz-column-rule": "column-rule",
7723
                "-webkit-column-rule": "column-rule",
7724
                
7725
                "-moz-column-rule-style": "column-rule-style",
7726
                "-webkit-column-rule-style": "column-rule-style",
7727
                
7728
                "-moz-column-rule-color": "column-rule-color",
7729
                "-webkit-column-rule-color": "column-rule-color",
7730
                
7731
                "-moz-column-rule-width": "column-rule-width",
7732
                "-webkit-column-rule-width": "column-rule-width",
7733
                
7734
                "-moz-column-width": "column-width",
7735
                "-webkit-column-width": "column-width",
7736
                
7737
                "-webkit-column-span": "column-span",
7738
                "-webkit-columns": "columns",
7739
                
7740
                "-moz-box-shadow": "box-shadow",
7741
                "-webkit-box-shadow": "box-shadow",
7742
                
7743
                "-moz-transform" : "transform",
7744
                "-webkit-transform" : "transform",
7745
                "-o-transform" : "transform",
7746
                "-ms-transform" : "transform",
7747
                
7748
                "-moz-transform-origin" : "transform-origin",
7749
                "-webkit-transform-origin" : "transform-origin",
7750
                "-o-transform-origin" : "transform-origin",
7751
                "-ms-transform-origin" : "transform-origin",
7752
                
7753
                "-moz-box-sizing" : "box-sizing",
7754
                "-webkit-box-sizing" : "box-sizing",
7755
                
7756
                "-moz-user-select" : "user-select",
7757
                "-khtml-user-select" : "user-select",
7758
                "-webkit-user-select" : "user-select"                
7759
            };
7760
        function startRule(){
7761
            properties = {};
7762
            num=1;        
7763
        }
7764
        function endRule(event){
7765
            var prop,
7766
                i, len,
7767
                standard,
7768
                needed,
7769
                actual,
7770
                needsStandard = [];
7771
7772
            for (prop in properties){
7773
                if (propertiesToCheck[prop]){
7774
                    needsStandard.push({ actual: prop, needed: propertiesToCheck[prop]});
7775
                }
7776
            }
7777
7778
            for (i=0, len=needsStandard.length; i < len; i++){
7779
                needed = needsStandard[i].needed;
7780
                actual = needsStandard[i].actual;
7781
7782
                if (!properties[needed]){               
7783
                    reporter.report("Missing standard property '" + needed + "' to go along with '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
7784
                } else {
7785
                    if (properties[needed][0].pos < properties[actual][0].pos){
7786
                        reporter.report("Standard property '" + needed + "' should come after vendor-prefixed property '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
7787
                    }
7788
                }
7789
            }
7790
7791
        }        
7792
        
7793
        parser.addListener("startrule", startRule);
7794
        parser.addListener("startfontface", startRule);
7795
        parser.addListener("startpage", startRule);
7796
        parser.addListener("startpagemargin", startRule);
7797
        parser.addListener("startkeyframerule", startRule);         
7798
7799
        parser.addListener("property", function(event){
7800
            var name = event.property.text.toLowerCase();
7801
7802
            if (!properties[name]){
7803
                properties[name] = [];
7804
            }
7805
7806
            properties[name].push({ name: event.property, value : event.value, pos:num++ });
7807
        });
7808
7809
        parser.addListener("endrule", endRule);
7810
        parser.addListener("endfontface", endRule);
7811
        parser.addListener("endpage", endRule);
7812
        parser.addListener("endpagemargin", endRule);
7813
        parser.addListener("endkeyframerule", endRule);         
7814
    }
7815
7816
});
7817
CSSLint.addRule({
7818
    id: "zero-units",
7819
    name: "Disallow units for 0 values",
7820
    desc: "You don't need to specify units when a value is 0.",
7821
    browsers: "All",
7822
    init: function(parser, reporter){
7823
        var rule = this;
7824
        parser.addListener("property", function(event){
7825
            var parts = event.value.parts,
7826
                i = 0, 
7827
                len = parts.length;
7828
7829
            while(i < len){
7830
                if ((parts[i].units || parts[i].type == "percentage") && parts[i].value === 0 && parts[i].type != "time"){
7831
                    reporter.report("Values of 0 shouldn't have units specified.", parts[i].line, parts[i].col, rule);
7832
                }
7833
                i++;
7834
            }
7835
7836
        });
7837
7838
    }
7839
7840
});
7841
7842
7843
exports.CSSLint = CSSLint;
7844
7845
7846
});