/home/arranoyd/magicraft/wp-content/plugins/popover/js/worker-css.js
/*! PopUp Pro - v4.7.11
 * http://premium.wpmudev.org/project/the-pop-over-plugin/
 * Copyright (c) 2015; * Licensed GPLv2+ */
"no use strict";;
(function(window) {
    if (typeof window.window != "undefined" && window.document) {
        return;
    }

    window.console = function() {
        var msgs = Array.prototype.slice.call(arguments, 0);
        postMessage({
            type: "log",
            data: msgs
        });
    };
    window.console.error =
        window.console.warn =
        window.console.log =
        window.console.trace = window.console;

    window.window = window;
    window.ace = window;

    window.onerror = function(message, file, line, col, err) {
        postMessage({
            type: "error",
            data: {
                message: message,
                file: file,
                line: line,
                col: col,
                stack: err.stack
            }
        });
    };

    window.normalizeModule = function(parentId, moduleName) {
        // normalize plugin requires
        if (moduleName.indexOf("!") !== -1) {
            var chunks = moduleName.split("!");
            return window.normalizeModule(parentId, chunks[0]) + "!" + window.normalizeModule(parentId, chunks[1]);
        }
        // normalize relative requires
        if (moduleName.charAt(0) == ".") {
            var base = parentId.split("/").slice(0, -1).join("/");
            moduleName = (base ? base + "/" : "") + moduleName;

            while (moduleName.indexOf(".") !== -1 && previous != moduleName) {
                var previous = moduleName;
                moduleName = moduleName.replace(/^\.\//, "").replace(/\/\.\//, "/").replace(/[^\/]+\/\.\.\//, "");
            }
        }

        return moduleName;
    };

    window.require = function(parentId, id) {
        if (!id) {
            id = parentId;
            parentId = null;
        }
        if (!id.charAt)
            throw new Error("worker.js require() accepts only (parentId, id) as arguments");

        id = window.normalizeModule(parentId, id);

        var module = window.require.modules[id];
        if (module) {
            if (!module.initialized) {
                module.initialized = true;
                module.exports = module.factory().exports;
            }
            return module.exports;
        }

        var chunks = id.split("/");
        if (!window.require.tlns)
            return console.log("unable to load " + id);
        chunks[0] = window.require.tlns[chunks[0]] || chunks[0];
        var path = chunks.join("/") + ".js";

        window.require.id = id;
        importScripts(path);
        return window.require(parentId, id);
    };
    window.require.modules = {};
    window.require.tlns = {};

    window.define = function(id, deps, factory) {
        if (arguments.length == 2) {
            factory = deps;
            if (typeof id != "string") {
                deps = id;
                id = window.require.id;
            }
        } else if (arguments.length == 1) {
            factory = id;
            deps = [];
            id = window.require.id;
        }

        if (typeof factory != "function") {
            window.require.modules[id] = {
                exports: factory,
                initialized: true
            };
            return;
        }

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

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

        window.require.modules[id] = {
            exports: {},
            factory: function() {
                var module = this;
                var returnExports = factory.apply(this, deps.map(function(dep) {
                    switch (dep) {
                        // Because 'require', 'exports' and 'module' aren't actual
                        // dependencies, we must handle them seperately.
                        case 'require':
                            return req;
                        case 'exports':
                            return module.exports;
                        case 'module':
                            return module;
                            // But for all other dependencies, we can just go ahead and
                            // require them.
                        default:
                            return req(dep);
                    }
                }));
                if (returnExports)
                    module.exports = returnExports;
                return module;
            }
        };
    };
    window.define.amd = {};

    window.initBaseUrls = function initBaseUrls(topLevelNamespaces) {
        require.tlns = topLevelNamespaces;
    };

    window.initSender = function initSender() {

        var EventEmitter = window.require("ace/lib/event_emitter").EventEmitter;
        var oop = window.require("ace/lib/oop");

        var Sender = function() {};

        (function() {

            oop.implement(this, EventEmitter);

            this.callback = function(data, callbackId) {
                postMessage({
                    type: "call",
                    id: callbackId,
                    data: data
                });
            };

            this.emit = function(name, data) {
                postMessage({
                    type: "event",
                    name: name,
                    data: data
                });
            };

        }).call(Sender.prototype);

        return new Sender();
    };

    var main = window.main = null;
    var sender = window.sender = null;

    window.onmessage = function(e) {
        var msg = e.data;
        if (msg.command) {
            if (main[msg.command])
                main[msg.command].apply(main, msg.args);
            else
                throw new Error("Unknown command:" + msg.command);
        } else if (msg.init) {
            initBaseUrls(msg.tlns);
            require("ace/lib/es5-shim");
            sender = window.sender = initSender();
            var clazz = require(msg.module)[msg.classname];
            main = window.main = new clazz(sender);
        } else if (msg.event && sender) {
            sender._signal(msg.event, msg.data);
        }
    };
})(this);

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

    exports.inherits = function(ctor, superCtor) {
        ctor.super_ = superCtor;
        ctor.prototype = Object.create(superCtor.prototype, {
            constructor: {
                value: ctor,
                enumerable: false,
                writable: true,
                configurable: true
            }
        });
    };

    exports.mixin = function(obj, mixin) {
        for (var key in mixin) {
            obj[key] = mixin[key];
        }
        return obj;
    };

    exports.implement = function(proto, mixin) {
        exports.mixin(proto, mixin);
    };

});

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

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

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

    exports.stringRepeat = function(string, count) {
        var result = '';
        while (count > 0) {
            if (count & 1)
                result += string;

            if (count >>= 1)
                string += string;
        }
        return result;
    };

    var trimBeginRegexp = /^\s\s*/;
    var trimEndRegexp = /\s\s*$/;

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

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

    exports.copyObject = function(obj) {
        var copy = {};
        for (var key in obj) {
            copy[key] = obj[key];
        }
        return copy;
    };

    exports.copyArray = function(array) {
        var copy = [];
        for (var i = 0, l = array.length; i < l; i++) {
            if (array[i] && typeof array[i] == "object")
                copy[i] = this.copyObject(array[i]);
            else
                copy[i] = array[i];
        }
        return copy;
    };

    exports.deepCopy = function(obj) {
        if (typeof obj !== "object" || !obj)
            return obj;
        var cons = obj.constructor;
        if (cons === RegExp)
            return obj;

        var copy = cons();
        for (var key in obj) {
            if (typeof obj[key] === "object") {
                copy[key] = exports.deepCopy(obj[key]);
            } else {
                copy[key] = obj[key];
            }
        }
        return copy;
    };

    exports.arrayToMap = function(arr) {
        var map = {};
        for (var i = 0; i < arr.length; i++) {
            map[arr[i]] = 1;
        }
        return map;

    };

    exports.createMap = function(props) {
        var map = Object.create(null);
        for (var i in props) {
            map[i] = props[i];
        }
        return map;
    };
    exports.arrayRemove = function(array, value) {
        for (var i = 0; i <= array.length; i++) {
            if (value === array[i]) {
                array.splice(i, 1);
            }
        }
    };

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

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

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

        string.replace(regExp, function(str) {
            matches.push({
                offset: arguments[arguments.length - 2],
                length: str.length
            });
        });

        return matches;
    };
    exports.deferredCall = function(fcn) {
        var timer = null;
        var callback = function() {
            timer = null;
            fcn();
        };

        var deferred = function(timeout) {
            deferred.cancel();
            timer = setTimeout(callback, timeout || 0);
            return deferred;
        };

        deferred.schedule = deferred;

        deferred.call = function() {
            this.cancel();
            fcn();
            return deferred;
        };

        deferred.cancel = function() {
            clearTimeout(timer);
            timer = null;
            return deferred;
        };

        deferred.isPending = function() {
            return timer;
        };

        return deferred;
    };


    exports.delayedCall = function(fcn, defaultTimeout) {
        var timer = null;
        var callback = function() {
            timer = null;
            fcn();
        };

        var _self = function(timeout) {
            if (timer == null)
                timer = setTimeout(callback, timeout || defaultTimeout);
        };

        _self.delay = function(timeout) {
            timer && clearTimeout(timer);
            timer = setTimeout(callback, timeout || defaultTimeout);
        };
        _self.schedule = _self;

        _self.call = function() {
            this.cancel();
            fcn();
        };

        _self.cancel = function() {
            timer && clearTimeout(timer);
            timer = null;
        };

        _self.isPending = function() {
            return timer;
        };

        return _self;
    };
});

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

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

    EventEmitter._emit =
        EventEmitter._dispatchEvent = function(eventName, e) {
            this._eventRegistry || (this._eventRegistry = {});
            this._defaultHandlers || (this._defaultHandlers = {});

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

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

            if (!e.type)
                e.type = eventName;
            if (!e.stopPropagation)
                e.stopPropagation = stopPropagation;
            if (!e.preventDefault)
                e.preventDefault = preventDefault;

            listeners = listeners.slice();
            for (var i = 0; i < listeners.length; i++) {
                listeners[i](e, this);
                if (e.propagationStopped)
                    break;
            }

            if (defaultHandler && !e.defaultPrevented)
                return defaultHandler(e, this);
    };


    EventEmitter._signal = function(eventName, e) {
        var listeners = (this._eventRegistry || {})[eventName];
        if (!listeners)
            return;
        listeners = listeners.slice();
        for (var i = 0; i < listeners.length; i++)
            listeners[i](e, this);
    };

    EventEmitter.once = function(eventName, callback) {
        var _self = this;
        callback && this.addEventListener(eventName, function newCallback() {
            _self.removeEventListener(eventName, newCallback);
            callback.apply(null, arguments);
        });
    };


    EventEmitter.setDefaultHandler = function(eventName, callback) {
        var handlers = this._defaultHandlers
        if (!handlers)
            handlers = this._defaultHandlers = {
                _disabled_: {}
            };

        if (handlers[eventName]) {
            var old = handlers[eventName];
            var disabled = handlers._disabled_[eventName];
            if (!disabled)
                handlers._disabled_[eventName] = disabled = [];
            disabled.push(old);
            var i = disabled.indexOf(callback);
            if (i != -1)
                disabled.splice(i, 1);
        }
        handlers[eventName] = callback;
    };
    EventEmitter.removeDefaultHandler = function(eventName, callback) {
        var handlers = this._defaultHandlers
        if (!handlers)
            return;
        var disabled = handlers._disabled_[eventName];

        if (handlers[eventName] == callback) {
            var old = handlers[eventName];
            if (disabled)
                this.setDefaultHandler(eventName, disabled.pop());
        } else if (disabled) {
            var i = disabled.indexOf(callback);
            if (i != -1)
                disabled.splice(i, 1);
        }
    };

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

            var listeners = this._eventRegistry[eventName];
            if (!listeners)
                listeners = this._eventRegistry[eventName] = [];

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

    EventEmitter.off =
        EventEmitter.removeListener =
        EventEmitter.removeEventListener = function(eventName, callback) {
            this._eventRegistry = this._eventRegistry || {};

            var listeners = this._eventRegistry[eventName];
            if (!listeners)
                return;

            var index = listeners.indexOf(callback);
            if (index !== -1)
                listeners.splice(index, 1);
    };

    EventEmitter.removeAllListeners = function(eventName) {
        if (this._eventRegistry) this._eventRegistry[eventName] = [];
    };

    exports.EventEmitter = EventEmitter;

});

ace.define("ace/range", ["require", "exports", "module"], function(require, exports, module) {
    "use strict";
    var comparePoints = function(p1, p2) {
        return p1.row - p2.row || p1.column - p2.column;
    };
    var Range = function(startRow, startColumn, endRow, endColumn) {
        this.start = {
            row: startRow,
            column: startColumn
        };

        this.end = {
            row: endRow,
            column: endColumn
        };
    };

    (function() {
        this.isEqual = function(range) {
            return this.start.row === range.start.row &&
                this.end.row === range.end.row &&
                this.start.column === range.start.column &&
                this.end.column === range.end.column;
        };
        this.toString = function() {
            return ("Range: [" + this.start.row + "/" + this.start.column +
                "] -> [" + this.end.row + "/" + this.end.column + "]");
        };

        this.contains = function(row, column) {
            return this.compare(row, column) == 0;
        };
        this.compareRange = function(range) {
            var cmp,
                end = range.end,
                start = range.start;

            cmp = this.compare(end.row, end.column);
            if (cmp == 1) {
                cmp = this.compare(start.row, start.column);
                if (cmp == 1) {
                    return 2;
                } else if (cmp == 0) {
                    return 1;
                } else {
                    return 0;
                }
            } else if (cmp == -1) {
                return -2;
            } else {
                cmp = this.compare(start.row, start.column);
                if (cmp == -1) {
                    return -1;
                } else if (cmp == 1) {
                    return 42;
                } else {
                    return 0;
                }
            }
        };
        this.comparePoint = function(p) {
            return this.compare(p.row, p.column);
        };
        this.containsRange = function(range) {
            return this.comparePoint(range.start) == 0 && this.comparePoint(range.end) == 0;
        };
        this.intersects = function(range) {
            var cmp = this.compareRange(range);
            return (cmp == -1 || cmp == 0 || cmp == 1);
        };
        this.isEnd = function(row, column) {
            return this.end.row == row && this.end.column == column;
        };
        this.isStart = function(row, column) {
            return this.start.row == row && this.start.column == column;
        };
        this.setStart = function(row, column) {
            if (typeof row == "object") {
                this.start.column = row.column;
                this.start.row = row.row;
            } else {
                this.start.row = row;
                this.start.column = column;
            }
        };
        this.setEnd = function(row, column) {
            if (typeof row == "object") {
                this.end.column = row.column;
                this.end.row = row.row;
            } else {
                this.end.row = row;
                this.end.column = column;
            }
        };
        this.inside = function(row, column) {
            if (this.compare(row, column) == 0) {
                if (this.isEnd(row, column) || this.isStart(row, column)) {
                    return false;
                } else {
                    return true;
                }
            }
            return false;
        };
        this.insideStart = function(row, column) {
            if (this.compare(row, column) == 0) {
                if (this.isEnd(row, column)) {
                    return false;
                } else {
                    return true;
                }
            }
            return false;
        };
        this.insideEnd = function(row, column) {
            if (this.compare(row, column) == 0) {
                if (this.isStart(row, column)) {
                    return false;
                } else {
                    return true;
                }
            }
            return false;
        };
        this.compare = function(row, column) {
            if (!this.isMultiLine()) {
                if (row === this.start.row) {
                    return column < this.start.column ? -1 : (column > this.end.column ? 1 : 0);
                };
            }

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

            if (row > this.end.row)
                return 1;

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

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

            return 0;
        };
        this.compareStart = function(row, column) {
            if (this.start.row == row && this.start.column == column) {
                return -1;
            } else {
                return this.compare(row, column);
            }
        };
        this.compareEnd = function(row, column) {
            if (this.end.row == row && this.end.column == column) {
                return 1;
            } else {
                return this.compare(row, column);
            }
        };
        this.compareInside = function(row, column) {
            if (this.end.row == row && this.end.column == column) {
                return 1;
            } else if (this.start.row == row && this.start.column == column) {
                return -1;
            } else {
                return this.compare(row, column);
            }
        };
        this.clipRows = function(firstRow, lastRow) {
            if (this.end.row > lastRow)
                var end = {
                    row: lastRow + 1,
                    column: 0
                };
            else if (this.end.row < firstRow)
                var end = {
                    row: firstRow,
                    column: 0
                };

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

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

            if (cmp == 0)
                return this;
            else if (cmp == -1)
                var start = {
                    row: row,
                    column: column
                };
            else
                var end = {
                    row: row,
                    column: column
                };

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

        this.isEmpty = function() {
            return (this.start.row === this.end.row && this.start.column === this.end.column);
        };
        this.isMultiLine = function() {
            return (this.start.row !== this.end.row);
        };
        this.clone = function() {
            return Range.fromPoints(this.start, this.end);
        };
        this.collapseRows = function() {
            if (this.end.column == 0)
                return new Range(this.start.row, 0, Math.max(this.start.row, this.end.row - 1), 0)
            else
                return new Range(this.start.row, 0, this.end.row, 0)
        };
        this.toScreenRange = function(session) {
            var screenPosStart = session.documentToScreenPosition(this.start);
            var screenPosEnd = session.documentToScreenPosition(this.end);

            return new Range(
                screenPosStart.row, screenPosStart.column,
                screenPosEnd.row, screenPosEnd.column
            );
        };
        this.moveBy = function(row, column) {
            this.start.row += row;
            this.start.column += column;
            this.end.row += row;
            this.end.column += column;
        };

    }).call(Range.prototype);
    Range.fromPoints = function(start, end) {
        return new Range(start.row, start.column, end.row, end.column);
    };
    Range.comparePoints = comparePoints;

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


    exports.Range = Range;
});

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

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

    var Anchor = exports.Anchor = function(doc, row, column) {
        this.$onChange = this.onChange.bind(this);
        this.attach(doc);

        if (typeof column == "undefined")
            this.setPosition(row.row, row.column);
        else
            this.setPosition(row, column);
    };

    (function() {

        oop.implement(this, EventEmitter);
        this.getPosition = function() {
            return this.$clipPositionToDocument(this.row, this.column);
        };
        this.getDocument = function() {
            return this.document;
        };
        this.$insertRight = false;
        this.onChange = function(e) {
            var delta = e.data;
            var range = delta.range;

            if (range.start.row == range.end.row && range.start.row != this.row)
                return;

            if (range.start.row > this.row)
                return;

            if (range.start.row == this.row && range.start.column > this.column)
                return;

            var row = this.row;
            var column = this.column;
            var start = range.start;
            var end = range.end;

            if (delta.action === "insertText") {
                if (start.row === row && start.column <= column) {
                    if (start.column === column && this.$insertRight) {} else if (start.row === end.row) {
                        column += end.column - start.column;
                    } else {
                        column -= start.column;
                        row += end.row - start.row;
                    }
                } else if (start.row !== end.row && start.row < row) {
                    row += end.row - start.row;
                }
            } else if (delta.action === "insertLines") {
                if (start.row === row && column === 0 && this.$insertRight) {} else if (start.row <= row) {
                    row += end.row - start.row;
                }
            } else if (delta.action === "removeText") {
                if (start.row === row && start.column < column) {
                    if (end.column >= column)
                        column = start.column;
                    else
                        column = Math.max(0, column - (end.column - start.column));

                } else if (start.row !== end.row && start.row < row) {
                    if (end.row === row)
                        column = Math.max(0, column - end.column) + start.column;
                    row -= (end.row - start.row);
                } else if (end.row === row) {
                    row -= end.row - start.row;
                    column = Math.max(0, column - end.column) + start.column;
                }
            } else if (delta.action == "removeLines") {
                if (start.row <= row) {
                    if (end.row <= row)
                        row -= end.row - start.row;
                    else {
                        row = start.row;
                        column = 0;
                    }
                }
            }

            this.setPosition(row, column, true);
        };
        this.setPosition = function(row, column, noClip) {
            var pos;
            if (noClip) {
                pos = {
                    row: row,
                    column: column
                };
            } else {
                pos = this.$clipPositionToDocument(row, column);
            }

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

            var old = {
                row: this.row,
                column: this.column
            };

            this.row = pos.row;
            this.column = pos.column;
            this._signal("change", {
                old: old,
                value: pos
            });
        };
        this.detach = function() {
            this.document.removeEventListener("change", this.$onChange);
        };
        this.attach = function(doc) {
            this.document = doc || this.document;
            this.document.on("change", this.$onChange);
        };
        this.$clipPositionToDocument = function(row, column) {
            var pos = {};

            if (row >= this.document.getLength()) {
                pos.row = Math.max(0, this.document.getLength() - 1);
                pos.column = this.document.getLine(pos.row).length;
            } else if (row < 0) {
                pos.row = 0;
                pos.column = 0;
            } else {
                pos.row = row;
                pos.column = Math.min(this.document.getLine(pos.row).length, Math.max(0, column));
            }

            if (column < 0)
                pos.column = 0;

            return pos;
        };

    }).call(Anchor.prototype);

});

ace.define("ace/document", ["require", "exports", "module", "ace/lib/oop", "ace/lib/event_emitter", "ace/range", "ace/anchor"], function(require, exports, module) {
    "use strict";

    var oop = require("./lib/oop");
    var EventEmitter = require("./lib/event_emitter").EventEmitter;
    var Range = require("./range").Range;
    var Anchor = require("./anchor").Anchor;

    var Document = function(text) {
        this.$lines = [];
        if (text.length === 0) {
            this.$lines = [""];
        } else if (Array.isArray(text)) {
            this._insertLines(0, text);
        } else {
            this.insert({
                row: 0,
                column: 0
            }, text);
        }
    };

    (function() {

        oop.implement(this, EventEmitter);
        this.setValue = function(text) {
            var len = this.getLength();
            this.remove(new Range(0, 0, len, this.getLine(len - 1).length));
            this.insert({
                row: 0,
                column: 0
            }, text);
        };
        this.getValue = function() {
            return this.getAllLines().join(this.getNewLineCharacter());
        };
        this.createAnchor = function(row, column) {
            return new Anchor(this, row, column);
        };
        if ("aaa".split(/a/).length === 0)
            this.$split = function(text) {
                return text.replace(/\r\n|\r/g, "\n").split("\n");
            };
        else
            this.$split = function(text) {
                return text.split(/\r\n|\r|\n/);
            };


        this.$detectNewLine = function(text) {
            var match = text.match(/^.*?(\r\n|\r|\n)/m);
            this.$autoNewLine = match ? match[1] : "\n";
            this._signal("changeNewLineMode");
        };
        this.getNewLineCharacter = function() {
            switch (this.$newLineMode) {
                case "windows":
                    return "\r\n";
                case "unix":
                    return "\n";
                default:
                    return this.$autoNewLine || "\n";
            }
        };

        this.$autoNewLine = "";
        this.$newLineMode = "auto";
        this.setNewLineMode = function(newLineMode) {
            if (this.$newLineMode === newLineMode)
                return;

            this.$newLineMode = newLineMode;
            this._signal("changeNewLineMode");
        };
        this.getNewLineMode = function() {
            return this.$newLineMode;
        };
        this.isNewLine = function(text) {
            return (text == "\r\n" || text == "\r" || text == "\n");
        };
        this.getLine = function(row) {
            return this.$lines[row] || "";
        };
        this.getLines = function(firstRow, lastRow) {
            return this.$lines.slice(firstRow, lastRow + 1);
        };
        this.getAllLines = function() {
            return this.getLines(0, this.getLength());
        };
        this.getLength = function() {
            return this.$lines.length;
        };
        this.getTextRange = function(range) {
            if (range.start.row == range.end.row) {
                return this.getLine(range.start.row)
                    .substring(range.start.column, range.end.column);
            }
            var lines = this.getLines(range.start.row, range.end.row);
            lines[0] = (lines[0] || "").substring(range.start.column);
            var l = lines.length - 1;
            if (range.end.row - range.start.row == l)
                lines[l] = lines[l].substring(0, range.end.column);
            return lines.join(this.getNewLineCharacter());
        };

        this.$clipPosition = function(position) {
            var length = this.getLength();
            if (position.row >= length) {
                position.row = Math.max(0, length - 1);
                position.column = this.getLine(length - 1).length;
            } else if (position.row < 0)
                position.row = 0;
            return position;
        };
        this.insert = function(position, text) {
            if (!text || text.length === 0)
                return position;

            position = this.$clipPosition(position);
            if (this.getLength() <= 1)
                this.$detectNewLine(text);

            var lines = this.$split(text);
            var firstLine = lines.splice(0, 1)[0];
            var lastLine = lines.length == 0 ? null : lines.splice(lines.length - 1, 1)[0];

            position = this.insertInLine(position, firstLine);
            if (lastLine !== null) {
                position = this.insertNewLine(position); // terminate first line
                position = this._insertLines(position.row, lines);
                position = this.insertInLine(position, lastLine || "");
            }
            return position;
        };
        this.insertLines = function(row, lines) {
            if (row >= this.getLength())
                return this.insert({
                    row: row,
                    column: 0
                }, "\n" + lines.join("\n"));
            return this._insertLines(Math.max(row, 0), lines);
        };
        this._insertLines = function(row, lines) {
            if (lines.length == 0)
                return {
                    row: row,
                    column: 0
                };
            while (lines.length > 0xF000) {
                var end = this._insertLines(row, lines.slice(0, 0xF000));
                lines = lines.slice(0xF000);
                row = end.row;
            }

            var args = [row, 0];
            args.push.apply(args, lines);
            this.$lines.splice.apply(this.$lines, args);

            var range = new Range(row, 0, row + lines.length, 0);
            var delta = {
                action: "insertLines",
                range: range,
                lines: lines
            };
            this._signal("change", {
                data: delta
            });
            return range.end;
        };
        this.insertNewLine = function(position) {
            position = this.$clipPosition(position);
            var line = this.$lines[position.row] || "";

            this.$lines[position.row] = line.substring(0, position.column);
            this.$lines.splice(position.row + 1, 0, line.substring(position.column, line.length));

            var end = {
                row: position.row + 1,
                column: 0
            };

            var delta = {
                action: "insertText",
                range: Range.fromPoints(position, end),
                text: this.getNewLineCharacter()
            };
            this._signal("change", {
                data: delta
            });

            return end;
        };
        this.insertInLine = function(position, text) {
            if (text.length == 0)
                return position;

            var line = this.$lines[position.row] || "";

            this.$lines[position.row] = line.substring(0, position.column) + text + line.substring(position.column);

            var end = {
                row: position.row,
                column: position.column + text.length
            };

            var delta = {
                action: "insertText",
                range: Range.fromPoints(position, end),
                text: text
            };
            this._signal("change", {
                data: delta
            });

            return end;
        };
        this.remove = function(range) {
            if (!(range instanceof Range))
                range = Range.fromPoints(range.start, range.end);
            range.start = this.$clipPosition(range.start);
            range.end = this.$clipPosition(range.end);

            if (range.isEmpty())
                return range.start;

            var firstRow = range.start.row;
            var lastRow = range.end.row;

            if (range.isMultiLine()) {
                var firstFullRow = range.start.column == 0 ? firstRow : firstRow + 1;
                var lastFullRow = lastRow - 1;

                if (range.end.column > 0)
                    this.removeInLine(lastRow, 0, range.end.column);

                if (lastFullRow >= firstFullRow)
                    this._removeLines(firstFullRow, lastFullRow);

                if (firstFullRow != firstRow) {
                    this.removeInLine(firstRow, range.start.column, this.getLine(firstRow).length);
                    this.removeNewLine(range.start.row);
                }
            } else {
                this.removeInLine(firstRow, range.start.column, range.end.column);
            }
            return range.start;
        };
        this.removeInLine = function(row, startColumn, endColumn) {
            if (startColumn == endColumn)
                return;

            var range = new Range(row, startColumn, row, endColumn);
            var line = this.getLine(row);
            var removed = line.substring(startColumn, endColumn);
            var newLine = line.substring(0, startColumn) + line.substring(endColumn, line.length);
            this.$lines.splice(row, 1, newLine);

            var delta = {
                action: "removeText",
                range: range,
                text: removed
            };
            this._signal("change", {
                data: delta
            });
            return range.start;
        };
        this.removeLines = function(firstRow, lastRow) {
            if (firstRow < 0 || lastRow >= this.getLength())
                return this.remove(new Range(firstRow, 0, lastRow + 1, 0));
            return this._removeLines(firstRow, lastRow);
        };

        this._removeLines = function(firstRow, lastRow) {
            var range = new Range(firstRow, 0, lastRow + 1, 0);
            var removed = this.$lines.splice(firstRow, lastRow - firstRow + 1);

            var delta = {
                action: "removeLines",
                range: range,
                nl: this.getNewLineCharacter(),
                lines: removed
            };
            this._signal("change", {
                data: delta
            });
            return removed;
        };
        this.removeNewLine = function(row) {
            var firstLine = this.getLine(row);
            var secondLine = this.getLine(row + 1);

            var range = new Range(row, firstLine.length, row + 1, 0);
            var line = firstLine + secondLine;

            this.$lines.splice(row, 2, line);

            var delta = {
                action: "removeText",
                range: range,
                text: this.getNewLineCharacter()
            };
            this._signal("change", {
                data: delta
            });
        };
        this.replace = function(range, text) {
            if (!(range instanceof Range))
                range = Range.fromPoints(range.start, range.end);
            if (text.length == 0 && range.isEmpty())
                return range.start;
            if (text == this.getTextRange(range))
                return range.end;

            this.remove(range);
            if (text) {
                var end = this.insert(range.start, text);
            } else {
                end = range.start;
            }

            return end;
        };
        this.applyDeltas = function(deltas) {
            for (var i = 0; i < deltas.length; i++) {
                var delta = deltas[i];
                var range = Range.fromPoints(delta.range.start, delta.range.end);

                if (delta.action == "insertLines")
                    this.insertLines(range.start.row, delta.lines);
                else if (delta.action == "insertText")
                    this.insert(range.start, delta.text);
                else if (delta.action == "removeLines")
                    this._removeLines(range.start.row, range.end.row - 1);
                else if (delta.action == "removeText")
                    this.remove(range);
            }
        };
        this.revertDeltas = function(deltas) {
            for (var i = deltas.length - 1; i >= 0; i--) {
                var delta = deltas[i];

                var range = Range.fromPoints(delta.range.start, delta.range.end);

                if (delta.action == "insertLines")
                    this._removeLines(range.start.row, range.end.row - 1);
                else if (delta.action == "insertText")
                    this.remove(range);
                else if (delta.action == "removeLines")
                    this._insertLines(range.start.row, delta.lines);
                else if (delta.action == "removeText")
                    this.insert(range.start, delta.text);
            }
        };
        this.indexToPosition = function(index, startRow) {
            var lines = this.$lines || this.getAllLines();
            var newlineLength = this.getNewLineCharacter().length;
            for (var i = startRow || 0, l = lines.length; i < l; i++) {
                index -= lines[i].length + newlineLength;
                if (index < 0)
                    return {
                        row: i,
                        column: index + lines[i].length + newlineLength
                    };
            }
            return {
                row: l - 1,
                column: lines[l - 1].length
            };
        };
        this.positionToIndex = function(pos, startRow) {
            var lines = this.$lines || this.getAllLines();
            var newlineLength = this.getNewLineCharacter().length;
            var index = 0;
            var row = Math.min(pos.row, lines.length);
            for (var i = startRow || 0; i < row; ++i)
                index += lines[i].length + newlineLength;

            return index + pos.column;
        };

    }).call(Document.prototype);

    exports.Document = Document;
});

ace.define("ace/worker/mirror", ["require", "exports", "module", "ace/document", "ace/lib/lang"], function(require, exports, module) {
    "use strict";

    var Document = require("../document").Document;
    var lang = require("../lib/lang");

    var Mirror = exports.Mirror = function(sender) {
        this.sender = sender;
        var doc = this.doc = new Document("");

        var deferredUpdate = this.deferredUpdate = lang.delayedCall(this.onUpdate.bind(this));

        var _self = this;
        sender.on("change", function(e) {
            doc.applyDeltas(e.data);
            if (_self.$timeout)
                return deferredUpdate.schedule(_self.$timeout);
            _self.onUpdate();
        });
    };

    (function() {

        this.$timeout = 500;

        this.setTimeout = function(timeout) {
            this.$timeout = timeout;
        };

        this.setValue = function(value) {
            this.doc.setValue(value);
            this.deferredUpdate.schedule(this.$timeout);
        };

        this.getValue = function(callbackId) {
            this.sender.callback(this.doc.getValue(), callbackId);
        };

        this.onUpdate = function() {};

        this.isPending = function() {
            return this.deferredUpdate.isPending();
        };

    }).call(Mirror.prototype);

});

ace.define("ace/mode/css/csslint", ["require", "exports", "module"], function(require, exports, module) {
    var parserlib = {};
    (function() {
        function EventTarget() {
            this._listeners = {};
        }

        EventTarget.prototype = {
            constructor: EventTarget,
            addListener: function(type, listener) {
                if (!this._listeners[type]) {
                    this._listeners[type] = [];
                }

                this._listeners[type].push(listener);
            },
            fire: function(event) {
                if (typeof event == "string") {
                    event = {
                        type: event
                    };
                }
                if (typeof event.target != "undefined") {
                    event.target = this;
                }

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

                if (this._listeners[event.type]) {
                    var listeners = this._listeners[event.type].concat();
                    for (var i = 0, len = listeners.length; i < len; i++) {
                        listeners[i].call(this, event);
                    }
                }
            },
            removeListener: function(type, listener) {
                if (this._listeners[type]) {
                    var listeners = this._listeners[type];
                    for (var i = 0, len = listeners.length; i < len; i++) {
                        if (listeners[i] === listener) {
                            listeners.splice(i, 1);
                            break;
                        }
                    }


                }
            }
        };

        function StringReader(text) {
            this._input = text.replace(/\n\r?/g, "\n");
            this._line = 1;
            this._col = 1;
            this._cursor = 0;
        }

        StringReader.prototype = {
            constructor: StringReader,
            getCol: function() {
                return this._col;
            },
            getLine: function() {
                return this._line;
            },
            eof: function() {
                return (this._cursor == this._input.length);
            },
            peek: function(count) {
                var c = null;
                count = (typeof count == "undefined" ? 1 : count);
                if (this._cursor < this._input.length) {
                    c = this._input.charAt(this._cursor + count - 1);
                }

                return c;
            },
            read: function() {
                var c = null;
                if (this._cursor < this._input.length) {
                    if (this._input.charAt(this._cursor) == "\n") {
                        this._line++;
                        this._col = 1;
                    } else {
                        this._col++;
                    }
                    c = this._input.charAt(this._cursor++);
                }

                return c;
            },
            mark: function() {
                this._bookmark = {
                    cursor: this._cursor,
                    line: this._line,
                    col: this._col
                };
            },

            reset: function() {
                if (this._bookmark) {
                    this._cursor = this._bookmark.cursor;
                    this._line = this._bookmark.line;
                    this._col = this._bookmark.col;
                    delete this._bookmark;
                }
            },
            readTo: function(pattern) {

                var buffer = "",
                    c;
                while (buffer.length < pattern.length || buffer.lastIndexOf(pattern) != buffer.length - pattern.length) {
                    c = this.read();
                    if (c) {
                        buffer += c;
                    } else {
                        throw new Error("Expected \"" + pattern + "\" at line " + this._line + ", col " + this._col + ".");
                    }
                }

                return buffer;

            },
            readWhile: function(filter) {

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

                while (c !== null && filter(c)) {
                    buffer += c;
                    c = this.read();
                }

                return buffer;

            },
            readMatch: function(matcher) {

                var source = this._input.substring(this._cursor),
                    value = null;
                if (typeof matcher == "string") {
                    if (source.indexOf(matcher) === 0) {
                        value = this.readCount(matcher.length);
                    }
                } else if (matcher instanceof RegExp) {
                    if (matcher.test(source)) {
                        value = this.readCount(RegExp.lastMatch.length);
                    }
                }

                return value;
            },
            readCount: function(count) {
                var buffer = "";

                while (count--) {
                    buffer += this.read();
                }

                return buffer;
            }

        };

        function SyntaxError(message, line, col) {
            this.col = col;
            this.line = line;
            this.message = message;

        }
        SyntaxError.prototype = new Error();

        function SyntaxUnit(text, line, col, type) {
            this.col = col;
            this.line = line;
            this.text = text;
            this.type = type;
        }
        SyntaxUnit.fromToken = function(token) {
            return new SyntaxUnit(token.value, token.startLine, token.startCol);
        };

        SyntaxUnit.prototype = {
            constructor: SyntaxUnit,
            valueOf: function() {
                return this.text;
            },
            toString: function() {
                return this.text;
            }

        };

        function TokenStreamBase(input, tokenData) {
            this._reader = input ? new StringReader(input.toString()) : null;
            this._token = null;
            this._tokenData = tokenData;
            this._lt = [];
            this._ltIndex = 0;

            this._ltIndexCache = [];
        }
        TokenStreamBase.createTokenData = function(tokens) {

            var nameMap = [],
                typeMap = {},
                tokenData = tokens.concat([]),
                i = 0,
                len = tokenData.length + 1;

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

            for (; i < len; i++) {
                nameMap.push(tokenData[i].name);
                tokenData[tokenData[i].name] = i;
                if (tokenData[i].text) {
                    typeMap[tokenData[i].text] = i;
                }
            }

            tokenData.name = function(tt) {
                return nameMap[tt];
            };

            tokenData.type = function(c) {
                return typeMap[c];
            };

            return tokenData;
        };

        TokenStreamBase.prototype = {
            constructor: TokenStreamBase,
            match: function(tokenTypes, channel) {
                if (!(tokenTypes instanceof Array)) {
                    tokenTypes = [tokenTypes];
                }

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

                while (i < len) {
                    if (tt == tokenTypes[i++]) {
                        return true;
                    }
                }
                this.unget();
                return false;
            },
            mustMatch: function(tokenTypes, channel) {

                var token;
                if (!(tokenTypes instanceof Array)) {
                    tokenTypes = [tokenTypes];
                }

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

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

                return this.LA(0);
            },
            get: function(channel) {

                var tokenInfo = this._tokenData,
                    reader = this._reader,
                    value,
                    i = 0,
                    len = tokenInfo.length,
                    found = false,
                    token,
                    info;
                if (this._lt.length && this._ltIndex >= 0 && this._ltIndex < this._lt.length) {

                    i++;
                    this._token = this._lt[this._ltIndex++];
                    info = tokenInfo[this._token.type];
                    while ((info.channel !== undefined && channel !== info.channel) &&
                        this._ltIndex < this._lt.length) {
                        this._token = this._lt[this._ltIndex++];
                        info = tokenInfo[this._token.type];
                        i++;
                    }
                    if ((info.channel === undefined || channel === info.channel) &&
                        this._ltIndex <= this._lt.length) {
                        this._ltIndexCache.push(i);
                        return this._token.type;
                    }
                }
                token = this._getToken();
                if (token.type > -1 && !tokenInfo[token.type].hide) {
                    token.channel = tokenInfo[token.type].channel;
                    this._token = token;
                    this._lt.push(token);
                    this._ltIndexCache.push(this._lt.length - this._ltIndex + i);
                    if (this._lt.length > 5) {
                        this._lt.shift();
                    }
                    if (this._ltIndexCache.length > 5) {
                        this._ltIndexCache.shift();
                    }
                    this._ltIndex = this._lt.length;
                }
                info = tokenInfo[token.type];
                if (info &&
                    (info.hide ||
                        (info.channel !== undefined && channel !== info.channel))) {
                    return this.get(channel);
                } else {
                    return token.type;
                }
            },
            LA: function(index) {
                var total = index,
                    tt;
                if (index > 0) {
                    if (index > 5) {
                        throw new Error("Too much lookahead.");
                    }
                    while (total) {
                        tt = this.get();
                        total--;
                    }
                    while (total < index) {
                        this.unget();
                        total++;
                    }
                } else if (index < 0) {

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

                } else {
                    tt = this._token.type;
                }

                return tt;

            },
            LT: function(index) {
                this.LA(index);
                return this._lt[this._ltIndex + index - 1];
            },
            peek: function() {
                return this.LA(1);
            },
            token: function() {
                return this._token;
            },
            tokenName: function(tokenType) {
                if (tokenType < 0 || tokenType > this._tokenData.length) {
                    return "UNKNOWN_TOKEN";
                } else {
                    return this._tokenData[tokenType].name;
                }
            },
            tokenType: function(tokenName) {
                return this._tokenData[tokenName] || -1;
            },
            unget: function() {
                if (this._ltIndexCache.length) {
                    this._ltIndex -= this._ltIndexCache.pop(); //--;
                    this._token = this._lt[this._ltIndex - 1];
                } else {
                    throw new Error("Too much lookahead.");
                }
            }

        };


        parserlib.util = {
            StringReader: StringReader,
            SyntaxError: SyntaxError,
            SyntaxUnit: SyntaxUnit,
            EventTarget: EventTarget,
            TokenStreamBase: TokenStreamBase
        };
    })();
    (function() {
        var EventTarget = parserlib.util.EventTarget,
            TokenStreamBase = parserlib.util.TokenStreamBase,
            StringReader = parserlib.util.StringReader,
            SyntaxError = parserlib.util.SyntaxError,
            SyntaxUnit = parserlib.util.SyntaxUnit;

        var Colors = {
            aliceblue: "#f0f8ff",
            antiquewhite: "#faebd7",
            aqua: "#00ffff",
            aquamarine: "#7fffd4",
            azure: "#f0ffff",
            beige: "#f5f5dc",
            bisque: "#ffe4c4",
            black: "#000000",
            blanchedalmond: "#ffebcd",
            blue: "#0000ff",
            blueviolet: "#8a2be2",
            brown: "#a52a2a",
            burlywood: "#deb887",
            cadetblue: "#5f9ea0",
            chartreuse: "#7fff00",
            chocolate: "#d2691e",
            coral: "#ff7f50",
            cornflowerblue: "#6495ed",
            cornsilk: "#fff8dc",
            crimson: "#dc143c",
            cyan: "#00ffff",
            darkblue: "#00008b",
            darkcyan: "#008b8b",
            darkgoldenrod: "#b8860b",
            darkgray: "#a9a9a9",
            darkgrey: "#a9a9a9",
            darkgreen: "#006400",
            darkkhaki: "#bdb76b",
            darkmagenta: "#8b008b",
            darkolivegreen: "#556b2f",
            darkorange: "#ff8c00",
            darkorchid: "#9932cc",
            darkred: "#8b0000",
            darksalmon: "#e9967a",
            darkseagreen: "#8fbc8f",
            darkslateblue: "#483d8b",
            darkslategray: "#2f4f4f",
            darkslategrey: "#2f4f4f",
            darkturquoise: "#00ced1",
            darkviolet: "#9400d3",
            deeppink: "#ff1493",
            deepskyblue: "#00bfff",
            dimgray: "#696969",
            dimgrey: "#696969",
            dodgerblue: "#1e90ff",
            firebrick: "#b22222",
            floralwhite: "#fffaf0",
            forestgreen: "#228b22",
            fuchsia: "#ff00ff",
            gainsboro: "#dcdcdc",
            ghostwhite: "#f8f8ff",
            gold: "#ffd700",
            goldenrod: "#daa520",
            gray: "#808080",
            grey: "#808080",
            green: "#008000",
            greenyellow: "#adff2f",
            honeydew: "#f0fff0",
            hotpink: "#ff69b4",
            indianred: "#cd5c5c",
            indigo: "#4b0082",
            ivory: "#fffff0",
            khaki: "#f0e68c",
            lavender: "#e6e6fa",
            lavenderblush: "#fff0f5",
            lawngreen: "#7cfc00",
            lemonchiffon: "#fffacd",
            lightblue: "#add8e6",
            lightcoral: "#f08080",
            lightcyan: "#e0ffff",
            lightgoldenrodyellow: "#fafad2",
            lightgray: "#d3d3d3",
            lightgrey: "#d3d3d3",
            lightgreen: "#90ee90",
            lightpink: "#ffb6c1",
            lightsalmon: "#ffa07a",
            lightseagreen: "#20b2aa",
            lightskyblue: "#87cefa",
            lightslategray: "#778899",
            lightslategrey: "#778899",
            lightsteelblue: "#b0c4de",
            lightyellow: "#ffffe0",
            lime: "#00ff00",
            limegreen: "#32cd32",
            linen: "#faf0e6",
            magenta: "#ff00ff",
            maroon: "#800000",
            mediumaquamarine: "#66cdaa",
            mediumblue: "#0000cd",
            mediumorchid: "#ba55d3",
            mediumpurple: "#9370d8",
            mediumseagreen: "#3cb371",
            mediumslateblue: "#7b68ee",
            mediumspringgreen: "#00fa9a",
            mediumturquoise: "#48d1cc",
            mediumvioletred: "#c71585",
            midnightblue: "#191970",
            mintcream: "#f5fffa",
            mistyrose: "#ffe4e1",
            moccasin: "#ffe4b5",
            navajowhite: "#ffdead",
            navy: "#000080",
            oldlace: "#fdf5e6",
            olive: "#808000",
            olivedrab: "#6b8e23",
            orange: "#ffa500",
            orangered: "#ff4500",
            orchid: "#da70d6",
            palegoldenrod: "#eee8aa",
            palegreen: "#98fb98",
            paleturquoise: "#afeeee",
            palevioletred: "#d87093",
            papayawhip: "#ffefd5",
            peachpuff: "#ffdab9",
            peru: "#cd853f",
            pink: "#ffc0cb",
            plum: "#dda0dd",
            powderblue: "#b0e0e6",
            purple: "#800080",
            red: "#ff0000",
            rosybrown: "#bc8f8f",
            royalblue: "#4169e1",
            saddlebrown: "#8b4513",
            salmon: "#fa8072",
            sandybrown: "#f4a460",
            seagreen: "#2e8b57",
            seashell: "#fff5ee",
            sienna: "#a0522d",
            silver: "#c0c0c0",
            skyblue: "#87ceeb",
            slateblue: "#6a5acd",
            slategray: "#708090",
            slategrey: "#708090",
            snow: "#fffafa",
            springgreen: "#00ff7f",
            steelblue: "#4682b4",
            tan: "#d2b48c",
            teal: "#008080",
            thistle: "#d8bfd8",
            tomato: "#ff6347",
            turquoise: "#40e0d0",
            violet: "#ee82ee",
            wheat: "#f5deb3",
            white: "#ffffff",
            whitesmoke: "#f5f5f5",
            yellow: "#ffff00",
            yellowgreen: "#9acd32",
            activeBorder: "Active window border.",
            activecaption: "Active window caption.",
            appworkspace: "Background color of multiple document interface.",
            background: "Desktop background.",
            buttonface: "The face background color for 3-D elements that appear 3-D due to one layer of surrounding border.",
            buttonhighlight: "The color of the border facing the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",
            buttonshadow: "The color of the border away from the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",
            buttontext: "Text on push buttons.",
            captiontext: "Text in caption, size box, and scrollbar arrow box.",
            graytext: "Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.",
            greytext: "Greyed (disabled) text. This color is set to #000 if the current display driver does not support a solid grey color.",
            highlight: "Item(s) selected in a control.",
            highlighttext: "Text of item(s) selected in a control.",
            inactiveborder: "Inactive window border.",
            inactivecaption: "Inactive window caption.",
            inactivecaptiontext: "Color of text in an inactive caption.",
            infobackground: "Background color for tooltip controls.",
            infotext: "Text color for tooltip controls.",
            menu: "Menu background.",
            menutext: "Text in menus.",
            scrollbar: "Scroll bar gray area.",
            threeddarkshadow: "The color of the darker (generally outer) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
            threedface: "The face background color for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
            threedhighlight: "The color of the lighter (generally outer) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
            threedlightshadow: "The color of the darker (generally inner) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
            threedshadow: "The color of the lighter (generally inner) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
            window: "Window background.",
            windowframe: "Window frame.",
            windowtext: "Text in windows."
        };

        function Combinator(text, line, col) {

            SyntaxUnit.call(this, text, line, col, Parser.COMBINATOR_TYPE);
            this.type = "unknown";
            if (/^\s+$/.test(text)) {
                this.type = "descendant";
            } else if (text == ">") {
                this.type = "child";
            } else if (text == "+") {
                this.type = "adjacent-sibling";
            } else if (text == "~") {
                this.type = "sibling";
            }

        }

        Combinator.prototype = new SyntaxUnit();
        Combinator.prototype.constructor = Combinator;

        function MediaFeature(name, value) {

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

        MediaFeature.prototype = new SyntaxUnit();
        MediaFeature.prototype.constructor = MediaFeature;

        function MediaQuery(modifier, mediaType, features, line, col) {

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

        }

        MediaQuery.prototype = new SyntaxUnit();
        MediaQuery.prototype.constructor = MediaQuery;

        function Parser(options) {
            EventTarget.call(this);


            this.options = options || {};

            this._tokenStream = null;
        }
        Parser.DEFAULT_TYPE = 0;
        Parser.COMBINATOR_TYPE = 1;
        Parser.MEDIA_FEATURE_TYPE = 2;
        Parser.MEDIA_QUERY_TYPE = 3;
        Parser.PROPERTY_NAME_TYPE = 4;
        Parser.PROPERTY_VALUE_TYPE = 5;
        Parser.PROPERTY_VALUE_PART_TYPE = 6;
        Parser.SELECTOR_TYPE = 7;
        Parser.SELECTOR_PART_TYPE = 8;
        Parser.SELECTOR_SUB_PART_TYPE = 9;

        Parser.prototype = function() {

            var proto = new EventTarget(), //new prototype
                prop,
                additions = {
                    constructor: Parser,
                    DEFAULT_TYPE: 0,
                    COMBINATOR_TYPE: 1,
                    MEDIA_FEATURE_TYPE: 2,
                    MEDIA_QUERY_TYPE: 3,
                    PROPERTY_NAME_TYPE: 4,
                    PROPERTY_VALUE_TYPE: 5,
                    PROPERTY_VALUE_PART_TYPE: 6,
                    SELECTOR_TYPE: 7,
                    SELECTOR_PART_TYPE: 8,
                    SELECTOR_SUB_PART_TYPE: 9,

                    _stylesheet: function() {

                        var tokenStream = this._tokenStream,
                            charset = null,
                            count,
                            token,
                            tt;

                        this.fire("startstylesheet");
                        this._charset();

                        this._skipCruft();
                        while (tokenStream.peek() == Tokens.IMPORT_SYM) {
                            this._import();
                            this._skipCruft();
                        }
                        while (tokenStream.peek() == Tokens.NAMESPACE_SYM) {
                            this._namespace();
                            this._skipCruft();
                        }
                        tt = tokenStream.peek();
                        while (tt > Tokens.EOF) {

                            try {

                                switch (tt) {
                                    case Tokens.MEDIA_SYM:
                                        this._media();
                                        this._skipCruft();
                                        break;
                                    case Tokens.PAGE_SYM:
                                        this._page();
                                        this._skipCruft();
                                        break;
                                    case Tokens.FONT_FACE_SYM:
                                        this._font_face();
                                        this._skipCruft();
                                        break;
                                    case Tokens.KEYFRAMES_SYM:
                                        this._keyframes();
                                        this._skipCruft();
                                        break;
                                    case Tokens.VIEWPORT_SYM:
                                        this._viewport();
                                        this._skipCruft();
                                        break;
                                    case Tokens.UNKNOWN_SYM: //unknown @ rule
                                        tokenStream.get();
                                        if (!this.options.strict) {
                                            this.fire({
                                                type: "error",
                                                error: null,
                                                message: "Unknown @ rule: " + tokenStream.LT(0).value + ".",
                                                line: tokenStream.LT(0).startLine,
                                                col: tokenStream.LT(0).startCol
                                            });
                                            count = 0;
                                            while (tokenStream.advance([Tokens.LBRACE, Tokens.RBRACE]) == Tokens.LBRACE) {
                                                count++; //keep track of nesting depth
                                            }

                                            while (count) {
                                                tokenStream.advance([Tokens.RBRACE]);
                                                count--;
                                            }

                                        } else {
                                            throw new SyntaxError("Unknown @ rule.", tokenStream.LT(0).startLine, tokenStream.LT(0).startCol);
                                        }
                                        break;
                                    case Tokens.S:
                                        this._readWhitespace();
                                        break;
                                    default:
                                        if (!this._ruleset()) {
                                            switch (tt) {
                                                case Tokens.CHARSET_SYM:
                                                    token = tokenStream.LT(1);
                                                    this._charset(false);
                                                    throw new SyntaxError("@charset not allowed here.", token.startLine, token.startCol);
                                                case Tokens.IMPORT_SYM:
                                                    token = tokenStream.LT(1);
                                                    this._import(false);
                                                    throw new SyntaxError("@import not allowed here.", token.startLine, token.startCol);
                                                case Tokens.NAMESPACE_SYM:
                                                    token = tokenStream.LT(1);
                                                    this._namespace(false);
                                                    throw new SyntaxError("@namespace not allowed here.", token.startLine, token.startCol);
                                                default:
                                                    tokenStream.get(); //get the last token
                                                    this._unexpectedToken(tokenStream.token());
                                            }

                                        }
                                }
                            } catch (ex) {
                                if (ex instanceof SyntaxError && !this.options.strict) {
                                    this.fire({
                                        type: "error",
                                        error: ex,
                                        message: ex.message,
                                        line: ex.line,
                                        col: ex.col
                                    });
                                } else {
                                    throw ex;
                                }
                            }

                            tt = tokenStream.peek();
                        }

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

                        this.fire("endstylesheet");
                    },

                    _charset: function(emit) {
                        var tokenStream = this._tokenStream,
                            charset,
                            token,
                            line,
                            col;

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

                            this._readWhitespace();
                            tokenStream.mustMatch(Tokens.STRING);

                            token = tokenStream.token();
                            charset = token.value;

                            this._readWhitespace();
                            tokenStream.mustMatch(Tokens.SEMICOLON);

                            if (emit !== false) {
                                this.fire({
                                    type: "charset",
                                    charset: charset,
                                    line: line,
                                    col: col
                                });
                            }
                        }
                    },

                    _import: function(emit) {

                        var tokenStream = this._tokenStream,
                            tt,
                            uri,
                            importToken,
                            mediaList = [];
                        tokenStream.mustMatch(Tokens.IMPORT_SYM);
                        importToken = tokenStream.token();
                        this._readWhitespace();

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

                        this._readWhitespace();

                        mediaList = this._media_query_list();
                        tokenStream.mustMatch(Tokens.SEMICOLON);
                        this._readWhitespace();

                        if (emit !== false) {
                            this.fire({
                                type: "import",
                                uri: uri,
                                media: mediaList,
                                line: importToken.startLine,
                                col: importToken.startCol
                            });
                        }

                    },

                    _namespace: function(emit) {

                        var tokenStream = this._tokenStream,
                            line,
                            col,
                            prefix,
                            uri;
                        tokenStream.mustMatch(Tokens.NAMESPACE_SYM);
                        line = tokenStream.token().startLine;
                        col = tokenStream.token().startCol;
                        this._readWhitespace();
                        if (tokenStream.match(Tokens.IDENT)) {
                            prefix = tokenStream.token().value;
                            this._readWhitespace();
                        }

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

                        this._readWhitespace();
                        tokenStream.mustMatch(Tokens.SEMICOLON);
                        this._readWhitespace();

                        if (emit !== false) {
                            this.fire({
                                type: "namespace",
                                prefix: prefix,
                                uri: uri,
                                line: line,
                                col: col
                            });
                        }

                    },

                    _media: function() {
                        var tokenStream = this._tokenStream,
                            line,
                            col,
                            mediaList; //       = [];
                        tokenStream.mustMatch(Tokens.MEDIA_SYM);
                        line = tokenStream.token().startLine;
                        col = tokenStream.token().startCol;

                        this._readWhitespace();

                        mediaList = this._media_query_list();

                        tokenStream.mustMatch(Tokens.LBRACE);
                        this._readWhitespace();

                        this.fire({
                            type: "startmedia",
                            media: mediaList,
                            line: line,
                            col: col
                        });

                        while (true) {
                            if (tokenStream.peek() == Tokens.PAGE_SYM) {
                                this._page();
                            } else if (tokenStream.peek() == Tokens.FONT_FACE_SYM) {
                                this._font_face();
                            } else if (tokenStream.peek() == Tokens.VIEWPORT_SYM) {
                                this._viewport();
                            } else if (!this._ruleset()) {
                                break;
                            }
                        }

                        tokenStream.mustMatch(Tokens.RBRACE);
                        this._readWhitespace();

                        this.fire({
                            type: "endmedia",
                            media: mediaList,
                            line: line,
                            col: col
                        });
                    },
                    _media_query_list: function() {
                        var tokenStream = this._tokenStream,
                            mediaList = [];


                        this._readWhitespace();

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

                        while (tokenStream.match(Tokens.COMMA)) {
                            this._readWhitespace();
                            mediaList.push(this._media_query());
                        }

                        return mediaList;
                    },
                    _media_query: function() {
                        var tokenStream = this._tokenStream,
                            type = null,
                            ident = null,
                            token = null,
                            expressions = [];

                        if (tokenStream.match(Tokens.IDENT)) {
                            ident = tokenStream.token().value.toLowerCase();
                            if (ident != "only" && ident != "not") {
                                tokenStream.unget();
                                ident = null;
                            } else {
                                token = tokenStream.token();
                            }
                        }

                        this._readWhitespace();

                        if (tokenStream.peek() == Tokens.IDENT) {
                            type = this._media_type();
                            if (token === null) {
                                token = tokenStream.token();
                            }
                        } else if (tokenStream.peek() == Tokens.LPAREN) {
                            if (token === null) {
                                token = tokenStream.LT(1);
                            }
                            expressions.push(this._media_expression());
                        }

                        if (type === null && expressions.length === 0) {
                            return null;
                        } else {
                            this._readWhitespace();
                            while (tokenStream.match(Tokens.IDENT)) {
                                if (tokenStream.token().value.toLowerCase() != "and") {
                                    this._unexpectedToken(tokenStream.token());
                                }

                                this._readWhitespace();
                                expressions.push(this._media_expression());
                            }
                        }

                        return new MediaQuery(ident, type, expressions, token.startLine, token.startCol);
                    },
                    _media_type: function() {
                        return this._media_feature();
                    },
                    _media_expression: function() {
                        var tokenStream = this._tokenStream,
                            feature = null,
                            token,
                            expression = null;

                        tokenStream.mustMatch(Tokens.LPAREN);

                        feature = this._media_feature();
                        this._readWhitespace();

                        if (tokenStream.match(Tokens.COLON)) {
                            this._readWhitespace();
                            token = tokenStream.LT(1);
                            expression = this._expression();
                        }

                        tokenStream.mustMatch(Tokens.RPAREN);
                        this._readWhitespace();

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

                        tokenStream.mustMatch(Tokens.IDENT);

                        return SyntaxUnit.fromToken(tokenStream.token());
                    },
                    _page: function() {
                        var tokenStream = this._tokenStream,
                            line,
                            col,
                            identifier = null,
                            pseudoPage = null;
                        tokenStream.mustMatch(Tokens.PAGE_SYM);
                        line = tokenStream.token().startLine;
                        col = tokenStream.token().startCol;

                        this._readWhitespace();

                        if (tokenStream.match(Tokens.IDENT)) {
                            identifier = tokenStream.token().value;
                            if (identifier.toLowerCase() === "auto") {
                                this._unexpectedToken(tokenStream.token());
                            }
                        }
                        if (tokenStream.peek() == Tokens.COLON) {
                            pseudoPage = this._pseudo_page();
                        }

                        this._readWhitespace();

                        this.fire({
                            type: "startpage",
                            id: identifier,
                            pseudo: pseudoPage,
                            line: line,
                            col: col
                        });

                        this._readDeclarations(true, true);

                        this.fire({
                            type: "endpage",
                            id: identifier,
                            pseudo: pseudoPage,
                            line: line,
                            col: col
                        });

                    },
                    _margin: function() {
                        var tokenStream = this._tokenStream,
                            line,
                            col,
                            marginSym = this._margin_sym();

                        if (marginSym) {
                            line = tokenStream.token().startLine;
                            col = tokenStream.token().startCol;

                            this.fire({
                                type: "startpagemargin",
                                margin: marginSym,
                                line: line,
                                col: col
                            });

                            this._readDeclarations(true);

                            this.fire({
                                type: "endpagemargin",
                                margin: marginSym,
                                line: line,
                                col: col
                            });
                            return true;
                        } else {
                            return false;
                        }
                    },
                    _margin_sym: function() {

                        var tokenStream = this._tokenStream;

                        if (tokenStream.match([Tokens.TOPLEFTCORNER_SYM, Tokens.TOPLEFT_SYM,
                            Tokens.TOPCENTER_SYM, Tokens.TOPRIGHT_SYM, Tokens.TOPRIGHTCORNER_SYM,
                            Tokens.BOTTOMLEFTCORNER_SYM, Tokens.BOTTOMLEFT_SYM,
                            Tokens.BOTTOMCENTER_SYM, Tokens.BOTTOMRIGHT_SYM,
                            Tokens.BOTTOMRIGHTCORNER_SYM, Tokens.LEFTTOP_SYM,
                            Tokens.LEFTMIDDLE_SYM, Tokens.LEFTBOTTOM_SYM, Tokens.RIGHTTOP_SYM,
                            Tokens.RIGHTMIDDLE_SYM, Tokens.RIGHTBOTTOM_SYM
                        ])) {
                            return SyntaxUnit.fromToken(tokenStream.token());
                        } else {
                            return null;
                        }

                    },

                    _pseudo_page: function() {

                        var tokenStream = this._tokenStream;

                        tokenStream.mustMatch(Tokens.COLON);
                        tokenStream.mustMatch(Tokens.IDENT);

                        return tokenStream.token().value;
                    },

                    _font_face: function() {
                        var tokenStream = this._tokenStream,
                            line,
                            col;
                        tokenStream.mustMatch(Tokens.FONT_FACE_SYM);
                        line = tokenStream.token().startLine;
                        col = tokenStream.token().startCol;

                        this._readWhitespace();

                        this.fire({
                            type: "startfontface",
                            line: line,
                            col: col
                        });

                        this._readDeclarations(true);

                        this.fire({
                            type: "endfontface",
                            line: line,
                            col: col
                        });
                    },

                    _viewport: function() {
                        var tokenStream = this._tokenStream,
                            line,
                            col;

                        tokenStream.mustMatch(Tokens.VIEWPORT_SYM);
                        line = tokenStream.token().startLine;
                        col = tokenStream.token().startCol;

                        this._readWhitespace();

                        this.fire({
                            type: "startviewport",
                            line: line,
                            col: col
                        });

                        this._readDeclarations(true);

                        this.fire({
                            type: "endviewport",
                            line: line,
                            col: col
                        });

                    },

                    _operator: function(inFunction) {

                        var tokenStream = this._tokenStream,
                            token = null;

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

                    },

                    _combinator: function() {

                        var tokenStream = this._tokenStream,
                            value = null,
                            token;

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

                        return value;
                    },

                    _unary_operator: function() {

                        var tokenStream = this._tokenStream;

                        if (tokenStream.match([Tokens.MINUS, Tokens.PLUS])) {
                            return tokenStream.token().value;
                        } else {
                            return null;
                        }
                    },

                    _property: function() {

                        var tokenStream = this._tokenStream,
                            value = null,
                            hack = null,
                            tokenValue,
                            token,
                            line,
                            col;
                        if (tokenStream.peek() == Tokens.STAR && this.options.starHack) {
                            tokenStream.get();
                            token = tokenStream.token();
                            hack = token.value;
                            line = token.startLine;
                            col = token.startCol;
                        }

                        if (tokenStream.match(Tokens.IDENT)) {
                            token = tokenStream.token();
                            tokenValue = token.value;
                            if (tokenValue.charAt(0) == "_" && this.options.underscoreHack) {
                                hack = "_";
                                tokenValue = tokenValue.substring(1);
                            }

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

                        return value;
                    },
                    _ruleset: function() {

                        var tokenStream = this._tokenStream,
                            tt,
                            selectors;
                        try {
                            selectors = this._selectors_group();
                        } catch (ex) {
                            if (ex instanceof SyntaxError && !this.options.strict) {
                                this.fire({
                                    type: "error",
                                    error: ex,
                                    message: ex.message,
                                    line: ex.line,
                                    col: ex.col
                                });
                                tt = tokenStream.advance([Tokens.RBRACE]);
                                if (tt == Tokens.RBRACE) {} else {
                                    throw ex;
                                }

                            } else {
                                throw ex;
                            }
                            return true;
                        }
                        if (selectors) {

                            this.fire({
                                type: "startrule",
                                selectors: selectors,
                                line: selectors[0].line,
                                col: selectors[0].col
                            });

                            this._readDeclarations(true);

                            this.fire({
                                type: "endrule",
                                selectors: selectors,
                                line: selectors[0].line,
                                col: selectors[0].col
                            });

                        }

                        return selectors;

                    },
                    _selectors_group: function() {
                        var tokenStream = this._tokenStream,
                            selectors = [],
                            selector;

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

                            selectors.push(selector);
                            while (tokenStream.match(Tokens.COMMA)) {
                                this._readWhitespace();
                                selector = this._selector();
                                if (selector !== null) {
                                    selectors.push(selector);
                                } else {
                                    this._unexpectedToken(tokenStream.LT(1));
                                }
                            }
                        }

                        return selectors.length ? selectors : null;
                    },
                    _selector: function() {

                        var tokenStream = this._tokenStream,
                            selector = [],
                            nextSelector = null,
                            combinator = null,
                            ws = null;
                        nextSelector = this._simple_selector_sequence();
                        if (nextSelector === null) {
                            return null;
                        }

                        selector.push(nextSelector);

                        do {
                            combinator = this._combinator();

                            if (combinator !== null) {
                                selector.push(combinator);
                                nextSelector = this._simple_selector_sequence();
                                if (nextSelector === null) {
                                    this._unexpectedToken(tokenStream.LT(1));
                                } else {
                                    selector.push(nextSelector);
                                }
                            } else {
                                if (this._readWhitespace()) {
                                    ws = new Combinator(tokenStream.token().value, tokenStream.token().startLine, tokenStream.token().startCol);
                                    combinator = this._combinator();
                                    nextSelector = this._simple_selector_sequence();
                                    if (nextSelector === null) {
                                        if (combinator !== null) {
                                            this._unexpectedToken(tokenStream.LT(1));
                                        }
                                    } else {

                                        if (combinator !== null) {
                                            selector.push(combinator);
                                        } else {
                                            selector.push(ws);
                                        }

                                        selector.push(nextSelector);
                                    }
                                } else {
                                    break;
                                }

                            }
                        } while (true);

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

                        var tokenStream = this._tokenStream,
                            elementName = null,
                            modifiers = [],
                            selectorText = "",
                            components = [
                                function() {
                                    return tokenStream.match(Tokens.HASH) ?
                                        new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
                                        null;
                                },
                                this._class,
                                this._attrib,
                                this._pseudo,
                                this._negation
                            ],
                            i = 0,
                            len = components.length,
                            component = null,
                            found = false,
                            line,
                            col;
                        line = tokenStream.LT(1).startLine;
                        col = tokenStream.LT(1).startCol;

                        elementName = this._type_selector();
                        if (!elementName) {
                            elementName = this._universal();
                        }

                        if (elementName !== null) {
                            selectorText += elementName;
                        }

                        while (true) {
                            if (tokenStream.peek() === Tokens.S) {
                                break;
                            }
                            while (i < len && component === null) {
                                component = components[i++].call(this);
                            }

                            if (component === null) {
                                if (selectorText === "") {
                                    return null;
                                } else {
                                    break;
                                }
                            } else {
                                i = 0;
                                modifiers.push(component);
                                selectorText += component.toString();
                                component = null;
                            }
                        }


                        return selectorText !== "" ?
                            new SelectorPart(elementName, modifiers, selectorText, line, col) :
                            null;
                    },
                    _type_selector: function() {

                        var tokenStream = this._tokenStream,
                            ns = this._namespace_prefix(),
                            elementName = this._element_name();

                        if (!elementName) {
                            if (ns) {
                                tokenStream.unget();
                                if (ns.length > 1) {
                                    tokenStream.unget();
                                }
                            }

                            return null;
                        } else {
                            if (ns) {
                                elementName.text = ns + elementName.text;
                                elementName.col -= ns.length;
                            }
                            return elementName;
                        }
                    },
                    _class: function() {

                        var tokenStream = this._tokenStream,
                            token;

                        if (tokenStream.match(Tokens.DOT)) {
                            tokenStream.mustMatch(Tokens.IDENT);
                            token = tokenStream.token();
                            return new SelectorSubPart("." + token.value, "class", token.startLine, token.startCol - 1);
                        } else {
                            return null;
                        }

                    },
                    _element_name: function() {

                        var tokenStream = this._tokenStream,
                            token;

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

                        } else {
                            return null;
                        }
                    },
                    _namespace_prefix: function() {
                        var tokenStream = this._tokenStream,
                            value = "";
                        if (tokenStream.LA(1) === Tokens.PIPE || tokenStream.LA(2) === Tokens.PIPE) {

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

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

                        }

                        return value.length ? value : null;
                    },
                    _universal: function() {
                        var tokenStream = this._tokenStream,
                            value = "",
                            ns;

                        ns = this._namespace_prefix();
                        if (ns) {
                            value += ns;
                        }

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

                        return value.length ? value : null;

                    },
                    _attrib: function() {

                        var tokenStream = this._tokenStream,
                            value = null,
                            ns,
                            token;

                        if (tokenStream.match(Tokens.LBRACKET)) {
                            token = tokenStream.token();
                            value = token.value;
                            value += this._readWhitespace();

                            ns = this._namespace_prefix();

                            if (ns) {
                                value += ns;
                            }

                            tokenStream.mustMatch(Tokens.IDENT);
                            value += tokenStream.token().value;
                            value += this._readWhitespace();

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

                                value += tokenStream.token().value;
                                value += this._readWhitespace();

                                tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
                                value += tokenStream.token().value;
                                value += this._readWhitespace();
                            }

                            tokenStream.mustMatch(Tokens.RBRACKET);

                            return new SelectorSubPart(value + "]", "attribute", token.startLine, token.startCol);
                        } else {
                            return null;
                        }
                    },
                    _pseudo: function() {

                        var tokenStream = this._tokenStream,
                            pseudo = null,
                            colons = ":",
                            line,
                            col;

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

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

                            if (tokenStream.match(Tokens.IDENT)) {
                                pseudo = tokenStream.token().value;
                                line = tokenStream.token().startLine;
                                col = tokenStream.token().startCol - colons.length;
                            } else if (tokenStream.peek() == Tokens.FUNCTION) {
                                line = tokenStream.LT(1).startLine;
                                col = tokenStream.LT(1).startCol - colons.length;
                                pseudo = this._functional_pseudo();
                            }

                            if (pseudo) {
                                pseudo = new SelectorSubPart(colons + pseudo, "pseudo", line, col);
                            }
                        }

                        return pseudo;
                    },
                    _functional_pseudo: function() {

                        var tokenStream = this._tokenStream,
                            value = null;

                        if (tokenStream.match(Tokens.FUNCTION)) {
                            value = tokenStream.token().value;
                            value += this._readWhitespace();
                            value += this._expression();
                            tokenStream.mustMatch(Tokens.RPAREN);
                            value += ")";
                        }

                        return value;
                    },
                    _expression: function() {

                        var tokenStream = this._tokenStream,
                            value = "";

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

                            value += tokenStream.token().value;
                            value += this._readWhitespace();
                        }

                        return value.length ? value : null;

                    },
                    _negation: function() {

                        var tokenStream = this._tokenStream,
                            line,
                            col,
                            value = "",
                            arg,
                            subpart = null;

                        if (tokenStream.match(Tokens.NOT)) {
                            value = tokenStream.token().value;
                            line = tokenStream.token().startLine;
                            col = tokenStream.token().startCol;
                            value += this._readWhitespace();
                            arg = this._negation_arg();
                            value += arg;
                            value += this._readWhitespace();
                            tokenStream.match(Tokens.RPAREN);
                            value += tokenStream.token().value;

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

                        return subpart;
                    },
                    _negation_arg: function() {

                        var tokenStream = this._tokenStream,
                            args = [
                                this._type_selector,
                                this._universal,
                                function() {
                                    return tokenStream.match(Tokens.HASH) ?
                                        new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
                                        null;
                                },
                                this._class,
                                this._attrib,
                                this._pseudo
                            ],
                            arg = null,
                            i = 0,
                            len = args.length,
                            elementName,
                            line,
                            col,
                            part;

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

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

                            arg = args[i].call(this);
                            i++;
                        }
                        if (arg === null) {
                            this._unexpectedToken(tokenStream.LT(1));
                        }
                        if (arg.type == "elementName") {
                            part = new SelectorPart(arg, [], arg.toString(), line, col);
                        } else {
                            part = new SelectorPart(null, [arg], arg.toString(), line, col);
                        }

                        return part;
                    },

                    _declaration: function() {

                        var tokenStream = this._tokenStream,
                            property = null,
                            expr = null,
                            prio = null,
                            error = null,
                            invalid = null,
                            propertyName = "";

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

                            tokenStream.mustMatch(Tokens.COLON);
                            this._readWhitespace();

                            expr = this._expr();
                            if (!expr || expr.length === 0) {
                                this._unexpectedToken(tokenStream.LT(1));
                            }

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

                                propertyName = property.text;
                            }

                            try {
                                this._validateProperty(propertyName, expr);
                            } catch (ex) {
                                invalid = ex;
                            }

                            this.fire({
                                type: "property",
                                property: property,
                                value: expr,
                                important: prio,
                                line: property.line,
                                col: property.col,
                                invalid: invalid
                            });

                            return true;
                        } else {
                            return false;
                        }
                    },

                    _prio: function() {

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

                        this._readWhitespace();
                        return result;
                    },

                    _expr: function(inFunction) {

                        var tokenStream = this._tokenStream,
                            values = [],
                            value = null,
                            operator = null;

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

                            values.push(value);

                            do {
                                operator = this._operator(inFunction);
                                if (operator) {
                                    values.push(operator);
                                }
                                /*else {
values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
valueParts = [];
}*/

                                value = this._term(inFunction);

                                if (value === null) {
                                    break;
                                } else {
                                    values.push(value);
                                }
                            } while (true);
                        }

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

                    _term: function(inFunction) {

                        var tokenStream = this._tokenStream,
                            unary = null,
                            value = null,
                            endChar = null,
                            token,
                            line,
                            col;
                        unary = this._unary_operator();
                        if (unary !== null) {
                            line = tokenStream.token().startLine;
                            col = tokenStream.token().startCol;
                        }
                        if (tokenStream.peek() == Tokens.IE_FUNCTION && this.options.ieFilters) {

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

                            token = tokenStream.token();
                            endChar = token.endChar;
                            value = token.value + this._expr(inFunction).text;
                            if (unary === null) {
                                line = tokenStream.token().startLine;
                                col = tokenStream.token().startCol;
                            }
                            tokenStream.mustMatch(Tokens.type(endChar));
                            value += endChar;
                            this._readWhitespace();
                        } else if (tokenStream.match([Tokens.NUMBER, Tokens.PERCENTAGE, Tokens.LENGTH,
                            Tokens.ANGLE, Tokens.TIME,
                            Tokens.FREQ, Tokens.STRING, Tokens.IDENT, Tokens.URI, Tokens.UNICODE_RANGE
                        ])) {

                            value = tokenStream.token().value;
                            if (unary === null) {
                                line = tokenStream.token().startLine;
                                col = tokenStream.token().startCol;
                            }
                            this._readWhitespace();
                        } else {
                            token = this._hexcolor();
                            if (token === null) {
                                if (unary === null) {
                                    line = tokenStream.LT(1).startLine;
                                    col = tokenStream.LT(1).startCol;
                                }
                                if (value === null) {
                                    if (tokenStream.LA(3) == Tokens.EQUALS && this.options.ieFilters) {
                                        value = this._ie_function();
                                    } else {
                                        value = this._function();
                                    }
                                }

                            } else {
                                value = token.value;
                                if (unary === null) {
                                    line = token.startLine;
                                    col = token.startCol;
                                }
                            }

                        }

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

                    },

                    _function: function() {

                        var tokenStream = this._tokenStream,
                            functionText = null,
                            expr = null,
                            lt;

                        if (tokenStream.match(Tokens.FUNCTION)) {
                            functionText = tokenStream.token().value;
                            this._readWhitespace();
                            expr = this._expr(true);
                            functionText += expr;
                            if (this.options.ieFilters && tokenStream.peek() == Tokens.EQUALS) {
                                do {

                                    if (this._readWhitespace()) {
                                        functionText += tokenStream.token().value;
                                    }
                                    if (tokenStream.LA(0) == Tokens.COMMA) {
                                        functionText += tokenStream.token().value;
                                    }

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

                                    tokenStream.match(Tokens.EQUALS);
                                    functionText += tokenStream.token().value;
                                    lt = tokenStream.peek();
                                    while (lt != Tokens.COMMA && lt != Tokens.S && lt != Tokens.RPAREN) {
                                        tokenStream.get();
                                        functionText += tokenStream.token().value;
                                        lt = tokenStream.peek();
                                    }
                                } while (tokenStream.match([Tokens.COMMA, Tokens.S]));
                            }

                            tokenStream.match(Tokens.RPAREN);
                            functionText += ")";
                            this._readWhitespace();
                        }

                        return functionText;
                    },

                    _ie_function: function() {

                        var tokenStream = this._tokenStream,
                            functionText = null,
                            expr = null,
                            lt;
                        if (tokenStream.match([Tokens.IE_FUNCTION, Tokens.FUNCTION])) {
                            functionText = tokenStream.token().value;

                            do {

                                if (this._readWhitespace()) {
                                    functionText += tokenStream.token().value;
                                }
                                if (tokenStream.LA(0) == Tokens.COMMA) {
                                    functionText += tokenStream.token().value;
                                }

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

                                tokenStream.match(Tokens.EQUALS);
                                functionText += tokenStream.token().value;
                                lt = tokenStream.peek();
                                while (lt != Tokens.COMMA && lt != Tokens.S && lt != Tokens.RPAREN) {
                                    tokenStream.get();
                                    functionText += tokenStream.token().value;
                                    lt = tokenStream.peek();
                                }
                            } while (tokenStream.match([Tokens.COMMA, Tokens.S]));

                            tokenStream.match(Tokens.RPAREN);
                            functionText += ")";
                            this._readWhitespace();
                        }

                        return functionText;
                    },

                    _hexcolor: function() {

                        var tokenStream = this._tokenStream,
                            token = null,
                            color;

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

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

                        return token;
                    },

                    _keyframes: function() {
                        var tokenStream = this._tokenStream,
                            token,
                            tt,
                            name,
                            prefix = "";

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

                        this._readWhitespace();
                        name = this._keyframe_name();

                        this._readWhitespace();
                        tokenStream.mustMatch(Tokens.LBRACE);

                        this.fire({
                            type: "startkeyframes",
                            name: name,
                            prefix: prefix,
                            line: token.startLine,
                            col: token.startCol
                        });

                        this._readWhitespace();
                        tt = tokenStream.peek();
                        while (tt == Tokens.IDENT || tt == Tokens.PERCENTAGE) {
                            this._keyframe_rule();
                            this._readWhitespace();
                            tt = tokenStream.peek();
                        }

                        this.fire({
                            type: "endkeyframes",
                            name: name,
                            prefix: prefix,
                            line: token.startLine,
                            col: token.startCol
                        });

                        this._readWhitespace();
                        tokenStream.mustMatch(Tokens.RBRACE);

                    },

                    _keyframe_name: function() {
                        var tokenStream = this._tokenStream,
                            token;

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

                    _keyframe_rule: function() {
                        var tokenStream = this._tokenStream,
                            token,
                            keyList = this._key_list();

                        this.fire({
                            type: "startkeyframerule",
                            keys: keyList,
                            line: keyList[0].line,
                            col: keyList[0].col
                        });

                        this._readDeclarations(true);

                        this.fire({
                            type: "endkeyframerule",
                            keys: keyList,
                            line: keyList[0].line,
                            col: keyList[0].col
                        });

                    },

                    _key_list: function() {
                        var tokenStream = this._tokenStream,
                            token,
                            key,
                            keyList = [];
                        keyList.push(this._key());

                        this._readWhitespace();

                        while (tokenStream.match(Tokens.COMMA)) {
                            this._readWhitespace();
                            keyList.push(this._key());
                            this._readWhitespace();
                        }

                        return keyList;
                    },

                    _key: function() {

                        var tokenStream = this._tokenStream,
                            token;

                        if (tokenStream.match(Tokens.PERCENTAGE)) {
                            return SyntaxUnit.fromToken(tokenStream.token());
                        } else if (tokenStream.match(Tokens.IDENT)) {
                            token = tokenStream.token();

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

                            tokenStream.unget();
                        }
                        this._unexpectedToken(tokenStream.LT(1));
                    },
                    _skipCruft: function() {
                        while (this._tokenStream.match([Tokens.S, Tokens.CDO, Tokens.CDC])) {}
                    },
                    _readDeclarations: function(checkStart, readMargins) {
                        var tokenStream = this._tokenStream,
                            tt;


                        this._readWhitespace();

                        if (checkStart) {
                            tokenStream.mustMatch(Tokens.LBRACE);
                        }

                        this._readWhitespace();

                        try {

                            while (true) {

                                if (tokenStream.match(Tokens.SEMICOLON) || (readMargins && this._margin())) {} else if (this._declaration()) {
                                    if (!tokenStream.match(Tokens.SEMICOLON)) {
                                        break;
                                    }
                                } else {
                                    break;
                                }
                                this._readWhitespace();
                            }

                            tokenStream.mustMatch(Tokens.RBRACE);
                            this._readWhitespace();

                        } catch (ex) {
                            if (ex instanceof SyntaxError && !this.options.strict) {
                                this.fire({
                                    type: "error",
                                    error: ex,
                                    message: ex.message,
                                    line: ex.line,
                                    col: ex.col
                                });
                                tt = tokenStream.advance([Tokens.SEMICOLON, Tokens.RBRACE]);
                                if (tt == Tokens.SEMICOLON) {
                                    this._readDeclarations(false, readMargins);
                                } else if (tt != Tokens.RBRACE) {
                                    throw ex;
                                }

                            } else {
                                throw ex;
                            }
                        }

                    },
                    _readWhitespace: function() {

                        var tokenStream = this._tokenStream,
                            ws = "";

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

                        return ws;
                    },
                    _unexpectedToken: function(token) {
                        throw new SyntaxError("Unexpected token '" + token.value + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
                    },
                    _verifyEnd: function() {
                        if (this._tokenStream.LA(1) != Tokens.EOF) {
                            this._unexpectedToken(this._tokenStream.LT(1));
                        }
                    },
                    _validateProperty: function(property, value) {
                        Validation.validate(property, value);
                    },

                    parse: function(input) {
                        this._tokenStream = new TokenStream(input, Tokens);
                        this._stylesheet();
                    },

                    parseStyleSheet: function(input) {
                        return this.parse(input);
                    },

                    parseMediaQuery: function(input) {
                        this._tokenStream = new TokenStream(input, Tokens);
                        var result = this._media_query();
                        this._verifyEnd();
                        return result;
                    },
                    parsePropertyValue: function(input) {

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

                        var result = this._expr();
                        this._readWhitespace();
                        this._verifyEnd();
                        return result;
                    },
                    parseRule: function(input) {
                        this._tokenStream = new TokenStream(input, Tokens);
                        this._readWhitespace();

                        var result = this._ruleset();
                        this._readWhitespace();
                        this._verifyEnd();
                        return result;
                    },
                    parseSelector: function(input) {

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

                        var result = this._selector();
                        this._readWhitespace();
                        this._verifyEnd();
                        return result;
                    },
                    parseStyleAttribute: function(input) {
                        input += "}"; // for error recovery in _readDeclarations()
                        this._tokenStream = new TokenStream(input, Tokens);
                        this._readDeclarations();
                    }
                };
            for (prop in additions) {
                if (additions.hasOwnProperty(prop)) {
                    proto[prop] = additions[prop];
                }
            }

            return proto;
        }();
        var Properties = {
            "align-items": "flex-start | flex-end | center | baseline | stretch",
            "align-content": "flex-start | flex-end | center | space-between | space-around | stretch",
            "align-self": "auto | flex-start | flex-end | center | baseline | stretch",
            "-webkit-align-items": "flex-start | flex-end | center | baseline | stretch",
            "-webkit-align-content": "flex-start | flex-end | center | space-between | space-around | stretch",
            "-webkit-align-self": "auto | flex-start | flex-end | center | baseline | stretch",
            "alignment-adjust": "auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | <percentage> | <length>",
            "alignment-baseline": "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
            "animation": 1,
            "animation-delay": {
                multi: "<time>",
                comma: true
            },
            "animation-direction": {
                multi: "normal | alternate",
                comma: true
            },
            "animation-duration": {
                multi: "<time>",
                comma: true
            },
            "animation-fill-mode": {
                multi: "none | forwards | backwards | both",
                comma: true
            },
            "animation-iteration-count": {
                multi: "<number> | infinite",
                comma: true
            },
            "animation-name": {
                multi: "none | <ident>",
                comma: true
            },
            "animation-play-state": {
                multi: "running | paused",
                comma: true
            },
            "animation-timing-function": 1,
            "-moz-animation-delay": {
                multi: "<time>",
                comma: true
            },
            "-moz-animation-direction": {
                multi: "normal | alternate",
                comma: true
            },
            "-moz-animation-duration": {
                multi: "<time>",
                comma: true
            },
            "-moz-animation-iteration-count": {
                multi: "<number> | infinite",
                comma: true
            },
            "-moz-animation-name": {
                multi: "none | <ident>",
                comma: true
            },
            "-moz-animation-play-state": {
                multi: "running | paused",
                comma: true
            },

            "-ms-animation-delay": {
                multi: "<time>",
                comma: true
            },
            "-ms-animation-direction": {
                multi: "normal | alternate",
                comma: true
            },
            "-ms-animation-duration": {
                multi: "<time>",
                comma: true
            },
            "-ms-animation-iteration-count": {
                multi: "<number> | infinite",
                comma: true
            },
            "-ms-animation-name": {
                multi: "none | <ident>",
                comma: true
            },
            "-ms-animation-play-state": {
                multi: "running | paused",
                comma: true
            },

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

            "-o-animation-delay": {
                multi: "<time>",
                comma: true
            },
            "-o-animation-direction": {
                multi: "normal | alternate",
                comma: true
            },
            "-o-animation-duration": {
                multi: "<time>",
                comma: true
            },
            "-o-animation-iteration-count": {
                multi: "<number> | infinite",
                comma: true
            },
            "-o-animation-name": {
                multi: "none | <ident>",
                comma: true
            },
            "-o-animation-play-state": {
                multi: "running | paused",
                comma: true
            },

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

                if (!ValidationTypes.isAny(expression, simple)) {
                    if (ValidationTypes.isAny(expression, "behind")) {
                        behind = true;
                        valid = true;
                    }

                    if (ValidationTypes.isAny(expression, direction)) {
                        valid = true;
                        if (!behind) {
                            ValidationTypes.isAny(expression, "behind");
                        }
                    }
                }

                if (expression.hasNext()) {
                    part = expression.next();
                    if (valid) {
                        throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
                    } else {
                        throw new ValidationError("Expected (<'azimuth'>) but found '" + part + "'.", part.line, part.col);
                    }
                }
            },
            "backface-visibility": "visible | hidden",
            "background": 1,
            "background-attachment": {
                multi: "<attachment>",
                comma: true
            },
            "background-clip": {
                multi: "<box>",
                comma: true
            },
            "background-color": "<color> | inherit",
            "background-image": {
                multi: "<bg-image>",
                comma: true
            },
            "background-origin": {
                multi: "<box>",
                comma: true
            },
            "background-position": {
                multi: "<bg-position>",
                comma: true
            },
            "background-repeat": {
                multi: "<repeat-style>"
            },
            "background-size": {
                multi: "<bg-size>",
                comma: true
            },
            "baseline-shift": "baseline | sub | super | <percentage> | <length>",
            "behavior": 1,
            "binding": 1,
            "bleed": "<length>",
            "bookmark-label": "<content> | <attr> | <string>",
            "bookmark-level": "none | <integer>",
            "bookmark-state": "open | closed",
            "bookmark-target": "none | <uri> | <attr>",
            "border": "<border-width> || <border-style> || <color>",
            "border-bottom": "<border-width> || <border-style> || <color>",
            "border-bottom-color": "<color> | inherit",
            "border-bottom-left-radius": "<x-one-radius>",
            "border-bottom-right-radius": "<x-one-radius>",
            "border-bottom-style": "<border-style>",
            "border-bottom-width": "<border-width>",
            "border-collapse": "collapse | separate | inherit",
            "border-color": {
                multi: "<color> | inherit",
                max: 4
            },
            "border-image": 1,
            "border-image-outset": {
                multi: "<length> | <number>",
                max: 4
            },
            "border-image-repeat": {
                multi: "stretch | repeat | round",
                max: 2
            },
            "border-image-slice": function(expression) {

                var valid = false,
                    numeric = "<number> | <percentage>",
                    fill = false,
                    count = 0,
                    max = 4,
                    part;

                if (ValidationTypes.isAny(expression, "fill")) {
                    fill = true;
                    valid = true;
                }

                while (expression.hasNext() && count < max) {
                    valid = ValidationTypes.isAny(expression, numeric);
                    if (!valid) {
                        break;
                    }
                    count++;
                }


                if (!fill) {
                    ValidationTypes.isAny(expression, "fill");
                } else {
                    valid = true;
                }

                if (expression.hasNext()) {
                    part = expression.next();
                    if (valid) {
                        throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
                    } else {
                        throw new ValidationError("Expected ([<number> | <percentage>]{1,4} && fill?) but found '" + part + "'.", part.line, part.col);
                    }
                }
            },
            "border-image-source": "<image> | none",
            "border-image-width": {
                multi: "<length> | <percentage> | <number> | auto",
                max: 4
            },
            "border-left": "<border-width> || <border-style> || <color>",
            "border-left-color": "<color> | inherit",
            "border-left-style": "<border-style>",
            "border-left-width": "<border-width>",
            "border-radius": function(expression) {

                var valid = false,
                    simple = "<length> | <percentage> | inherit",
                    slash = false,
                    fill = false,
                    count = 0,
                    max = 8,
                    part;

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

                        if (expression.peek() == "/" && count > 0 && !slash) {
                            slash = true;
                            max = count + 5;
                            expression.next();
                        } else {
                            break;
                        }
                    }
                    count++;
                }

                if (expression.hasNext()) {
                    part = expression.next();
                    if (valid) {
                        throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
                    } else {
                        throw new ValidationError("Expected (<'border-radius'>) but found '" + part + "'.", part.line, part.col);
                    }
                }
            },
            "border-right": "<border-width> || <border-style> || <color>",
            "border-right-color": "<color> | inherit",
            "border-right-style": "<border-style>",
            "border-right-width": "<border-width>",
            "border-spacing": {
                multi: "<length> | inherit",
                max: 2
            },
            "border-style": {
                multi: "<border-style>",
                max: 4
            },
            "border-top": "<border-width> || <border-style> || <color>",
            "border-top-color": "<color> | inherit",
            "border-top-left-radius": "<x-one-radius>",
            "border-top-right-radius": "<x-one-radius>",
            "border-top-style": "<border-style>",
            "border-top-width": "<border-width>",
            "border-width": {
                multi: "<border-width>",
                max: 4
            },
            "bottom": "<margin-width> | inherit",
            "-moz-box-align": "start | end | center | baseline | stretch",
            "-moz-box-decoration-break": "slice |clone",
            "-moz-box-direction": "normal | reverse | inherit",
            "-moz-box-flex": "<number>",
            "-moz-box-flex-group": "<integer>",
            "-moz-box-lines": "single | multiple",
            "-moz-box-ordinal-group": "<integer>",
            "-moz-box-orient": "horizontal | vertical | inline-axis | block-axis | inherit",
            "-moz-box-pack": "start | end | center | justify",
            "-webkit-box-align": "start | end | center | baseline | stretch",
            "-webkit-box-decoration-break": "slice |clone",
            "-webkit-box-direction": "normal | reverse | inherit",
            "-webkit-box-flex": "<number>",
            "-webkit-box-flex-group": "<integer>",
            "-webkit-box-lines": "single | multiple",
            "-webkit-box-ordinal-group": "<integer>",
            "-webkit-box-orient": "horizontal | vertical | inline-axis | block-axis | inherit",
            "-webkit-box-pack": "start | end | center | justify",
            "box-shadow": function(expression) {
                var result = false,
                    part;

                if (!ValidationTypes.isAny(expression, "none")) {
                    Validation.multiProperty("<shadow>", expression, true, Infinity);
                } else {
                    if (expression.hasNext()) {
                        part = expression.next();
                        throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
                    }
                }
            },
            "box-sizing": "content-box | border-box | inherit",
            "break-after": "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
            "break-before": "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
            "break-inside": "auto | avoid | avoid-page | avoid-column",
            "caption-side": "top | bottom | inherit",
            "clear": "none | right | left | both | inherit",
            "clip": 1,
            "color": "<color> | inherit",
            "color-profile": 1,
            "column-count": "<integer> | auto", //http://www.w3.org/TR/css3-multicol/
            "column-fill": "auto | balance",
            "column-gap": "<length> | normal",
            "column-rule": "<border-width> || <border-style> || <color>",
            "column-rule-color": "<color>",
            "column-rule-style": "<border-style>",
            "column-rule-width": "<border-width>",
            "column-span": "none | all",
            "column-width": "<length> | auto",
            "columns": 1,
            "content": 1,
            "counter-increment": 1,
            "counter-reset": 1,
            "crop": "<shape> | auto",
            "cue": "cue-after | cue-before | inherit",
            "cue-after": 1,
            "cue-before": 1,
            "cursor": 1,
            "direction": "ltr | rtl | inherit",
            "display": "inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | grid | inline-grid | none | inherit | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker | -webkit-box | -webkit-inline-box | -ms-flexbox | -ms-inline-flexbox | flex | -webkit-flex | inline-flex | -webkit-inline-flex",
            "dominant-baseline": 1,
            "drop-initial-after-adjust": "central | middle | after-edge | text-after-edge | ideographic | alphabetic | mathematical | <percentage> | <length>",
            "drop-initial-after-align": "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
            "drop-initial-before-adjust": "before-edge | text-before-edge | central | middle | hanging | mathematical | <percentage> | <length>",
            "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",
            "drop-initial-size": "auto | line | <length> | <percentage>",
            "drop-initial-value": "initial | <integer>",
            "elevation": "<angle> | below | level | above | higher | lower | inherit",
            "empty-cells": "show | hide | inherit",
            "filter": 1,
            "fit": "fill | hidden | meet | slice",
            "fit-position": 1,
            "flex": "<flex>",
            "flex-basis": "<width>",
            "flex-direction": "row | row-reverse | column | column-reverse",
            "flex-flow": "<flex-direction> || <flex-wrap>",
            "flex-grow": "<number>",
            "flex-shrink": "<number>",
            "flex-wrap": "nowrap | wrap | wrap-reverse",
            "-webkit-flex": "<flex>",
            "-webkit-flex-basis": "<width>",
            "-webkit-flex-direction": "row | row-reverse | column | column-reverse",
            "-webkit-flex-flow": "<flex-direction> || <flex-wrap>",
            "-webkit-flex-grow": "<number>",
            "-webkit-flex-shrink": "<number>",
            "-webkit-flex-wrap": "nowrap | wrap | wrap-reverse",
            "-ms-flex": "<flex>",
            "-ms-flex-align": "start | end | center | stretch | baseline",
            "-ms-flex-direction": "row | row-reverse | column | column-reverse | inherit",
            "-ms-flex-order": "<number>",
            "-ms-flex-pack": "start | end | center | justify",
            "-ms-flex-wrap": "nowrap | wrap | wrap-reverse",
            "float": "left | right | none | inherit",
            "float-offset": 1,
            "font": 1,
            "font-family": 1,
            "font-size": "<absolute-size> | <relative-size> | <length> | <percentage> | inherit",
            "font-size-adjust": "<number> | none | inherit",
            "font-stretch": "normal | ultra-condensed | extra-condensed | condensed | semi-condensed | semi-expanded | expanded | extra-expanded | ultra-expanded | inherit",
            "font-style": "normal | italic | oblique | inherit",
            "font-variant": "normal | small-caps | inherit",
            "font-weight": "normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | inherit",
            "grid-cell-stacking": "columns | rows | layer",
            "grid-column": 1,
            "grid-columns": 1,
            "grid-column-align": "start | end | center | stretch",
            "grid-column-sizing": 1,
            "grid-column-span": "<integer>",
            "grid-flow": "none | rows | columns",
            "grid-layer": "<integer>",
            "grid-row": 1,
            "grid-rows": 1,
            "grid-row-align": "start | end | center | stretch",
            "grid-row-span": "<integer>",
            "grid-row-sizing": 1,
            "hanging-punctuation": 1,
            "height": "<margin-width> | <content-sizing> | inherit",
            "hyphenate-after": "<integer> | auto",
            "hyphenate-before": "<integer> | auto",
            "hyphenate-character": "<string> | auto",
            "hyphenate-lines": "no-limit | <integer>",
            "hyphenate-resource": 1,
            "hyphens": "none | manual | auto",
            "icon": 1,
            "image-orientation": "angle | auto",
            "image-rendering": 1,
            "image-resolution": 1,
            "inline-box-align": "initial | last | <integer>",
            "justify-content": "flex-start | flex-end | center | space-between | space-around",
            "-webkit-justify-content": "flex-start | flex-end | center | space-between | space-around",
            "left": "<margin-width> | inherit",
            "letter-spacing": "<length> | normal | inherit",
            "line-height": "<number> | <length> | <percentage> | normal | inherit",
            "line-break": "auto | loose | normal | strict",
            "line-stacking": 1,
            "line-stacking-ruby": "exclude-ruby | include-ruby",
            "line-stacking-shift": "consider-shifts | disregard-shifts",
            "line-stacking-strategy": "inline-line-height | block-line-height | max-height | grid-height",
            "list-style": 1,
            "list-style-image": "<uri> | none | inherit",
            "list-style-position": "inside | outside | inherit",
            "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",
            "margin": {
                multi: "<margin-width> | inherit",
                max: 4
            },
            "margin-bottom": "<margin-width> | inherit",
            "margin-left": "<margin-width> | inherit",
            "margin-right": "<margin-width> | inherit",
            "margin-top": "<margin-width> | inherit",
            "mark": 1,
            "mark-after": 1,
            "mark-before": 1,
            "marks": 1,
            "marquee-direction": 1,
            "marquee-play-count": 1,
            "marquee-speed": 1,
            "marquee-style": 1,
            "max-height": "<length> | <percentage> | <content-sizing> | none | inherit",
            "max-width": "<length> | <percentage> | <content-sizing> | none | inherit",
            "min-height": "<length> | <percentage> | <content-sizing> | contain-floats | -moz-contain-floats | -webkit-contain-floats | inherit",
            "min-width": "<length> | <percentage> | <content-sizing> | contain-floats | -moz-contain-floats | -webkit-contain-floats | inherit",
            "move-to": 1,
            "nav-down": 1,
            "nav-index": 1,
            "nav-left": 1,
            "nav-right": 1,
            "nav-up": 1,
            "opacity": "<number> | inherit",
            "order": "<integer>",
            "-webkit-order": "<integer>",
            "orphans": "<integer> | inherit",
            "outline": 1,
            "outline-color": "<color> | invert | inherit",
            "outline-offset": 1,
            "outline-style": "<border-style> | inherit",
            "outline-width": "<border-width> | inherit",
            "overflow": "visible | hidden | scroll | auto | inherit",
            "overflow-style": 1,
            "overflow-wrap": "normal | break-word",
            "overflow-x": 1,
            "overflow-y": 1,
            "padding": {
                multi: "<padding-width> | inherit",
                max: 4
            },
            "padding-bottom": "<padding-width> | inherit",
            "padding-left": "<padding-width> | inherit",
            "padding-right": "<padding-width> | inherit",
            "padding-top": "<padding-width> | inherit",
            "page": 1,
            "page-break-after": "auto | always | avoid | left | right | inherit",
            "page-break-before": "auto | always | avoid | left | right | inherit",
            "page-break-inside": "auto | avoid | inherit",
            "page-policy": 1,
            "pause": 1,
            "pause-after": 1,
            "pause-before": 1,
            "perspective": 1,
            "perspective-origin": 1,
            "phonemes": 1,
            "pitch": 1,
            "pitch-range": 1,
            "play-during": 1,
            "pointer-events": "auto | none | visiblePainted | visibleFill | visibleStroke | visible | painted | fill | stroke | all | inherit",
            "position": "static | relative | absolute | fixed | inherit",
            "presentation-level": 1,
            "punctuation-trim": 1,
            "quotes": 1,
            "rendering-intent": 1,
            "resize": 1,
            "rest": 1,
            "rest-after": 1,
            "rest-before": 1,
            "richness": 1,
            "right": "<margin-width> | inherit",
            "rotation": 1,
            "rotation-point": 1,
            "ruby-align": 1,
            "ruby-overhang": 1,
            "ruby-position": 1,
            "ruby-span": 1,
            "size": 1,
            "speak": "normal | none | spell-out | inherit",
            "speak-header": "once | always | inherit",
            "speak-numeral": "digits | continuous | inherit",
            "speak-punctuation": "code | none | inherit",
            "speech-rate": 1,
            "src": 1,
            "stress": 1,
            "string-set": 1,

            "table-layout": "auto | fixed | inherit",
            "tab-size": "<integer> | <length>",
            "target": 1,
            "target-name": 1,
            "target-new": 1,
            "target-position": 1,
            "text-align": "left | right | center | justify | inherit",
            "text-align-last": 1,
            "text-decoration": 1,
            "text-emphasis": 1,
            "text-height": 1,
            "text-indent": "<length> | <percentage> | inherit",
            "text-justify": "auto | none | inter-word | inter-ideograph | inter-cluster | distribute | kashida",
            "text-outline": 1,
            "text-overflow": 1,
            "text-rendering": "auto | optimizeSpeed | optimizeLegibility | geometricPrecision | inherit",
            "text-shadow": 1,
            "text-transform": "capitalize | uppercase | lowercase | none | inherit",
            "text-wrap": "normal | none | avoid",
            "top": "<margin-width> | inherit",
            "-ms-touch-action": "auto | none | pan-x | pan-y",
            "touch-action": "auto | none | pan-x | pan-y",
            "transform": 1,
            "transform-origin": 1,
            "transform-style": 1,
            "transition": 1,
            "transition-delay": 1,
            "transition-duration": 1,
            "transition-property": 1,
            "transition-timing-function": 1,
            "unicode-bidi": "normal | embed | isolate | bidi-override | isolate-override | plaintext | inherit",
            "user-modify": "read-only | read-write | write-only | inherit",
            "user-select": "none | text | toggle | element | elements | all | inherit",
            "vertical-align": "auto | use-script | baseline | sub | super | top | text-top | central | middle | bottom | text-bottom | <percentage> | <length>",
            "visibility": "visible | hidden | collapse | inherit",
            "voice-balance": 1,
            "voice-duration": 1,
            "voice-family": 1,
            "voice-pitch": 1,
            "voice-pitch-range": 1,
            "voice-rate": 1,
            "voice-stress": 1,
            "voice-volume": 1,
            "volume": 1,
            "white-space": "normal | pre | nowrap | pre-wrap | pre-line | inherit | -pre-wrap | -o-pre-wrap | -moz-pre-wrap | -hp-pre-wrap", //http://perishablepress.com/wrapping-content/
            "white-space-collapse": 1,
            "widows": "<integer> | inherit",
            "width": "<length> | <percentage> | <content-sizing> | auto | inherit",
            "word-break": "normal | keep-all | break-all",
            "word-spacing": "<length> | normal | inherit",
            "word-wrap": "normal | break-word",
            "writing-mode": "horizontal-tb | vertical-rl | vertical-lr | lr-tb | rl-tb | tb-rl | bt-rl | tb-lr | bt-lr | lr-bt | rl-bt | lr | rl | tb | inherit",
            "z-index": "<integer> | auto | inherit",
            "zoom": "<number> | <percentage> | normal"
        };

        function PropertyName(text, hack, line, col) {

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

        }

        PropertyName.prototype = new SyntaxUnit();
        PropertyName.prototype.constructor = PropertyName;
        PropertyName.prototype.toString = function() {
            return (this.hack ? this.hack : "") + this.text;
        };

        function PropertyValue(parts, line, col) {

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

        }

        PropertyValue.prototype = new SyntaxUnit();
        PropertyValue.prototype.constructor = PropertyValue;

        function PropertyValueIterator(value) {
            this._i = 0;
            this._parts = value.parts;
            this._marks = [];
            this.value = value;

        }
        PropertyValueIterator.prototype.count = function() {
            return this._parts.length;
        };
        PropertyValueIterator.prototype.isFirst = function() {
            return this._i === 0;
        };
        PropertyValueIterator.prototype.hasNext = function() {
            return (this._i < this._parts.length);
        };
        PropertyValueIterator.prototype.mark = function() {
            this._marks.push(this._i);
        };
        PropertyValueIterator.prototype.peek = function(count) {
            return this.hasNext() ? this._parts[this._i + (count || 0)] : null;
        };
        PropertyValueIterator.prototype.next = function() {
            return this.hasNext() ? this._parts[this._i++] : null;
        };
        PropertyValueIterator.prototype.previous = function() {
            return this._i > 0 ? this._parts[--this._i] : null;
        };
        PropertyValueIterator.prototype.restore = function() {
            if (this._marks.length) {
                this._i = this._marks.pop();
            }
        };

        function PropertyValuePart(text, line, col) {

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

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

                    case "em":
                    case "rem":
                    case "ex":
                    case "px":
                    case "cm":
                    case "mm":
                    case "in":
                    case "pt":
                    case "pc":
                    case "ch":
                    case "vh":
                    case "vw":
                    case "vmax":
                    case "vmin":
                        this.type = "length";
                        break;

                    case "deg":
                    case "rad":
                    case "grad":
                        this.type = "angle";
                        break;

                    case "ms":
                    case "s":
                        this.type = "time";
                        break;

                    case "hz":
                    case "khz":
                        this.type = "frequency";
                        break;

                    case "dpi":
                    case "dpcm":
                        this.type = "resolution";
                        break;

                }

            } else if (/^([+\-]?[\d\.]+)%$/i.test(text)) { //percentage
                this.type = "percentage";
                this.value = +RegExp.$1;
            } else if (/^([+\-]?\d+)$/i.test(text)) { //integer
                this.type = "integer";
                this.value = +RegExp.$1;
            } else if (/^([+\-]?[\d\.]+)$/i.test(text)) { //number
                this.type = "number";
                this.value = +RegExp.$1;

            } else if (/^#([a-f0-9]{3,6})/i.test(text)) { //hexcolor
                this.type = "color";
                temp = RegExp.$1;
                if (temp.length == 3) {
                    this.red = parseInt(temp.charAt(0) + temp.charAt(0), 16);
                    this.green = parseInt(temp.charAt(1) + temp.charAt(1), 16);
                    this.blue = parseInt(temp.charAt(2) + temp.charAt(2), 16);
                } else {
                    this.red = parseInt(temp.substring(0, 2), 16);
                    this.green = parseInt(temp.substring(2, 4), 16);
                    this.blue = parseInt(temp.substring(4, 6), 16);
                }
            } else if (/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i.test(text)) { //rgb() color with absolute numbers
                this.type = "color";
                this.red = +RegExp.$1;
                this.green = +RegExp.$2;
                this.blue = +RegExp.$3;
            } else if (/^rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)) { //rgb() color with percentages
                this.type = "color";
                this.red = +RegExp.$1 * 255 / 100;
                this.green = +RegExp.$2 * 255 / 100;
                this.blue = +RegExp.$3 * 255 / 100;
            } else if (/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //rgba() color with absolute numbers
                this.type = "color";
                this.red = +RegExp.$1;
                this.green = +RegExp.$2;
                this.blue = +RegExp.$3;
                this.alpha = +RegExp.$4;
            } else if (/^rgba\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //rgba() color with percentages
                this.type = "color";
                this.red = +RegExp.$1 * 255 / 100;
                this.green = +RegExp.$2 * 255 / 100;
                this.blue = +RegExp.$3 * 255 / 100;
                this.alpha = +RegExp.$4;
            } else if (/^hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)) { //hsl()
                this.type = "color";
                this.hue = +RegExp.$1;
                this.saturation = +RegExp.$2 / 100;
                this.lightness = +RegExp.$3 / 100;
            } else if (/^hsla\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //hsla() color with percentages
                this.type = "color";
                this.hue = +RegExp.$1;
                this.saturation = +RegExp.$2 / 100;
                this.lightness = +RegExp.$3 / 100;
                this.alpha = +RegExp.$4;
            } else if (/^url\(["']?([^\)"']+)["']?\)/i.test(text)) { //URI
                this.type = "uri";
                this.uri = RegExp.$1;
            } else if (/^([^\(]+)\(/i.test(text)) {
                this.type = "function";
                this.name = RegExp.$1;
                this.value = text;
            } else if (/^["'][^"']*["']/.test(text)) { //string
                this.type = "string";
                this.value = eval(text);
            } else if (Colors[text.toLowerCase()]) { //named color
                this.type = "color";
                temp = Colors[text.toLowerCase()].substring(1);
                this.red = parseInt(temp.substring(0, 2), 16);
                this.green = parseInt(temp.substring(2, 4), 16);
                this.blue = parseInt(temp.substring(4, 6), 16);
            } else if (/^[\,\/]$/.test(text)) {
                this.type = "operator";
                this.value = text;
            } else if (/^[a-z\-_\u0080-\uFFFF][a-z0-9\-_\u0080-\uFFFF]*$/i.test(text)) {
                this.type = "identifier";
                this.value = text;
            }

        }

        PropertyValuePart.prototype = new SyntaxUnit();
        PropertyValuePart.prototype.constructor = PropertyValuePart;
        PropertyValuePart.fromToken = function(token) {
            return new PropertyValuePart(token.value, token.startLine, token.startCol);
        };
        var Pseudos = {
            ":first-letter": 1,
            ":first-line": 1,
            ":before": 1,
            ":after": 1
        };

        Pseudos.ELEMENT = 1;
        Pseudos.CLASS = 2;

        Pseudos.isElement = function(pseudo) {
            return pseudo.indexOf("::") === 0 || Pseudos[pseudo.toLowerCase()] == Pseudos.ELEMENT;
        };

        function Selector(parts, line, col) {

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

        }

        Selector.prototype = new SyntaxUnit();
        Selector.prototype.constructor = Selector;

        function SelectorPart(elementName, modifiers, text, line, col) {

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

        }

        SelectorPart.prototype = new SyntaxUnit();
        SelectorPart.prototype.constructor = SelectorPart;

        function SelectorSubPart(text, type, line, col) {

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

        }

        SelectorSubPart.prototype = new SyntaxUnit();
        SelectorSubPart.prototype.constructor = SelectorSubPart;

        function Specificity(a, b, c, d) {
            this.a = a;
            this.b = b;
            this.c = c;
            this.d = d;
        }

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

                for (i = 0, len = comps.length; i < len; i++) {
                    if (this[comps[i]] < other[comps[i]]) {
                        return -1;
                    } else if (this[comps[i]] > other[comps[i]]) {
                        return 1;
                    }
                }

                return 0;
            },
            valueOf: function() {
                return (this.a * 1000) + (this.b * 100) + (this.c * 10) + this.d;
            },
            toString: function() {
                return this.a + "," + this.b + "," + this.c + "," + this.d;
            }

        };
        Specificity.calculate = function(selector) {

            var i, len,
                part,
                b = 0,
                c = 0,
                d = 0;

            function updateValues(part) {

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

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

                for (i = 0, len = part.modifiers.length; i < len; i++) {
                    modifier = part.modifiers[i];
                    switch (modifier.type) {
                        case "class":
                        case "attribute":
                            c++;
                            break;

                        case "id":
                            b++;
                            break;

                        case "pseudo":
                            if (Pseudos.isElement(modifier.text)) {
                                d++;
                            } else {
                                c++;
                            }
                            break;

                        case "not":
                            for (j = 0, num = modifier.args.length; j < num; j++) {
                                updateValues(modifier.args[j]);
                            }
                    }
                }
            }

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

                if (part instanceof SelectorPart) {
                    updateValues(part);
                }
            }

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

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


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

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

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

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

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

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

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

        function mix(receiver, supplier) {
            for (var prop in supplier) {
                if (supplier.hasOwnProperty(prop)) {
                    receiver[prop] = supplier[prop];
                }
            }
            return receiver;
        }

        function TokenStream(input) {
            TokenStreamBase.call(this, input, Tokens);
        }

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

                var c,
                    reader = this._reader,
                    token = null,
                    startLine = reader.getLine(),
                    startCol = reader.getCol();

                c = reader.read();


                while (c) {
                    switch (c) {
                        case "/":

                            if (reader.peek() == "*") {
                                token = this.commentToken(c, startLine, startCol);
                            } else {
                                token = this.charToken(c, startLine, startCol);
                            }
                            break;
                        case "|":
                        case "~":
                        case "^":
                        case "$":
                        case "*":
                            if (reader.peek() == "=") {
                                token = this.comparisonToken(c, startLine, startCol);
                            } else {
                                token = this.charToken(c, startLine, startCol);
                            }
                            break;
                        case "\"":
                        case "'":
                            token = this.stringToken(c, startLine, startCol);
                            break;
                        case "#":
                            if (isNameChar(reader.peek())) {
                                token = this.hashToken(c, startLine, startCol);
                            } else {
                                token = this.charToken(c, startLine, startCol);
                            }
                            break;
                        case ".":
                            if (isDigit(reader.peek())) {
                                token = this.numberToken(c, startLine, startCol);
                            } else {
                                token = this.charToken(c, startLine, startCol);
                            }
                            break;
                        case "-":
                            if (reader.peek() == "-") { //could be closing HTML-style comment
                                token = this.htmlCommentEndToken(c, startLine, startCol);
                            } else if (isNameStart(reader.peek())) {
                                token = this.identOrFunctionToken(c, startLine, startCol);
                            } else {
                                token = this.charToken(c, startLine, startCol);
                            }
                            break;
                        case "!":
                            token = this.importantToken(c, startLine, startCol);
                            break;
                        case "@":
                            token = this.atRuleToken(c, startLine, startCol);
                            break;
                        case ":":
                            token = this.notToken(c, startLine, startCol);
                            break;
                        case "<":
                            token = this.htmlCommentStartToken(c, startLine, startCol);
                            break;
                        case "U":
                        case "u":
                            if (reader.peek() == "+") {
                                token = this.unicodeRangeToken(c, startLine, startCol);
                                break;
                            }
                        default:
                            if (isDigit(c)) {
                                token = this.numberToken(c, startLine, startCol);
                            } else
                            if (isWhitespace(c)) {
                                token = this.whitespaceToken(c, startLine, startCol);
                            } else
                            if (isIdentStart(c)) {
                                token = this.identOrFunctionToken(c, startLine, startCol);
                            } else {
                                token = this.charToken(c, startLine, startCol);
                            }






                    }
                    break;
                }

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

                return token;
            },
            createToken: function(tt, value, startLine, startCol, options) {
                var reader = this._reader;
                options = options || {};

                return {
                    value: value,
                    type: tt,
                    channel: options.channel,
                    endChar: options.endChar,
                    hide: options.hide || false,
                    startLine: startLine,
                    startCol: startCol,
                    endLine: reader.getLine(),
                    endCol: reader.getCol()
                };
            },
            atRuleToken: function(first, startLine, startCol) {
                var rule = first,
                    reader = this._reader,
                    tt = Tokens.CHAR,
                    valid = false,
                    ident,
                    c;
                reader.mark();
                ident = this.readName();
                rule = first + ident;
                tt = Tokens.type(rule.toLowerCase());
                if (tt == Tokens.CHAR || tt == Tokens.UNKNOWN) {
                    if (rule.length > 1) {
                        tt = Tokens.UNKNOWN_SYM;
                    } else {
                        tt = Tokens.CHAR;
                        rule = first;
                        reader.reset();
                    }
                }

                return this.createToken(tt, rule, startLine, startCol);
            },
            charToken: function(c, startLine, startCol) {
                var tt = Tokens.type(c);
                var opts = {};

                if (tt == -1) {
                    tt = Tokens.CHAR;
                } else {
                    opts.endChar = Tokens[tt].endChar;
                }

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

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

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

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

                reader.mark();
                text += reader.readCount(3);

                if (text == "<!--") {
                    return this.createToken(Tokens.CDO, text, startLine, startCol);
                } else {
                    reader.reset();
                    return this.charToken(first, startLine, startCol);
                }
            },
            htmlCommentEndToken: function(first, startLine, startCol) {
                var reader = this._reader,
                    text = first;

                reader.mark();
                text += reader.readCount(2);

                if (text == "-->") {
                    return this.createToken(Tokens.CDC, text, startLine, startCol);
                } else {
                    reader.reset();
                    return this.charToken(first, startLine, startCol);
                }
            },
            identOrFunctionToken: function(first, startLine, startCol) {
                var reader = this._reader,
                    ident = this.readName(first),
                    tt = Tokens.IDENT;
                if (reader.peek() == "(") {
                    ident += reader.read();
                    if (ident.toLowerCase() == "url(") {
                        tt = Tokens.URI;
                        ident = this.readURI(ident);
                        if (ident.toLowerCase() == "url(") {
                            tt = Tokens.FUNCTION;
                        }
                    } else {
                        tt = Tokens.FUNCTION;
                    }
                } else if (reader.peek() == ":") { //might be an IE function
                    if (ident.toLowerCase() == "progid") {
                        ident += reader.readTo("(");
                        tt = Tokens.IE_FUNCTION;
                    }
                }

                return this.createToken(tt, ident, startLine, startCol);
            },
            importantToken: function(first, startLine, startCol) {
                var reader = this._reader,
                    important = first,
                    tt = Tokens.CHAR,
                    temp,
                    c;

                reader.mark();
                c = reader.read();

                while (c) {
                    if (c == "/") {
                        if (reader.peek() != "*") {
                            break;
                        } else {
                            temp = this.readComment(c);
                            if (temp === "") { //broken!
                                break;
                            }
                        }
                    } else if (isWhitespace(c)) {
                        important += c + this.readWhitespace();
                    } else if (/i/i.test(c)) {
                        temp = reader.readCount(8);
                        if (/mportant/i.test(temp)) {
                            important += c + temp;
                            tt = Tokens.IMPORTANT_SYM;

                        }
                        break; //we're done
                    } else {
                        break;
                    }

                    c = reader.read();
                }

                if (tt == Tokens.CHAR) {
                    reader.reset();
                    return this.charToken(first, startLine, startCol);
                } else {
                    return this.createToken(tt, important, startLine, startCol);
                }


            },
            notToken: function(first, startLine, startCol) {
                var reader = this._reader,
                    text = first;

                reader.mark();
                text += reader.readCount(4);

                if (text.toLowerCase() == ":not(") {
                    return this.createToken(Tokens.NOT, text, startLine, startCol);
                } else {
                    reader.reset();
                    return this.charToken(first, startLine, startCol);
                }
            },
            numberToken: function(first, startLine, startCol) {
                var reader = this._reader,
                    value = this.readNumber(first),
                    ident,
                    tt = Tokens.NUMBER,
                    c = reader.peek();

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

                    if (/^em$|^ex$|^px$|^gd$|^rem$|^vw$|^vh$|^vmax$|^vmin$|^ch$|^cm$|^mm$|^in$|^pt$|^pc$/i.test(ident)) {
                        tt = Tokens.LENGTH;
                    } else if (/^deg|^rad$|^grad$/i.test(ident)) {
                        tt = Tokens.ANGLE;
                    } else if (/^ms$|^s$/i.test(ident)) {
                        tt = Tokens.TIME;
                    } else if (/^hz$|^khz$/i.test(ident)) {
                        tt = Tokens.FREQ;
                    } else if (/^dpi$|^dpcm$/i.test(ident)) {
                        tt = Tokens.RESOLUTION;
                    } else {
                        tt = Tokens.DIMENSION;
                    }

                } else if (c == "%") {
                    value += reader.read();
                    tt = Tokens.PERCENTAGE;
                }

                return this.createToken(tt, value, startLine, startCol);
            },
            stringToken: function(first, startLine, startCol) {
                var delim = first,
                    string = first,
                    reader = this._reader,
                    prev = first,
                    tt = Tokens.STRING,
                    c = reader.read();

                while (c) {
                    string += c;
                    if (c == delim && prev != "\\") {
                        break;
                    }
                    if (isNewLine(reader.peek()) && c != "\\") {
                        tt = Tokens.INVALID;
                        break;
                    }
                    prev = c;
                    c = reader.read();
                }
                if (c === null) {
                    tt = Tokens.INVALID;
                }

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

            unicodeRangeToken: function(first, startLine, startCol) {
                var reader = this._reader,
                    value = first,
                    temp,
                    tt = Tokens.CHAR;
                if (reader.peek() == "+") {
                    reader.mark();
                    value += reader.read();
                    value += this.readUnicodeRangePart(true);
                    if (value.length == 2) {
                        reader.reset();
                    } else {

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

                            if (reader.peek() == "-") {
                                reader.mark();
                                temp = reader.read();
                                temp += this.readUnicodeRangePart(false);
                                if (temp.length == 1) {
                                    reader.reset();
                                } else {
                                    value += temp;
                                }
                            }

                        }
                    }
                }

                return this.createToken(tt, value, startLine, startCol);
            },
            whitespaceToken: function(first, startLine, startCol) {
                var reader = this._reader,
                    value = first + this.readWhitespace();
                return this.createToken(Tokens.S, value, startLine, startCol);
            },

            readUnicodeRangePart: function(allowQuestionMark) {
                var reader = this._reader,
                    part = "",
                    c = reader.peek();
                while (isHexDigit(c) && part.length < 6) {
                    reader.read();
                    part += c;
                    c = reader.peek();
                }
                if (allowQuestionMark) {
                    while (c == "?" && part.length < 6) {
                        reader.read();
                        part += c;
                        c = reader.peek();
                    }
                }

                return part;
            },

            readWhitespace: function() {
                var reader = this._reader,
                    whitespace = "",
                    c = reader.peek();

                while (isWhitespace(c)) {
                    reader.read();
                    whitespace += c;
                    c = reader.peek();
                }

                return whitespace;
            },
            readNumber: function(first) {
                var reader = this._reader,
                    number = first,
                    hasDot = (first == "."),
                    c = reader.peek();


                while (c) {
                    if (isDigit(c)) {
                        number += reader.read();
                    } else if (c == ".") {
                        if (hasDot) {
                            break;
                        } else {
                            hasDot = true;
                            number += reader.read();
                        }
                    } else {
                        break;
                    }

                    c = reader.peek();
                }

                return number;
            },
            readString: function() {
                var reader = this._reader,
                    delim = reader.read(),
                    string = delim,
                    prev = delim,
                    c = reader.peek();

                while (c) {
                    c = reader.read();
                    string += c;
                    if (c == delim && prev != "\\") {
                        break;
                    }
                    if (isNewLine(reader.peek()) && c != "\\") {
                        string = "";
                        break;
                    }
                    prev = c;
                    c = reader.peek();
                }
                if (c === null) {
                    string = "";
                }

                return string;
            },
            readURI: function(first) {
                var reader = this._reader,
                    uri = first,
                    inner = "",
                    c = reader.peek();

                reader.mark();
                while (c && isWhitespace(c)) {
                    reader.read();
                    c = reader.peek();
                }
                if (c == "'" || c == "\"") {
                    inner = this.readString();
                } else {
                    inner = this.readURL();
                }

                c = reader.peek();
                while (c && isWhitespace(c)) {
                    reader.read();
                    c = reader.peek();
                }
                if (inner === "" || c != ")") {
                    uri = first;
                    reader.reset();
                } else {
                    uri += inner + reader.read();
                }

                return uri;
            },
            readURL: function() {
                var reader = this._reader,
                    url = "",
                    c = reader.peek();
                while (/^[!#$%&\\*-~]$/.test(c)) {
                    url += reader.read();
                    c = reader.peek();
                }

                return url;

            },
            readName: function(first) {
                var reader = this._reader,
                    ident = first || "",
                    c = reader.peek();

                while (true) {
                    if (c == "\\") {
                        ident += this.readEscape(reader.read());
                        c = reader.peek();
                    } else if (c && isNameChar(c)) {
                        ident += reader.read();
                        c = reader.peek();
                    } else {
                        break;
                    }
                }

                return ident;
            },

            readEscape: function(first) {
                var reader = this._reader,
                    cssEscape = first || "",
                    i = 0,
                    c = reader.peek();

                if (isHexDigit(c)) {
                    do {
                        cssEscape += reader.read();
                        c = reader.peek();
                    } while (c && isHexDigit(c) && ++i < 6);
                }

                if (cssEscape.length == 3 && /\s/.test(c) ||
                    cssEscape.length == 7 || cssEscape.length == 1) {
                    reader.read();
                } else {
                    c = "";
                }

                return cssEscape + c;
            },

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

                if (c == "*") {
                    while (c) {
                        comment += c;
                        if (comment.length > 2 && c == "*" && reader.peek() == "/") {
                            comment += reader.read();
                            break;
                        }

                        c = reader.read();
                    }

                    return comment;
                } else {
                    return "";
                }

            }
        });

        var Tokens = [{
                name: "CDO"
            }, {
                name: "CDC"
            }, {
                name: "S",
                whitespace: true /*, channel: "ws"*/
            }, {
                name: "COMMENT",
                comment: true,
                hide: true,
                channel: "comment"
            }, {
                name: "INCLUDES",
                text: "~="
            }, {
                name: "DASHMATCH",
                text: "|="
            }, {
                name: "PREFIXMATCH",
                text: "^="
            }, {
                name: "SUFFIXMATCH",
                text: "$="
            }, {
                name: "SUBSTRINGMATCH",
                text: "*="
            }, {
                name: "STRING"
            }, {
                name: "IDENT"
            }, {
                name: "HASH"
            }, {
                name: "IMPORT_SYM",
                text: "@import"
            }, {
                name: "PAGE_SYM",
                text: "@page"
            }, {
                name: "MEDIA_SYM",
                text: "@media"
            }, {
                name: "FONT_FACE_SYM",
                text: "@font-face"
            }, {
                name: "CHARSET_SYM",
                text: "@charset"
            }, {
                name: "NAMESPACE_SYM",
                text: "@namespace"
            }, {
                name: "VIEWPORT_SYM",
                text: ["@viewport", "@-ms-viewport"]
            }, {
                name: "UNKNOWN_SYM"
            }, {
                name: "KEYFRAMES_SYM",
                text: ["@keyframes", "@-webkit-keyframes", "@-moz-keyframes", "@-o-keyframes"]
            }, {
                name: "IMPORTANT_SYM"
            }, {
                name: "LENGTH"
            }, {
                name: "ANGLE"
            }, {
                name: "TIME"
            }, {
                name: "FREQ"
            }, {
                name: "DIMENSION"
            }, {
                name: "PERCENTAGE"
            }, {
                name: "NUMBER"
            }, {
                name: "URI"
            }, {
                name: "FUNCTION"
            }, {
                name: "UNICODE_RANGE"
            }, {
                name: "INVALID"
            }, {
                name: "PLUS",
                text: "+"
            }, {
                name: "GREATER",
                text: ">"
            }, {
                name: "COMMA",
                text: ","
            }, {
                name: "TILDE",
                text: "~"
            }, {
                name: "NOT"
            }, {
                name: "TOPLEFTCORNER_SYM",
                text: "@top-left-corner"
            }, {
                name: "TOPLEFT_SYM",
                text: "@top-left"
            }, {
                name: "TOPCENTER_SYM",
                text: "@top-center"
            }, {
                name: "TOPRIGHT_SYM",
                text: "@top-right"
            }, {
                name: "TOPRIGHTCORNER_SYM",
                text: "@top-right-corner"
            }, {
                name: "BOTTOMLEFTCORNER_SYM",
                text: "@bottom-left-corner"
            }, {
                name: "BOTTOMLEFT_SYM",
                text: "@bottom-left"
            }, {
                name: "BOTTOMCENTER_SYM",
                text: "@bottom-center"
            }, {
                name: "BOTTOMRIGHT_SYM",
                text: "@bottom-right"
            }, {
                name: "BOTTOMRIGHTCORNER_SYM",
                text: "@bottom-right-corner"
            }, {
                name: "LEFTTOP_SYM",
                text: "@left-top"
            }, {
                name: "LEFTMIDDLE_SYM",
                text: "@left-middle"
            }, {
                name: "LEFTBOTTOM_SYM",
                text: "@left-bottom"
            }, {
                name: "RIGHTTOP_SYM",
                text: "@right-top"
            }, {
                name: "RIGHTMIDDLE_SYM",
                text: "@right-middle"
            }, {
                name: "RIGHTBOTTOM_SYM",
                text: "@right-bottom"
            }, {
                name: "RESOLUTION",
                state: "media"
            }, {
                name: "IE_FUNCTION"
            }, {
                name: "CHAR"
            }, {
                name: "PIPE",
                text: "|"
            }, {
                name: "SLASH",
                text: "/"
            }, {
                name: "MINUS",
                text: "-"
            }, {
                name: "STAR",
                text: "*"
            },

            {
                name: "LBRACE",
                endChar: "}",
                text: "{"
            }, {
                name: "RBRACE",
                text: "}"
            }, {
                name: "LBRACKET",
                endChar: "]",
                text: "["
            }, {
                name: "RBRACKET",
                text: "]"
            }, {
                name: "EQUALS",
                text: "="
            }, {
                name: "COLON",
                text: ":"
            }, {
                name: "SEMICOLON",
                text: ";"
            },

            {
                name: "LPAREN",
                endChar: ")",
                text: "("
            }, {
                name: "RPAREN",
                text: ")"
            }, {
                name: "DOT",
                text: "."
            }
        ];

        (function() {

            var nameMap = [],
                typeMap = {};

            Tokens.UNKNOWN = -1;
            Tokens.unshift({
                name: "EOF"
            });
            for (var i = 0, len = Tokens.length; i < len; i++) {
                nameMap.push(Tokens[i].name);
                Tokens[Tokens[i].name] = i;
                if (Tokens[i].text) {
                    if (Tokens[i].text instanceof Array) {
                        for (var j = 0; j < Tokens[i].text.length; j++) {
                            typeMap[Tokens[i].text[j]] = i;
                        }
                    } else {
                        typeMap[Tokens[i].text] = i;
                    }
                }
            }

            Tokens.name = function(tt) {
                return nameMap[tt];
            };

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

        })();
        var Validation = {

            validate: function(property, value) {
                var name = property.toString().toLowerCase(),
                    parts = value.parts,
                    expression = new PropertyValueIterator(value),
                    spec = Properties[name],
                    part,
                    valid,
                    j, count,
                    msg,
                    types,
                    last,
                    literals,
                    max, multi, group;

                if (!spec) {
                    if (name.indexOf("-") !== 0) { //vendor prefixed are ok
                        throw new ValidationError("Unknown property '" + property + "'.", property.line, property.col);
                    }
                } else if (typeof spec != "number") {
                    if (typeof spec == "string") {
                        if (spec.indexOf("||") > -1) {
                            this.groupProperty(spec, expression);
                        } else {
                            this.singleProperty(spec, expression, 1);
                        }

                    } else if (spec.multi) {
                        this.multiProperty(spec.multi, expression, spec.comma, spec.max || Infinity);
                    } else if (typeof spec == "function") {
                        spec(expression);
                    }

                }

            },

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

                var result = false,
                    value = expression.value,
                    count = 0,
                    part;

                while (expression.hasNext() && count < max) {
                    result = ValidationTypes.isAny(expression, types);
                    if (!result) {
                        break;
                    }
                    count++;
                }

                if (!result) {
                    if (expression.hasNext() && !expression.isFirst()) {
                        part = expression.peek();
                        throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
                    } else {
                        throw new ValidationError("Expected (" + types + ") but found '" + value + "'.", value.line, value.col);
                    }
                } else if (expression.hasNext()) {
                    part = expression.next();
                    throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
                }

            },

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

                var result = false,
                    value = expression.value,
                    count = 0,
                    sep = false,
                    part;

                while (expression.hasNext() && !result && count < max) {
                    if (ValidationTypes.isAny(expression, types)) {
                        count++;
                        if (!expression.hasNext()) {
                            result = true;

                        } else if (comma) {
                            if (expression.peek() == ",") {
                                part = expression.next();
                            } else {
                                break;
                            }
                        }
                    } else {
                        break;

                    }
                }

                if (!result) {
                    if (expression.hasNext() && !expression.isFirst()) {
                        part = expression.peek();
                        throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
                    } else {
                        part = expression.previous();
                        if (comma && part == ",") {
                            throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
                        } else {
                            throw new ValidationError("Expected (" + types + ") but found '" + value + "'.", value.line, value.col);
                        }
                    }

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

            },

            groupProperty: function(types, expression, comma) {

                var result = false,
                    value = expression.value,
                    typeCount = types.split("||").length,
                    groups = {
                        count: 0
                    },
                    partial = false,
                    name,
                    part;

                while (expression.hasNext() && !result) {
                    name = ValidationTypes.isAnyOfGroup(expression, types);
                    if (name) {
                        if (groups[name]) {
                            break;
                        } else {
                            groups[name] = 1;
                            groups.count++;
                            partial = true;

                            if (groups.count == typeCount || !expression.hasNext()) {
                                result = true;
                            }
                        }
                    } else {
                        break;
                    }
                }

                if (!result) {
                    if (partial && expression.hasNext()) {
                        part = expression.peek();
                        throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
                    } else {
                        throw new ValidationError("Expected (" + types + ") but found '" + value + "'.", value.line, value.col);
                    }
                } else if (expression.hasNext()) {
                    part = expression.next();
                    throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
                }
            }



        };

        function ValidationError(message, line, col) {
            this.col = col;
            this.line = line;
            this.message = message;

        }
        ValidationError.prototype = new Error();
        var ValidationTypes = {

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

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

                return found;
            },

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

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

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

                return found;
            },
            isAnyOfGroup: function(expression, types) {
                var args = types.split(" || "),
                    i, len, found = false;

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

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

                if (type.charAt(0) != "<") {
                    result = this.isLiteral(part, type);
                    if (result) {
                        expression.next();
                    }
                } else if (this.simple[type]) {
                    result = this.simple[type](part);
                    if (result) {
                        expression.next();
                    }
                } else {
                    result = this.complex[type](expression);
                }

                return result;
            },



            simple: {

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

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

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

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

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

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

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

                "<relative-size>": function(part) {
                    return ValidationTypes.isLiteral(part, "smaller | larger");
                },
                "<ident>": function(part) {
                    return part.type == "identifier";
                },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            complex: {

                "<bg-position>": function(expression) {
                    var types = this,
                        result = false,
                        numeric = "<percentage> | <length>",
                        xDir = "left | right",
                        yDir = "top | bottom",
                        count = 0,
                        hasNext = function() {
                            return expression.hasNext() && expression.peek() != ",";
                        };

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

                    if (count < 3) {
                        if (ValidationTypes.isAny(expression, xDir + " | center | " + numeric)) {
                            result = true;
                            ValidationTypes.isAny(expression, yDir + " | center | " + numeric);
                        } else if (ValidationTypes.isAny(expression, yDir)) {
                            result = true;
                            ValidationTypes.isAny(expression, xDir + " | center");
                        }
                    } else {
                        if (ValidationTypes.isAny(expression, xDir)) {
                            if (ValidationTypes.isAny(expression, yDir)) {
                                result = true;
                                ValidationTypes.isAny(expression, numeric);
                            } else if (ValidationTypes.isAny(expression, numeric)) {
                                if (ValidationTypes.isAny(expression, yDir)) {
                                    result = true;
                                    ValidationTypes.isAny(expression, numeric);
                                } else if (ValidationTypes.isAny(expression, "center")) {
                                    result = true;
                                }
                            }
                        } else if (ValidationTypes.isAny(expression, yDir)) {
                            if (ValidationTypes.isAny(expression, xDir)) {
                                result = true;
                                ValidationTypes.isAny(expression, numeric);
                            } else if (ValidationTypes.isAny(expression, numeric)) {
                                if (ValidationTypes.isAny(expression, xDir)) {
                                    result = true;
                                    ValidationTypes.isAny(expression, numeric);
                                } else if (ValidationTypes.isAny(expression, "center")) {
                                    result = true;
                                }
                            }
                        } else if (ValidationTypes.isAny(expression, "center")) {
                            if (ValidationTypes.isAny(expression, xDir + " | " + yDir)) {
                                result = true;
                                ValidationTypes.isAny(expression, numeric);
                            }
                        }
                    }

                    return result;
                },

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

                    if (ValidationTypes.isAny(expression, "cover | contain")) {
                        result = true;
                    } else if (ValidationTypes.isAny(expression, numeric)) {
                        result = true;
                        ValidationTypes.isAny(expression, numeric);
                    }

                    return result;
                },

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

                    if (expression.hasNext()) {
                        part = expression.next();

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

                            if (expression.hasNext() && ValidationTypes.isLiteral(expression.peek(), values)) {
                                expression.next();
                            }
                        }
                    }

                    return result;

                },

                "<shadow>": function(expression) {
                    var result = false,
                        count = 0,
                        inset = false,
                        color = false,
                        part;

                    if (expression.hasNext()) {

                        if (ValidationTypes.isAny(expression, "inset")) {
                            inset = true;
                        }

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

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


                        if (expression.hasNext()) {
                            if (!color) {
                                ValidationTypes.isAny(expression, "<color>");
                            }

                            if (!inset) {
                                ValidationTypes.isAny(expression, "inset");
                            }

                        }

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

                    }

                    return result;
                },

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

                    if (ValidationTypes.isAny(expression, simple)) {
                        result = true;
                        ValidationTypes.isAny(expression, simple);
                    }

                    return result;
                },

                "<flex>": function(expression) {
                    var part,
                        result = false;
                    if (ValidationTypes.isAny(expression, "none | inherit")) {
                        result = true;
                    } else {
                        if (ValidationTypes.isType(expression, "<flex-grow>")) {
                            if (expression.peek()) {
                                if (ValidationTypes.isType(expression, "<flex-shrink>")) {
                                    if (expression.peek()) {
                                        result = ValidationTypes.isType(expression, "<flex-basis>");
                                    } else {
                                        result = true;
                                    }
                                } else if (ValidationTypes.isType(expression, "<flex-basis>")) {
                                    result = expression.peek() === null;
                                }
                            } else {
                                result = true;
                            }
                        } else if (ValidationTypes.isType(expression, "<flex-basis>")) {
                            result = true;
                        }
                    }

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

                    return result;
                }
            }
        };

        parserlib.css = {
            Colors: Colors,
            Combinator: Combinator,
            Parser: Parser,
            PropertyName: PropertyName,
            PropertyValue: PropertyValue,
            PropertyValuePart: PropertyValuePart,
            MediaFeature: MediaFeature,
            MediaQuery: MediaQuery,
            Selector: Selector,
            SelectorPart: SelectorPart,
            SelectorSubPart: SelectorSubPart,
            Specificity: Specificity,
            TokenStream: TokenStream,
            Tokens: Tokens,
            ValidationError: ValidationError
        };
    })();

    (function() {
        for (var prop in parserlib) {
            exports[prop] = parserlib[prop];
        }
    })();


    function objectToString(o) {
        return Object.prototype.toString.call(o);
    }
    var util = {
        isArray: function(ar) {
            return Array.isArray(ar) || (typeof ar === 'object' && objectToString(ar) === '[object Array]');
        },
        isDate: function(d) {
            return typeof d === 'object' && objectToString(d) === '[object Date]';
        },
        isRegExp: function(re) {
            return typeof re === 'object' && objectToString(re) === '[object RegExp]';
        },
        getRegExpFlags: function(re) {
            var flags = '';
            re.global && (flags += 'g');
            re.ignoreCase && (flags += 'i');
            re.multiline && (flags += 'm');
            return flags;
        }
    };


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

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

        var useBuffer = typeof Buffer != 'undefined';

        if (typeof circular == 'undefined')
            circular = true;

        if (typeof depth == 'undefined')
            depth = Infinity;

        function _clone(parent, depth) {
            if (parent === null)
                return null;

            if (depth == 0)
                return parent;

            var child;
            if (typeof parent != 'object') {
                return parent;
            }

            if (util.isArray(parent)) {
                child = [];
            } else if (util.isRegExp(parent)) {
                child = new RegExp(parent.source, util.getRegExpFlags(parent));
                if (parent.lastIndex) child.lastIndex = parent.lastIndex;
            } else if (util.isDate(parent)) {
                child = new Date(parent.getTime());
            } else if (useBuffer && Buffer.isBuffer(parent)) {
                child = new Buffer(parent.length);
                parent.copy(child);
                return child;
            } else {
                if (typeof prototype == 'undefined') child = Object.create(Object.getPrototypeOf(parent));
                else child = Object.create(prototype);
            }

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

                if (index != -1) {
                    return allChildren[index];
                }
                allParents.push(parent);
                allChildren.push(child);
            }

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

            return child;
        }

        return _clone(parent, depth);
    }
    clone.clonePrototype = function(parent) {
        if (parent === null)
            return null;

        var c = function() {};
        c.prototype = parent;
        return new c();
    };

    var CSSLint = (function() {

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

        api.version = "@VERSION@";
        api.addRule = function(rule) {
            rules.push(rule);
            rules[rule.id] = rule;
        };
        api.clearRules = function() {
            rules = [];
        };
        api.getRules = function() {
            return [].concat(rules).sort(function(a, b) {
                return a.id > b.id ? 1 : 0;
            });
        };
        api.getRuleset = function() {
            var ruleset = {},
                i = 0,
                len = rules.length;

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

            return ruleset;
        };

        function applyEmbeddedRuleset(text, ruleset) {
            var valueMap,
                embedded = text && text.match(embeddedRuleset),
                rules = embedded && embedded[1];

            if (rules) {
                valueMap = {
                    "true": 2, // true is error
                    "": 1, // blank is warning
                    "false": 0, // false is ignore

                    "2": 2, // explicit error
                    "1": 1, // explicit warning
                    "0": 0 // explicit ignore
                };

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

                    ruleset[property.trim()] = valueMap[value.trim()];
                });
            }

            return ruleset;
        }
        api.addFormatter = function(formatter) {
            formatters[formatter.id] = formatter;
        };
        api.getFormatter = function(formatId) {
            return formatters[formatId];
        };
        api.format = function(results, filename, formatId, options) {
            var formatter = this.getFormatter(formatId),
                result = null;

            if (formatter) {
                result = formatter.startFormat();
                result += formatter.formatResults(results, filename, options || {});
                result += formatter.endFormat();
            }

            return result;
        };
        api.hasFormat = function(formatId) {
            return formatters.hasOwnProperty(formatId);
        };
        api.verify = function(text, ruleset) {

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

            if (!ruleset) {
                ruleset = this.getRuleset();
            }

            if (embeddedRuleset.test(text)) {
                ruleset = clone(ruleset);
                ruleset = applyEmbeddedRuleset(text, ruleset);
            }

            reporter = new Reporter(lines, ruleset);

            ruleset.errors = 2; //always report parsing errors as errors
            for (i in ruleset) {
                if (ruleset.hasOwnProperty(i) && ruleset[i]) {
                    if (rules[i]) {
                        rules[i].init(parser, reporter);
                    }
                }
            }
            try {
                parser.parse(text);
            } catch (ex) {
                reporter.error("Fatal error, cannot continue: " + ex.message, ex.line, ex.col, {});
            }

            report = {
                messages: reporter.messages,
                stats: reporter.stats,
                ruleset: reporter.ruleset
            };
            report.messages.sort(function(a, b) {
                if (a.rollup && !b.rollup) {
                    return 1;
                } else if (!a.rollup && b.rollup) {
                    return -1;
                } else {
                    return a.line - b.line;
                }
            });

            return report;
        };

        return api;

    })();

    function Reporter(lines, ruleset) {
        this.messages = [];
        this.stats = [];
        this.lines = lines;
        this.ruleset = ruleset;
    }

    Reporter.prototype = {
        constructor: Reporter,
        error: function(message, line, col, rule) {
            this.messages.push({
                type: "error",
                line: line,
                col: col,
                message: message,
                evidence: this.lines[line - 1],
                rule: rule || {}
            });
        },
        warn: function(message, line, col, rule) {
            this.report(message, line, col, rule);
        },
        report: function(message, line, col, rule) {
            this.messages.push({
                type: this.ruleset[rule.id] === 2 ? "error" : "warning",
                line: line,
                col: col,
                message: message,
                evidence: this.lines[line - 1],
                rule: rule
            });
        },
        info: function(message, line, col, rule) {
            this.messages.push({
                type: "info",
                line: line,
                col: col,
                message: message,
                evidence: this.lines[line - 1],
                rule: rule
            });
        },
        rollupError: function(message, rule) {
            this.messages.push({
                type: "error",
                rollup: true,
                message: message,
                rule: rule
            });
        },
        rollupWarn: function(message, rule) {
            this.messages.push({
                type: "warning",
                rollup: true,
                message: message,
                rule: rule
            });
        },
        stat: function(name, value) {
            this.stats[name] = value;
        }
    };
    CSSLint._Reporter = Reporter;
    CSSLint.Util = {
        mix: function(receiver, supplier) {
            var prop;

            for (prop in supplier) {
                if (supplier.hasOwnProperty(prop)) {
                    receiver[prop] = supplier[prop];
                }
            }

            return prop;
        },
        indexOf: function(values, value) {
            if (values.indexOf) {
                return values.indexOf(value);
            } else {
                for (var i = 0, len = values.length; i < len; i++) {
                    if (values[i] === value) {
                        return i;
                    }
                }
                return -1;
            }
        },
        forEach: function(values, func) {
            if (values.forEach) {
                return values.forEach(func);
            } else {
                for (var i = 0, len = values.length; i < len; i++) {
                    func(values[i], i, values);
                }
            }
        }
    };

    CSSLint.addRule({
        id: "adjoining-classes",
        name: "Disallow adjoining classes",
        desc: "Don't use adjoining classes.",
        browsers: "IE6",
        init: function(parser, reporter) {
            var rule = this;
            parser.addListener("startrule", function(event) {
                var selectors = event.selectors,
                    selector,
                    part,
                    modifier,
                    classCount,
                    i, j, k;

                for (i = 0; i < selectors.length; i++) {
                    selector = selectors[i];
                    for (j = 0; j < selector.parts.length; j++) {
                        part = selector.parts[j];
                        if (part.type === parser.SELECTOR_PART_TYPE) {
                            classCount = 0;
                            for (k = 0; k < part.modifiers.length; k++) {
                                modifier = part.modifiers[k];
                                if (modifier.type === "class") {
                                    classCount++;
                                }
                                if (classCount > 1) {
                                    reporter.report("Don't use adjoining classes.", part.line, part.col, rule);
                                }
                            }
                        }
                    }
                }
            });
        }

    });
    CSSLint.addRule({
        id: "box-model",
        name: "Beware of broken box size",
        desc: "Don't use width or height when using padding or border.",
        browsers: "All",
        init: function(parser, reporter) {
            var rule = this,
                widthProperties = {
                    border: 1,
                    "border-left": 1,
                    "border-right": 1,
                    padding: 1,
                    "padding-left": 1,
                    "padding-right": 1
                },
                heightProperties = {
                    border: 1,
                    "border-bottom": 1,
                    "border-top": 1,
                    padding: 1,
                    "padding-bottom": 1,
                    "padding-top": 1
                },
                properties,
                boxSizing = false;

            function startRule() {
                properties = {};
                boxSizing = false;
            }

            function endRule() {
                var prop, value;

                if (!boxSizing) {
                    if (properties.height) {
                        for (prop in heightProperties) {
                            if (heightProperties.hasOwnProperty(prop) && properties[prop]) {
                                value = properties[prop].value;
                                if (!(prop === "padding" && value.parts.length === 2 && value.parts[0].value === 0)) {
                                    reporter.report("Using height with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
                                }
                            }
                        }
                    }

                    if (properties.width) {
                        for (prop in widthProperties) {
                            if (widthProperties.hasOwnProperty(prop) && properties[prop]) {
                                value = properties[prop].value;

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

            parser.addListener("startrule", startRule);
            parser.addListener("startfontface", startRule);
            parser.addListener("startpage", startRule);
            parser.addListener("startpagemargin", startRule);
            parser.addListener("startkeyframerule", startRule);

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

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

            });

            parser.addListener("endrule", endRule);
            parser.addListener("endfontface", endRule);
            parser.addListener("endpage", endRule);
            parser.addListener("endpagemargin", endRule);
            parser.addListener("endkeyframerule", endRule);
        }

    });

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

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

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

    });

    CSSLint.addRule({
        id: "bulletproof-font-face",
        name: "Use the bulletproof @font-face syntax",
        desc: "Use the bulletproof @font-face syntax to avoid 404's in old IE (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax).",
        browsers: "All",
        init: function(parser, reporter) {
            var rule = this,
                fontFaceRule = false,
                firstSrc = true,
                ruleFailed = false,
                line, col;
            parser.addListener("startfontface", function() {
                fontFaceRule = true;
            });

            parser.addListener("property", function(event) {
                if (!fontFaceRule) {
                    return;
                }

                var propertyName = event.property.toString().toLowerCase(),
                    value = event.value.toString();
                line = event.line;
                col = event.col;
                if (propertyName === "src") {
                    var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i;
                    if (!value.match(regex) && firstSrc) {
                        ruleFailed = true;
                        firstSrc = false;
                    } else if (value.match(regex) && !firstSrc) {
                        ruleFailed = false;
                    }
                }


            });
            parser.addListener("endfontface", function() {
                fontFaceRule = false;

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

    CSSLint.addRule({
        id: "compatible-vendor-prefixes",
        name: "Require compatible vendor prefixes",
        desc: "Include all compatible vendor prefixes to reach a wider range of users.",
        browsers: "All",
        init: function(parser, reporter) {
            var rule = this,
                compatiblePrefixes,
                properties,
                prop,
                variations,
                prefixed,
                i,
                len,
                inKeyFrame = false,
                arrayPush = Array.prototype.push,
                applyTo = [];
            compatiblePrefixes = {
                "animation": "webkit moz",
                "animation-delay": "webkit moz",
                "animation-direction": "webkit moz",
                "animation-duration": "webkit moz",
                "animation-fill-mode": "webkit moz",
                "animation-iteration-count": "webkit moz",
                "animation-name": "webkit moz",
                "animation-play-state": "webkit moz",
                "animation-timing-function": "webkit moz",
                "appearance": "webkit moz",
                "border-end": "webkit moz",
                "border-end-color": "webkit moz",
                "border-end-style": "webkit moz",
                "border-end-width": "webkit moz",
                "border-image": "webkit moz o",
                "border-radius": "webkit",
                "border-start": "webkit moz",
                "border-start-color": "webkit moz",
                "border-start-style": "webkit moz",
                "border-start-width": "webkit moz",
                "box-align": "webkit moz ms",
                "box-direction": "webkit moz ms",
                "box-flex": "webkit moz ms",
                "box-lines": "webkit ms",
                "box-ordinal-group": "webkit moz ms",
                "box-orient": "webkit moz ms",
                "box-pack": "webkit moz ms",
                "box-sizing": "webkit moz",
                "box-shadow": "webkit moz",
                "column-count": "webkit moz ms",
                "column-gap": "webkit moz ms",
                "column-rule": "webkit moz ms",
                "column-rule-color": "webkit moz ms",
                "column-rule-style": "webkit moz ms",
                "column-rule-width": "webkit moz ms",
                "column-width": "webkit moz ms",
                "hyphens": "epub moz",
                "line-break": "webkit ms",
                "margin-end": "webkit moz",
                "margin-start": "webkit moz",
                "marquee-speed": "webkit wap",
                "marquee-style": "webkit wap",
                "padding-end": "webkit moz",
                "padding-start": "webkit moz",
                "tab-size": "moz o",
                "text-size-adjust": "webkit ms",
                "transform": "webkit moz ms o",
                "transform-origin": "webkit moz ms o",
                "transition": "webkit moz o",
                "transition-delay": "webkit moz o",
                "transition-duration": "webkit moz o",
                "transition-property": "webkit moz o",
                "transition-timing-function": "webkit moz o",
                "user-modify": "webkit moz",
                "user-select": "webkit moz ms",
                "word-break": "epub ms",
                "writing-mode": "epub ms"
            };


            for (prop in compatiblePrefixes) {
                if (compatiblePrefixes.hasOwnProperty(prop)) {
                    variations = [];
                    prefixed = compatiblePrefixes[prop].split(" ");
                    for (i = 0, len = prefixed.length; i < len; i++) {
                        variations.push("-" + prefixed[i] + "-" + prop);
                    }
                    compatiblePrefixes[prop] = variations;
                    arrayPush.apply(applyTo, variations);
                }
            }

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

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

            parser.addListener("endkeyframes", function() {
                inKeyFrame = false;
            });

            parser.addListener("property", function(event) {
                var name = event.property;
                if (CSSLint.Util.indexOf(applyTo, name.text) > -1) {
                    if (!inKeyFrame || typeof inKeyFrame !== "string" ||
                        name.text.indexOf("-" + inKeyFrame + "-") !== 0) {
                        properties.push(name);
                    }
                }
            });

            parser.addListener("endrule", function() {
                if (!properties.length) {
                    return;
                }

                var propertyGroups = {},
                    i,
                    len,
                    name,
                    prop,
                    variations,
                    value,
                    full,
                    actual,
                    item,
                    propertiesSpecified;

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

                    for (prop in compatiblePrefixes) {
                        if (compatiblePrefixes.hasOwnProperty(prop)) {
                            variations = compatiblePrefixes[prop];
                            if (CSSLint.Util.indexOf(variations, name.text) > -1) {
                                if (!propertyGroups[prop]) {
                                    propertyGroups[prop] = {
                                        full: variations.slice(0),
                                        actual: [],
                                        actualNodes: []
                                    };
                                }
                                if (CSSLint.Util.indexOf(propertyGroups[prop].actual, name.text) === -1) {
                                    propertyGroups[prop].actual.push(name.text);
                                    propertyGroups[prop].actualNodes.push(name);
                                }
                            }
                        }
                    }
                }

                for (prop in propertyGroups) {
                    if (propertyGroups.hasOwnProperty(prop)) {
                        value = propertyGroups[prop];
                        full = value.full;
                        actual = value.actual;

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

                        }
                    }
                }
            });
        }
    });

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

            var propertiesToCheck = {
                display: 1,
                "float": "none",
                height: 1,
                width: 1,
                margin: 1,
                "margin-left": 1,
                "margin-right": 1,
                "margin-bottom": 1,
                "margin-top": 1,
                padding: 1,
                "padding-left": 1,
                "padding-right": 1,
                "padding-bottom": 1,
                "padding-top": 1,
                "vertical-align": 1
            },
                properties;

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

            function startRule() {
                properties = {};
            }

            function endRule() {

                var display = properties.display ? properties.display.value : null;
                if (display) {
                    switch (display) {

                        case "inline":
                            reportProperty("height", display);
                            reportProperty("width", display);
                            reportProperty("margin", display);
                            reportProperty("margin-top", display);
                            reportProperty("margin-bottom", display);
                            reportProperty("float", display, "display:inline has no effect on floated elements (but may be used to fix the IE6 double-margin bug).");
                            break;

                        case "block":
                            reportProperty("vertical-align", display);
                            break;

                        case "inline-block":
                            reportProperty("float", display);
                            break;

                        default:
                            if (display.indexOf("table-") === 0) {
                                reportProperty("margin", display);
                                reportProperty("margin-left", display);
                                reportProperty("margin-right", display);
                                reportProperty("margin-top", display);
                                reportProperty("margin-bottom", display);
                                reportProperty("float", display);
                            }
                    }
                }

            }

            parser.addListener("startrule", startRule);
            parser.addListener("startfontface", startRule);
            parser.addListener("startkeyframerule", startRule);
            parser.addListener("startpagemargin", startRule);
            parser.addListener("startpage", startRule);

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

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

            parser.addListener("endrule", endRule);
            parser.addListener("endfontface", endRule);
            parser.addListener("endkeyframerule", endRule);
            parser.addListener("endpagemargin", endRule);
            parser.addListener("endpage", endRule);

        }

    });

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

            parser.addListener("property", function(event) {
                var name = event.property.text,
                    value = event.value,
                    i, len;

                if (name.match(/background/i)) {
                    for (i = 0, len = value.parts.length; i < len; i++) {
                        if (value.parts[i].type === "uri") {
                            if (typeof stack[value.parts[i].uri] === "undefined") {
                                stack[value.parts[i].uri] = event;
                            } else {
                                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);
                            }
                        }
                    }
                }
            });
        }
    });

    CSSLint.addRule({
        id: "duplicate-properties",
        name: "Disallow duplicate properties",
        desc: "Duplicate properties must appear one after the other.",
        browsers: "All",
        init: function(parser, reporter) {
            var rule = this,
                properties,
                lastProperty;

            function startRule() {
                properties = {};
            }

            parser.addListener("startrule", startRule);
            parser.addListener("startfontface", startRule);
            parser.addListener("startpage", startRule);
            parser.addListener("startpagemargin", startRule);
            parser.addListener("startkeyframerule", startRule);

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

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

                properties[name] = event.value.text;
                lastProperty = name;

            });


        }

    });

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

            parser.addListener("startrule", function() {
                count = 0;
            });

            parser.addListener("property", function() {
                count++;
            });

            parser.addListener("endrule", function(event) {
                var selectors = event.selectors;
                if (count === 0) {
                    reporter.report("Rule is empty.", selectors[0].line, selectors[0].col, rule);
                }
            });
        }

    });

    CSSLint.addRule({
        id: "errors",
        name: "Parsing Errors",
        desc: "This rule looks for recoverable syntax errors.",
        browsers: "All",
        init: function(parser, reporter) {
            var rule = this;

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

        }

    });

    CSSLint.addRule({
        id: "fallback-colors",
        name: "Require fallback colors",
        desc: "For older browsers that don't support RGBA, HSL, or HSLA, provide a fallback color.",
        browsers: "IE6,IE7,IE8",
        init: function(parser, reporter) {
            var rule = this,
                lastProperty,
                propertiesToCheck = {
                    color: 1,
                    background: 1,
                    "border-color": 1,
                    "border-top-color": 1,
                    "border-right-color": 1,
                    "border-bottom-color": 1,
                    "border-left-color": 1,
                    border: 1,
                    "border-top": 1,
                    "border-right": 1,
                    "border-bottom": 1,
                    "border-left": 1,
                    "background-color": 1
                },
                properties;

            function startRule() {
                properties = {};
                lastProperty = null;
            }

            parser.addListener("startrule", startRule);
            parser.addListener("startfontface", startRule);
            parser.addListener("startpage", startRule);
            parser.addListener("startpagemargin", startRule);
            parser.addListener("startkeyframerule", startRule);

            parser.addListener("property", function(event) {
                var property = event.property,
                    name = property.text.toLowerCase(),
                    parts = event.value.parts,
                    i = 0,
                    colorType = "",
                    len = parts.length;

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

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

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

                        i++;
                    }
                }

                lastProperty = event;
            });

        }

    });

    CSSLint.addRule({
        id: "floats",
        name: "Disallow too many floats",
        desc: "This rule tests if the float property is used too many times",
        browsers: "All",
        init: function(parser, reporter) {
            var rule = this;
            var count = 0;
            parser.addListener("property", function(event) {
                if (event.property.text.toLowerCase() === "float" &&
                    event.value.text.toLowerCase() !== "none") {
                    count++;
                }
            });
            parser.addListener("endstylesheet", function() {
                reporter.stat("floats", count);
                if (count >= 10) {
                    reporter.rollupWarn("Too many floats (" + count + "), you're probably using them for layout. Consider using a grid system instead.", rule);
                }
            });
        }

    });

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


            parser.addListener("startfontface", function() {
                count++;
            });

            parser.addListener("endstylesheet", function() {
                if (count > 5) {
                    reporter.rollupWarn("Too many @font-face declarations (" + count + ").", rule);
                }
            });
        }

    });

    CSSLint.addRule({
        id: "font-sizes",
        name: "Disallow too many font sizes",
        desc: "Checks the number of font-size declarations.",
        browsers: "All",
        init: function(parser, reporter) {
            var rule = this,
                count = 0;
            parser.addListener("property", function(event) {
                if (event.property.toString() === "font-size") {
                    count++;
                }
            });
            parser.addListener("endstylesheet", function() {
                reporter.stat("font-sizes", count);
                if (count >= 10) {
                    reporter.rollupWarn("Too many font-size declarations (" + count + "), abstraction needed.", rule);
                }
            });
        }

    });

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

            parser.addListener("startrule", function() {
                gradients = {
                    moz: 0,
                    webkit: 0,
                    oldWebkit: 0,
                    o: 0
                };
            });

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

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

            });

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

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

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

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

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

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

            });

        }

    });

    CSSLint.addRule({
        id: "ids",
        name: "Disallow IDs in selectors",
        desc: "Selectors should not contain IDs.",
        browsers: "All",
        init: function(parser, reporter) {
            var rule = this;
            parser.addListener("startrule", function(event) {
                var selectors = event.selectors,
                    selector,
                    part,
                    modifier,
                    idCount,
                    i, j, k;

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

                    for (j = 0; j < selector.parts.length; j++) {
                        part = selector.parts[j];
                        if (part.type === parser.SELECTOR_PART_TYPE) {
                            for (k = 0; k < part.modifiers.length; k++) {
                                modifier = part.modifiers[k];
                                if (modifier.type === "id") {
                                    idCount++;
                                }
                            }
                        }
                    }

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

            });
        }

    });

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

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

        }

    });

    CSSLint.addRule({
        id: "important",
        name: "Disallow !important",
        desc: "Be careful when using !important declaration",
        browsers: "All",
        init: function(parser, reporter) {
            var rule = this,
                count = 0;
            parser.addListener("property", function(event) {
                if (event.important === true) {
                    count++;
                    reporter.report("Use of !important", event.line, event.col, rule);
                }
            });
            parser.addListener("endstylesheet", function() {
                reporter.stat("important", count);
                if (count >= 10) {
                    reporter.rollupWarn("Too many !important declarations (" + count + "), try to use less than 10 to avoid specificity issues.", rule);
                }
            });
        }

    });

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

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

            });
        }

    });
    CSSLint.addRule({
        id: "order-alphabetical",
        name: "Alphabetical order",
        desc: "Assure properties are in alphabetical order",
        browsers: "All",
        init: function(parser, reporter) {
            var rule = this,
                properties;

            var startRule = function() {
                properties = [];
            };

            parser.addListener("startrule", startRule);
            parser.addListener("startfontface", startRule);
            parser.addListener("startpage", startRule);
            parser.addListener("startpagemargin", startRule);
            parser.addListener("startkeyframerule", startRule);

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

                properties.push(lowerCasePrefixLessName);
            });

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

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

    });

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

            function startRule(event) {
                if (event.selectors) {
                    lastRule = {
                        line: event.line,
                        col: event.col,
                        selectors: event.selectors,
                        propCount: 0,
                        outline: false
                    };
                } else {
                    lastRule = null;
                }
            }

            function endRule() {
                if (lastRule) {
                    if (lastRule.outline) {
                        if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") === -1) {
                            reporter.report("Outlines should only be modified using :focus.", lastRule.line, lastRule.col, rule);
                        } else if (lastRule.propCount === 1) {
                            reporter.report("Outlines shouldn't be hidden unless other visual changes are made.", lastRule.line, lastRule.col, rule);
                        }
                    }
                }
            }

            parser.addListener("startrule", startRule);
            parser.addListener("startfontface", startRule);
            parser.addListener("startpage", startRule);
            parser.addListener("startpagemargin", startRule);
            parser.addListener("startkeyframerule", startRule);

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

                if (lastRule) {
                    lastRule.propCount++;
                    if (name === "outline" && (value.toString() === "none" || value.toString() === "0")) {
                        lastRule.outline = true;
                    }
                }

            });

            parser.addListener("endrule", endRule);
            parser.addListener("endfontface", endRule);
            parser.addListener("endpage", endRule);
            parser.addListener("endpagemargin", endRule);
            parser.addListener("endkeyframerule", endRule);

        }

    });

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

            parser.addListener("startrule", function(event) {
                var selectors = event.selectors,
                    selector,
                    part,
                    modifier,
                    i, j, k;

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

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

                                    if (!classes[modifier]) {
                                        classes[modifier] = [];
                                    }
                                    classes[modifier].push({
                                        modifier: modifier,
                                        part: part
                                    });
                                }
                            }
                        }
                    }
                }
            });

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

                var prop;
                for (prop in classes) {
                    if (classes.hasOwnProperty(prop)) {
                        if (classes[prop].length === 1 && classes[prop][0].part.elementName) {
                            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);
                        }
                    }
                }
            });
        }

    });

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

            parser.addListener("startrule", function(event) {
                var selectors = event.selectors,
                    selector,
                    part,
                    i, j;

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

                    for (j = 0; j < selector.parts.length; j++) {
                        part = selector.parts[j];
                        if (part.type === parser.SELECTOR_PART_TYPE) {
                            if (part.elementName && /h[1-6]/.test(part.elementName.toString()) && j > 0) {
                                reporter.report("Heading (" + part.elementName + ") should not be qualified.", part.line, part.col, rule);
                            }
                        }
                    }
                }
            });
        }

    });

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

            parser.addListener("startrule", function(event) {
                var selectors = event.selectors,
                    selector,
                    part,
                    modifier,
                    i, j, k;

                for (i = 0; i < selectors.length; i++) {
                    selector = selectors[i];
                    for (j = 0; j < selector.parts.length; j++) {
                        part = selector.parts[j];
                        if (part.type === parser.SELECTOR_PART_TYPE) {
                            for (k = 0; k < part.modifiers.length; k++) {
                                modifier = part.modifiers[k];
                                if (modifier.type === "attribute") {
                                    if (/([\~\|\^\$\*]=)/.test(modifier)) {
                                        reporter.report("Attribute selectors with " + RegExp.$1 + " are slow!", modifier.line, modifier.col, rule);
                                    }
                                }

                            }
                        }
                    }
                }
            });
        }

    });

    CSSLint.addRule({
        id: "rules-count",
        name: "Rules Count",
        desc: "Track how many rules there are.",
        browsers: "All",
        init: function(parser, reporter) {
            var count = 0;
            parser.addListener("startrule", function() {
                count++;
            });

            parser.addListener("endstylesheet", function() {
                reporter.stat("rule-count", count);
            });
        }

    });

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

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

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

    });

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

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

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

    });

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

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

                for (i = 0, len = selectors.length; i < len; i++) {
                    selector = selectors[i];
                    for (p = 0, pLen = selector.parts.length; p < pLen; p++) {
                        for (n = p + 1; n < pLen; n++) {
                            part = selector.parts[p];
                            part2 = selector.parts[n];
                            type = part.type;
                            currentLine = part.line;
                            nextLine = part2.line;

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

                }
            }

            parser.addListener("startrule", startRule);

        }
    });

    CSSLint.addRule({
        id: "shorthand",
        name: "Require shorthand properties",
        desc: "Use shorthand properties where possible.",
        browsers: "All",
        init: function(parser, reporter) {
            var rule = this,
                prop, i, len,
                propertiesToCheck = {},
                properties,
                mapping = {
                    "margin": [
                        "margin-top",
                        "margin-bottom",
                        "margin-left",
                        "margin-right"
                    ],
                    "padding": [
                        "padding-top",
                        "padding-bottom",
                        "padding-left",
                        "padding-right"
                    ]
                };
            for (prop in mapping) {
                if (mapping.hasOwnProperty(prop)) {
                    for (i = 0, len = mapping[prop].length; i < len; i++) {
                        propertiesToCheck[mapping[prop][i]] = prop;
                    }
                }
            }

            function startRule() {
                properties = {};
            }

            function endRule(event) {

                var prop, i, len, total;
                for (prop in mapping) {
                    if (mapping.hasOwnProperty(prop)) {
                        total = 0;

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

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

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

                if (propertiesToCheck[name]) {
                    properties[name] = 1;
                }
            });

            parser.addListener("endrule", endRule);
            parser.addListener("endfontface", endRule);

        }

    });

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

                if (property.hack === "*") {
                    reporter.report("Property with star prefix found.", event.property.line, event.property.col, rule);
                }
            });
        }
    });

    CSSLint.addRule({
        id: "text-indent",
        name: "Disallow negative text-indent",
        desc: "Checks for text indent less than -99px",
        browsers: "All",
        init: function(parser, reporter) {
            var rule = this,
                textIndent,
                direction;


            function startRule() {
                textIndent = false;
                direction = "inherit";
            }

            function endRule() {
                if (textIndent && direction !== "ltr") {
                    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);
                }
            }

            parser.addListener("startrule", startRule);
            parser.addListener("startfontface", startRule);
            parser.addListener("property", function(event) {
                var name = event.property.toString().toLowerCase(),
                    value = event.value;

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

            parser.addListener("endrule", endRule);
            parser.addListener("endfontface", endRule);

        }

    });

    CSSLint.addRule({
        id: "underscore-property-hack",
        name: "Disallow properties with an underscore prefix",
        desc: "Checks for the underscore property hack (targets IE6)",
        browsers: "All",
        init: function(parser, reporter) {
            var rule = this;
            parser.addListener("property", function(event) {
                var property = event.property;

                if (property.hack === "_") {
                    reporter.report("Property with underscore prefix found.", event.property.line, event.property.col, rule);
                }
            });
        }
    });

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

            var headings = {
                h1: 0,
                h2: 0,
                h3: 0,
                h4: 0,
                h5: 0,
                h6: 0
            };

            parser.addListener("startrule", function(event) {
                var selectors = event.selectors,
                    selector,
                    part,
                    pseudo,
                    i, j;

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

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

                        for (j = 0; j < part.modifiers.length; j++) {
                            if (part.modifiers[j].type === "pseudo") {
                                pseudo = true;
                                break;
                            }
                        }

                        if (!pseudo) {
                            headings[RegExp.$1]++;
                            if (headings[RegExp.$1] > 1) {
                                reporter.report("Heading (" + part.elementName + ") has already been defined.", part.line, part.col, rule);
                            }
                        }
                    }
                }
            });

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

                for (prop in headings) {
                    if (headings.hasOwnProperty(prop)) {
                        if (headings[prop] > 1) {
                            messages.push(headings[prop] + " " + prop + "s");
                        }
                    }
                }

                if (messages.length) {
                    reporter.rollupWarn("You have " + messages.join(", ") + " defined in this stylesheet.", rule);
                }
            });
        }

    });

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

            parser.addListener("startrule", function(event) {
                var selectors = event.selectors,
                    selector,
                    part,
                    i;

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

                    part = selector.parts[selector.parts.length - 1];
                    if (part.elementName === "*") {
                        reporter.report(rule.desc, part.line, part.col, rule);
                    }
                }
            });
        }

    });

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

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

                var selectors = event.selectors,
                    selector,
                    part,
                    modifier,
                    i, k;

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

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

                }
            });
        }

    });

    CSSLint.addRule({
        id: "vendor-prefix",
        name: "Require standard property with vendor prefix",
        desc: "When using a vendor-prefixed property, make sure to include the standard one.",
        browsers: "All",
        init: function(parser, reporter) {
            var rule = this,
                properties,
                num,
                propertiesToCheck = {
                    "-webkit-border-radius": "border-radius",
                    "-webkit-border-top-left-radius": "border-top-left-radius",
                    "-webkit-border-top-right-radius": "border-top-right-radius",
                    "-webkit-border-bottom-left-radius": "border-bottom-left-radius",
                    "-webkit-border-bottom-right-radius": "border-bottom-right-radius",

                    "-o-border-radius": "border-radius",
                    "-o-border-top-left-radius": "border-top-left-radius",
                    "-o-border-top-right-radius": "border-top-right-radius",
                    "-o-border-bottom-left-radius": "border-bottom-left-radius",
                    "-o-border-bottom-right-radius": "border-bottom-right-radius",

                    "-moz-border-radius": "border-radius",
                    "-moz-border-radius-topleft": "border-top-left-radius",
                    "-moz-border-radius-topright": "border-top-right-radius",
                    "-moz-border-radius-bottomleft": "border-bottom-left-radius",
                    "-moz-border-radius-bottomright": "border-bottom-right-radius",

                    "-moz-column-count": "column-count",
                    "-webkit-column-count": "column-count",

                    "-moz-column-gap": "column-gap",
                    "-webkit-column-gap": "column-gap",

                    "-moz-column-rule": "column-rule",
                    "-webkit-column-rule": "column-rule",

                    "-moz-column-rule-style": "column-rule-style",
                    "-webkit-column-rule-style": "column-rule-style",

                    "-moz-column-rule-color": "column-rule-color",
                    "-webkit-column-rule-color": "column-rule-color",

                    "-moz-column-rule-width": "column-rule-width",
                    "-webkit-column-rule-width": "column-rule-width",

                    "-moz-column-width": "column-width",
                    "-webkit-column-width": "column-width",

                    "-webkit-column-span": "column-span",
                    "-webkit-columns": "columns",

                    "-moz-box-shadow": "box-shadow",
                    "-webkit-box-shadow": "box-shadow",

                    "-moz-transform": "transform",
                    "-webkit-transform": "transform",
                    "-o-transform": "transform",
                    "-ms-transform": "transform",

                    "-moz-transform-origin": "transform-origin",
                    "-webkit-transform-origin": "transform-origin",
                    "-o-transform-origin": "transform-origin",
                    "-ms-transform-origin": "transform-origin",

                    "-moz-box-sizing": "box-sizing",
                    "-webkit-box-sizing": "box-sizing"
                };

            function startRule() {
                properties = {};
                num = 1;
            }

            function endRule() {
                var prop,
                    i,
                    len,
                    needed,
                    actual,
                    needsStandard = [];

                for (prop in properties) {
                    if (propertiesToCheck[prop]) {
                        needsStandard.push({
                            actual: prop,
                            needed: propertiesToCheck[prop]
                        });
                    }
                }

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

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

            }

            parser.addListener("startrule", startRule);
            parser.addListener("startfontface", startRule);
            parser.addListener("startpage", startRule);
            parser.addListener("startpagemargin", startRule);
            parser.addListener("startkeyframerule", startRule);

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

                if (!properties[name]) {
                    properties[name] = [];
                }

                properties[name].push({
                    name: event.property,
                    value: event.value,
                    pos: num++
                });
            });

            parser.addListener("endrule", endRule);
            parser.addListener("endfontface", endRule);
            parser.addListener("endpage", endRule);
            parser.addListener("endpagemargin", endRule);
            parser.addListener("endkeyframerule", endRule);
        }

    });

    CSSLint.addRule({
        id: "zero-units",
        name: "Disallow units for 0 values",
        desc: "You don't need to specify units when a value is 0.",
        browsers: "All",
        init: function(parser, reporter) {
            var rule = this;
            parser.addListener("property", function(event) {
                var parts = event.value.parts,
                    i = 0,
                    len = parts.length;

                while (i < len) {
                    if ((parts[i].units || parts[i].type === "percentage") && parts[i].value === 0 && parts[i].type !== "time") {
                        reporter.report("Values of 0 shouldn't have units specified.", parts[i].line, parts[i].col, rule);
                    }
                    i++;
                }

            });

        }

    });

    (function() {
        var xmlEscape = function(str) {
            if (!str || str.constructor !== String) {
                return "";
            }

            return str.replace(/[\"&><]/g, function(match) {
                switch (match) {
                    case "\"":
                        return "&quot;";
                    case "&":
                        return "&amp;";
                    case "<":
                        return "&lt;";
                    case ">":
                        return "&gt;";
                }
            });
        };

        CSSLint.addFormatter({
            id: "checkstyle-xml",
            name: "Checkstyle XML format",
            startFormat: function() {
                return "<?xml version=\"1.0\" encoding=\"utf-8\"?><checkstyle>";
            },
            endFormat: function() {
                return "</checkstyle>";
            },
            readError: function(filename, message) {
                return "<file name=\"" + xmlEscape(filename) + "\"><error line=\"0\" column=\"0\" severty=\"error\" message=\"" + xmlEscape(message) + "\"></error></file>";
            },
            formatResults: function(results, filename /*, options*/ ) {
                var messages = results.messages,
                    output = [];
                var generateSource = function(rule) {
                    if (!rule || !("name" in rule)) {
                        return "";
                    }
                    return "net.csslint." + rule.name.replace(/\s/g, "");
                };



                if (messages.length > 0) {
                    output.push("<file name=\"" + filename + "\">");
                    CSSLint.Util.forEach(messages, function(message) {
                        if (!message.rollup) {
                            output.push("<error line=\"" + message.line + "\" column=\"" + message.col + "\" severity=\"" + message.type + "\"" +
                                " message=\"" + xmlEscape(message.message) + "\" source=\"" + generateSource(message.rule) + "\"/>");
                        }
                    });
                    output.push("</file>");
                }

                return output.join("");
            }
        });

    }());

    CSSLint.addFormatter({
        id: "compact",
        name: "Compact, 'porcelain' format",
        startFormat: function() {
            return "";
        },
        endFormat: function() {
            return "";
        },
        formatResults: function(results, filename, options) {
            var messages = results.messages,
                output = "";
            options = options || {};
            var capitalize = function(str) {
                return str.charAt(0).toUpperCase() + str.slice(1);
            };

            if (messages.length === 0) {
                return options.quiet ? "" : filename + ": Lint Free!";
            }

            CSSLint.Util.forEach(messages, function(message) {
                if (message.rollup) {
                    output += filename + ": " + capitalize(message.type) + " - " + message.message + "\n";
                } else {
                    output += filename + ": " + "line " + message.line +
                        ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
                }
            });

            return output;
        }
    });

    CSSLint.addFormatter({
        id: "csslint-xml",
        name: "CSSLint XML format",
        startFormat: function() {
            return "<?xml version=\"1.0\" encoding=\"utf-8\"?><csslint>";
        },
        endFormat: function() {
            return "</csslint>";
        },
        formatResults: function(results, filename /*, options*/ ) {
            var messages = results.messages,
                output = [];
            var escapeSpecialCharacters = function(str) {
                if (!str || str.constructor !== String) {
                    return "";
                }
                return str.replace(/\"/g, "'").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
            };

            if (messages.length > 0) {
                output.push("<file name=\"" + filename + "\">");
                CSSLint.Util.forEach(messages, function(message) {
                    if (message.rollup) {
                        output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
                    } else {
                        output.push("<issue line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
                            " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
                    }
                });
                output.push("</file>");
            }

            return output.join("");
        }
    });

    CSSLint.addFormatter({
        id: "junit-xml",
        name: "JUNIT XML format",
        startFormat: function() {
            return "<?xml version=\"1.0\" encoding=\"utf-8\"?><testsuites>";
        },
        endFormat: function() {
            return "</testsuites>";
        },
        formatResults: function(results, filename /*, options*/ ) {

            var messages = results.messages,
                output = [],
                tests = {
                    "error": 0,
                    "failure": 0
                };
            var generateSource = function(rule) {
                if (!rule || !("name" in rule)) {
                    return "";
                }
                return "net.csslint." + rule.name.replace(/\s/g, "");
            };
            var escapeSpecialCharacters = function(str) {

                if (!str || str.constructor !== String) {
                    return "";
                }

                return str.replace(/\"/g, "'").replace(/</g, "&lt;").replace(/>/g, "&gt;");

            };

            if (messages.length > 0) {

                messages.forEach(function(message) {
                    var type = message.type === "warning" ? "error" : message.type;
                    if (!message.rollup) {
                        output.push("<testcase time=\"0\" name=\"" + generateSource(message.rule) + "\">");
                        output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\"><![CDATA[" + message.line + ":" + message.col + ":" + escapeSpecialCharacters(message.evidence) + "]]></" + type + ">");
                        output.push("</testcase>");

                        tests[type] += 1;

                    }

                });

                output.unshift("<testsuite time=\"0\" tests=\"" + messages.length + "\" skipped=\"0\" errors=\"" + tests.error + "\" failures=\"" + tests.failure + "\" package=\"net.csslint\" name=\"" + filename + "\">");
                output.push("</testsuite>");

            }

            return output.join("");

        }
    });

    CSSLint.addFormatter({
        id: "lint-xml",
        name: "Lint XML format",
        startFormat: function() {
            return "<?xml version=\"1.0\" encoding=\"utf-8\"?><lint>";
        },
        endFormat: function() {
            return "</lint>";
        },
        formatResults: function(results, filename /*, options*/ ) {
            var messages = results.messages,
                output = [];
            var escapeSpecialCharacters = function(str) {
                if (!str || str.constructor !== String) {
                    return "";
                }
                return str.replace(/\"/g, "'").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
            };

            if (messages.length > 0) {

                output.push("<file name=\"" + filename + "\">");
                CSSLint.Util.forEach(messages, function(message) {
                    if (message.rollup) {
                        output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
                    } else {
                        output.push("<issue line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
                            " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
                    }
                });
                output.push("</file>");
            }

            return output.join("");
        }
    });

    CSSLint.addFormatter({
        id: "text",
        name: "Plain Text",
        startFormat: function() {
            return "";
        },
        endFormat: function() {
            return "";
        },
        formatResults: function(results, filename, options) {
            var messages = results.messages,
                output = "";
            options = options || {};

            if (messages.length === 0) {
                return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + ".";
            }

            output = "\n\ncsslint: There ";
            if (messages.length === 1) {
                output += "is 1 problem";
            } else {
                output += "are " + messages.length + " problems";
            }
            output += " in " + filename + ".";

            var pos = filename.lastIndexOf("/"),
                shortFilename = filename;

            if (pos === -1) {
                pos = filename.lastIndexOf("\\");
            }
            if (pos > -1) {
                shortFilename = filename.substring(pos + 1);
            }

            CSSLint.Util.forEach(messages, function(message, i) {
                output = output + "\n\n" + shortFilename;
                if (message.rollup) {
                    output += "\n" + (i + 1) + ": " + message.type;
                    output += "\n" + message.message;
                } else {
                    output += "\n" + (i + 1) + ": " + message.type + " at line " + message.line + ", col " + message.col;
                    output += "\n" + message.message;
                    output += "\n" + message.evidence;
                }
            });

            return output;
        }
    });

    module.exports.CSSLint = CSSLint;

});

ace.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) {
    "use strict";

    var oop = require("../lib/oop");
    var lang = require("../lib/lang");
    var Mirror = require("../worker/mirror").Mirror;
    var CSSLint = require("./css/csslint").CSSLint;

    var Worker = exports.Worker = function(sender) {
        Mirror.call(this, sender);
        this.setTimeout(400);
        this.ruleset = null;
        this.setDisabledRules("ids|order-alphabetical");
        this.setInfoRules(
            "adjoining-classes|qualified-headings|zero-units|gradients|" +
            "import|outline-none|vendor-prefix"
        );
    };

    oop.inherits(Worker, Mirror);

    (function() {
        this.setInfoRules = function(ruleNames) {
            if (typeof ruleNames == "string")
                ruleNames = ruleNames.split("|");
            this.infoRules = lang.arrayToMap(ruleNames);
            this.doc.getValue() && this.deferredUpdate.schedule(100);
        };

        this.setDisabledRules = function(ruleNames) {
            if (!ruleNames) {
                this.ruleset = null;
            } else {
                if (typeof ruleNames == "string")
                    ruleNames = ruleNames.split("|");
                var all = {};

                CSSLint.getRules().forEach(function(x) {
                    all[x.id] = true;
                });
                ruleNames.forEach(function(x) {
                    delete all[x];
                });

                this.ruleset = all;
            }
            this.doc.getValue() && this.deferredUpdate.schedule(100);
        };

        this.onUpdate = function() {
            var value = this.doc.getValue();
            if (!value)
                return this.sender.emit("csslint", []);
            var infoRules = this.infoRules;

            var result = CSSLint.verify(value, this.ruleset);
            this.sender.emit("csslint", result.messages.map(function(msg) {
                return {
                    row: msg.line - 1,
                    column: msg.col - 1,
                    text: msg.message,
                    type: infoRules[msg.rule.id] ? "info" : msg.type,
                    rule: msg.rule.name
                }
            }));
        };

    }).call(Worker.prototype);

});

ace.define("ace/lib/es5-shim", ["require", "exports", "module"], function(require, exports, module) {

    function Empty() {}

    if (!Function.prototype.bind) {
        Function.prototype.bind = function bind(that) { // .length is 1
            var target = this;
            if (typeof target != "function") {
                throw new TypeError("Function.prototype.bind called on incompatible " + target);
            }
            var args = slice.call(arguments, 1); // for normal call
            var bound = function() {

                if (this instanceof bound) {

                    var result = target.apply(
                        this,
                        args.concat(slice.call(arguments))
                    );
                    if (Object(result) === result) {
                        return result;
                    }
                    return this;

                } else {
                    return target.apply(
                        that,
                        args.concat(slice.call(arguments))
                    );

                }

            };
            if (target.prototype) {
                Empty.prototype = target.prototype;
                bound.prototype = new Empty();
                Empty.prototype = null;
            }
            return bound;
        };
    }
    var call = Function.prototype.call;
    var prototypeOfArray = Array.prototype;
    var prototypeOfObject = Object.prototype;
    var slice = prototypeOfArray.slice;
    var _toString = call.bind(prototypeOfObject.toString);
    var owns = call.bind(prototypeOfObject.hasOwnProperty);
    var defineGetter;
    var defineSetter;
    var lookupGetter;
    var lookupSetter;
    var supportsAccessors;
    if ((supportsAccessors = owns(prototypeOfObject, "__defineGetter__"))) {
        defineGetter = call.bind(prototypeOfObject.__defineGetter__);
        defineSetter = call.bind(prototypeOfObject.__defineSetter__);
        lookupGetter = call.bind(prototypeOfObject.__lookupGetter__);
        lookupSetter = call.bind(prototypeOfObject.__lookupSetter__);
    }
    if ([1, 2].splice(0).length != 2) {
        if (function() { // test IE < 9 to splice bug - see issue #138
            function makeArray(l) {
                var a = new Array(l + 2);
                a[0] = a[1] = 0;
                return a;
            }
            var array = [],
                lengthBefore;

            array.splice.apply(array, makeArray(20));
            array.splice.apply(array, makeArray(26));

            lengthBefore = array.length; //46
            array.splice(5, 0, "XXX"); // add one element

            lengthBefore + 1 == array.length

            if (lengthBefore + 1 == array.length) {
                return true; // has right splice implementation without bugs
            }
        }()) { //IE 6/7
            var array_splice = Array.prototype.splice;
            Array.prototype.splice = function(start, deleteCount) {
                if (!arguments.length) {
                    return [];
                } else {
                    return array_splice.apply(this, [
                        start === void 0 ? 0 : start,
                        deleteCount === void 0 ? (this.length - start) : deleteCount
                    ].concat(slice.call(arguments, 2)))
                }
            };
        } else { //IE8
            Array.prototype.splice = function(pos, removeCount) {
                var length = this.length;
                if (pos > 0) {
                    if (pos > length)
                        pos = length;
                } else if (pos == void 0) {
                    pos = 0;
                } else if (pos < 0) {
                    pos = Math.max(length + pos, 0);
                }

                if (!(pos + removeCount < length))
                    removeCount = length - pos;

                var removed = this.slice(pos, pos + removeCount);
                var insert = slice.call(arguments, 2);
                var add = insert.length;
                if (pos === length) {
                    if (add) {
                        this.push.apply(this, insert);
                    }
                } else {
                    var remove = Math.min(removeCount, length - pos);
                    var tailOldPos = pos + remove;
                    var tailNewPos = tailOldPos + add - remove;
                    var tailCount = length - tailOldPos;
                    var lengthAfterRemove = length - remove;

                    if (tailNewPos < tailOldPos) { // case A
                        for (var i = 0; i < tailCount; ++i) {
                            this[tailNewPos + i] = this[tailOldPos + i];
                        }
                    } else if (tailNewPos > tailOldPos) { // case B
                        for (i = tailCount; i--;) {
                            this[tailNewPos + i] = this[tailOldPos + i];
                        }
                    } // else, add == remove (nothing to do)

                    if (add && pos === lengthAfterRemove) {
                        this.length = lengthAfterRemove; // truncate array
                        this.push.apply(this, insert);
                    } else {
                        this.length = lengthAfterRemove + add; // reserves space
                        for (i = 0; i < add; ++i) {
                            this[pos + i] = insert[i];
                        }
                    }
                }
                return removed;
            };
        }
    }
    if (!Array.isArray) {
        Array.isArray = function isArray(obj) {
            return _toString(obj) == "[object Array]";
        };
    }
    var boxedString = Object("a"),
        splitString = boxedString[0] != "a" || !(0 in boxedString);

    if (!Array.prototype.forEach) {
        Array.prototype.forEach = function forEach(fun /*, thisp*/ ) {
            var object = toObject(this),
                self = splitString && _toString(this) == "[object String]" ?
                    this.split("") :
                    object,
                thisp = arguments[1],
                i = -1,
                length = self.length >>> 0;
            if (_toString(fun) != "[object Function]") {
                throw new TypeError(); // TODO message
            }

            while (++i < length) {
                if (i in self) {
                    fun.call(thisp, self[i], i, object);
                }
            }
        };
    }
    if (!Array.prototype.map) {
        Array.prototype.map = function map(fun /*, thisp*/ ) {
            var object = toObject(this),
                self = splitString && _toString(this) == "[object String]" ?
                    this.split("") :
                    object,
                length = self.length >>> 0,
                result = Array(length),
                thisp = arguments[1];
            if (_toString(fun) != "[object Function]") {
                throw new TypeError(fun + " is not a function");
            }

            for (var i = 0; i < length; i++) {
                if (i in self)
                    result[i] = fun.call(thisp, self[i], i, object);
            }
            return result;
        };
    }
    if (!Array.prototype.filter) {
        Array.prototype.filter = function filter(fun /*, thisp */ ) {
            var object = toObject(this),
                self = splitString && _toString(this) == "[object String]" ?
                    this.split("") :
                    object,
                length = self.length >>> 0,
                result = [],
                value,
                thisp = arguments[1];
            if (_toString(fun) != "[object Function]") {
                throw new TypeError(fun + " is not a function");
            }

            for (var i = 0; i < length; i++) {
                if (i in self) {
                    value = self[i];
                    if (fun.call(thisp, value, i, object)) {
                        result.push(value);
                    }
                }
            }
            return result;
        };
    }
    if (!Array.prototype.every) {
        Array.prototype.every = function every(fun /*, thisp */ ) {
            var object = toObject(this),
                self = splitString && _toString(this) == "[object String]" ?
                    this.split("") :
                    object,
                length = self.length >>> 0,
                thisp = arguments[1];
            if (_toString(fun) != "[object Function]") {
                throw new TypeError(fun + " is not a function");
            }

            for (var i = 0; i < length; i++) {
                if (i in self && !fun.call(thisp, self[i], i, object)) {
                    return false;
                }
            }
            return true;
        };
    }
    if (!Array.prototype.some) {
        Array.prototype.some = function some(fun /*, thisp */ ) {
            var object = toObject(this),
                self = splitString && _toString(this) == "[object String]" ?
                    this.split("") :
                    object,
                length = self.length >>> 0,
                thisp = arguments[1];
            if (_toString(fun) != "[object Function]") {
                throw new TypeError(fun + " is not a function");
            }

            for (var i = 0; i < length; i++) {
                if (i in self && fun.call(thisp, self[i], i, object)) {
                    return true;
                }
            }
            return false;
        };
    }
    if (!Array.prototype.reduce) {
        Array.prototype.reduce = function reduce(fun /*, initial*/ ) {
            var object = toObject(this),
                self = splitString && _toString(this) == "[object String]" ?
                    this.split("") :
                    object,
                length = self.length >>> 0;
            if (_toString(fun) != "[object Function]") {
                throw new TypeError(fun + " is not a function");
            }
            if (!length && arguments.length == 1) {
                throw new TypeError("reduce of empty array with no initial value");
            }

            var i = 0;
            var result;
            if (arguments.length >= 2) {
                result = arguments[1];
            } else {
                do {
                    if (i in self) {
                        result = self[i++];
                        break;
                    }
                    if (++i >= length) {
                        throw new TypeError("reduce of empty array with no initial value");
                    }
                } while (true);
            }

            for (; i < length; i++) {
                if (i in self) {
                    result = fun.call(void 0, result, self[i], i, object);
                }
            }

            return result;
        };
    }
    if (!Array.prototype.reduceRight) {
        Array.prototype.reduceRight = function reduceRight(fun /*, initial*/ ) {
            var object = toObject(this),
                self = splitString && _toString(this) == "[object String]" ?
                    this.split("") :
                    object,
                length = self.length >>> 0;
            if (_toString(fun) != "[object Function]") {
                throw new TypeError(fun + " is not a function");
            }
            if (!length && arguments.length == 1) {
                throw new TypeError("reduceRight of empty array with no initial value");
            }

            var result, i = length - 1;
            if (arguments.length >= 2) {
                result = arguments[1];
            } else {
                do {
                    if (i in self) {
                        result = self[i--];
                        break;
                    }
                    if (--i < 0) {
                        throw new TypeError("reduceRight of empty array with no initial value");
                    }
                } while (true);
            }

            do {
                if (i in this) {
                    result = fun.call(void 0, result, self[i], i, object);
                }
            } while (i--);

            return result;
        };
    }
    if (!Array.prototype.indexOf || ([0, 1].indexOf(1, 2) != -1)) {
        Array.prototype.indexOf = function indexOf(sought /*, fromIndex */ ) {
            var self = splitString && _toString(this) == "[object String]" ?
                this.split("") :
                toObject(this),
                length = self.length >>> 0;

            if (!length) {
                return -1;
            }

            var i = 0;
            if (arguments.length > 1) {
                i = toInteger(arguments[1]);
            }
            i = i >= 0 ? i : Math.max(0, length + i);
            for (; i < length; i++) {
                if (i in self && self[i] === sought) {
                    return i;
                }
            }
            return -1;
        };
    }
    if (!Array.prototype.lastIndexOf || ([0, 1].lastIndexOf(0, -3) != -1)) {
        Array.prototype.lastIndexOf = function lastIndexOf(sought /*, fromIndex */ ) {
            var self = splitString && _toString(this) == "[object String]" ?
                this.split("") :
                toObject(this),
                length = self.length >>> 0;

            if (!length) {
                return -1;
            }
            var i = length - 1;
            if (arguments.length > 1) {
                i = Math.min(i, toInteger(arguments[1]));
            }
            i = i >= 0 ? i : length - Math.abs(i);
            for (; i >= 0; i--) {
                if (i in self && sought === self[i]) {
                    return i;
                }
            }
            return -1;
        };
    }
    if (!Object.getPrototypeOf) {
        Object.getPrototypeOf = function getPrototypeOf(object) {
            return object.__proto__ || (
                object.constructor ?
                object.constructor.prototype :
                prototypeOfObject
            );
        };
    }
    if (!Object.getOwnPropertyDescriptor) {
        var ERR_NON_OBJECT = "Object.getOwnPropertyDescriptor called on a " +
            "non-object: ";
        Object.getOwnPropertyDescriptor = function getOwnPropertyDescriptor(object, property) {
            if ((typeof object != "object" && typeof object != "function") || object === null)
                throw new TypeError(ERR_NON_OBJECT + object);
            if (!owns(object, property))
                return;

            var descriptor, getter, setter;
            descriptor = {
                enumerable: true,
                configurable: true
            };
            if (supportsAccessors) {
                var prototype = object.__proto__;
                object.__proto__ = prototypeOfObject;

                var getter = lookupGetter(object, property);
                var setter = lookupSetter(object, property);
                object.__proto__ = prototype;

                if (getter || setter) {
                    if (getter) descriptor.get = getter;
                    if (setter) descriptor.set = setter;
                    return descriptor;
                }
            }
            descriptor.value = object[property];
            return descriptor;
        };
    }
    if (!Object.getOwnPropertyNames) {
        Object.getOwnPropertyNames = function getOwnPropertyNames(object) {
            return Object.keys(object);
        };
    }
    if (!Object.create) {
        var createEmpty;
        if (Object.prototype.__proto__ === null) {
            createEmpty = function() {
                return {
                    "__proto__": null
                };
            };
        } else {
            createEmpty = function() {
                var empty = {};
                for (var i in empty)
                    empty[i] = null;
                empty.constructor =
                    empty.hasOwnProperty =
                    empty.propertyIsEnumerable =
                    empty.isPrototypeOf =
                    empty.toLocaleString =
                    empty.toString =
                    empty.valueOf =
                    empty.__proto__ = null;
                return empty;
            }
        }

        Object.create = function create(prototype, properties) {
            var object;
            if (prototype === null) {
                object = createEmpty();
            } else {
                if (typeof prototype != "object")
                    throw new TypeError("typeof prototype[" + (typeof prototype) + "] != 'object'");
                var Type = function() {};
                Type.prototype = prototype;
                object = new Type();
                object.__proto__ = prototype;
            }
            if (properties !== void 0)
                Object.defineProperties(object, properties);
            return object;
        };
    }

    function doesDefinePropertyWork(object) {
        try {
            Object.defineProperty(object, "sentinel", {});
            return "sentinel" in object;
        } catch (exception) {}
    }
    if (Object.defineProperty) {
        var definePropertyWorksOnObject = doesDefinePropertyWork({});
        var definePropertyWorksOnDom = typeof document == "undefined" ||
            doesDefinePropertyWork(document.createElement("div"));
        if (!definePropertyWorksOnObject || !definePropertyWorksOnDom) {
            var definePropertyFallback = Object.defineProperty;
        }
    }

    if (!Object.defineProperty || definePropertyFallback) {
        var ERR_NON_OBJECT_DESCRIPTOR = "Property description must be an object: ";
        var ERR_NON_OBJECT_TARGET = "Object.defineProperty called on non-object: "
        var ERR_ACCESSORS_NOT_SUPPORTED = "getters & setters can not be defined " +
            "on this javascript engine";

        Object.defineProperty = function defineProperty(object, property, descriptor) {
            if ((typeof object != "object" && typeof object != "function") || object === null)
                throw new TypeError(ERR_NON_OBJECT_TARGET + object);
            if ((typeof descriptor != "object" && typeof descriptor != "function") || descriptor === null)
                throw new TypeError(ERR_NON_OBJECT_DESCRIPTOR + descriptor);
            if (definePropertyFallback) {
                try {
                    return definePropertyFallback.call(Object, object, property, descriptor);
                } catch (exception) {}
            }
            if (owns(descriptor, "value")) {

                if (supportsAccessors && (lookupGetter(object, property) ||
                    lookupSetter(object, property))) {
                    var prototype = object.__proto__;
                    object.__proto__ = prototypeOfObject;
                    delete object[property];
                    object[property] = descriptor.value;
                    object.__proto__ = prototype;
                } else {
                    object[property] = descriptor.value;
                }
            } else {
                if (!supportsAccessors)
                    throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
                if (owns(descriptor, "get"))
                    defineGetter(object, property, descriptor.get);
                if (owns(descriptor, "set"))
                    defineSetter(object, property, descriptor.set);
            }

            return object;
        };
    }
    if (!Object.defineProperties) {
        Object.defineProperties = function defineProperties(object, properties) {
            for (var property in properties) {
                if (owns(properties, property))
                    Object.defineProperty(object, property, properties[property]);
            }
            return object;
        };
    }
    if (!Object.seal) {
        Object.seal = function seal(object) {
            return object;
        };
    }
    if (!Object.freeze) {
        Object.freeze = function freeze(object) {
            return object;
        };
    }
    try {
        Object.freeze(function() {});
    } catch (exception) {
        Object.freeze = (function freeze(freezeObject) {
            return function freeze(object) {
                if (typeof object == "function") {
                    return object;
                } else {
                    return freezeObject(object);
                }
            };
        })(Object.freeze);
    }
    if (!Object.preventExtensions) {
        Object.preventExtensions = function preventExtensions(object) {
            return object;
        };
    }
    if (!Object.isSealed) {
        Object.isSealed = function isSealed(object) {
            return false;
        };
    }
    if (!Object.isFrozen) {
        Object.isFrozen = function isFrozen(object) {
            return false;
        };
    }
    if (!Object.isExtensible) {
        Object.isExtensible = function isExtensible(object) {
            if (Object(object) === object) {
                throw new TypeError(); // TODO message
            }
            var name = '';
            while (owns(object, name)) {
                name += '?';
            }
            object[name] = true;
            var returnValue = owns(object, name);
            delete object[name];
            return returnValue;
        };
    }
    if (!Object.keys) {
        var hasDontEnumBug = true,
            dontEnums = [
                "toString",
                "toLocaleString",
                "valueOf",
                "hasOwnProperty",
                "isPrototypeOf",
                "propertyIsEnumerable",
                "constructor"
            ],
            dontEnumsLength = dontEnums.length;

        for (var key in {
            "toString": null
        }) {
            hasDontEnumBug = false;
        }

        Object.keys = function keys(object) {

            if (
                (typeof object != "object" && typeof object != "function") ||
                object === null
            ) {
                throw new TypeError("Object.keys called on a non-object");
            }

            var keys = [];
            for (var name in object) {
                if (owns(object, name)) {
                    keys.push(name);
                }
            }

            if (hasDontEnumBug) {
                for (var i = 0, ii = dontEnumsLength; i < ii; i++) {
                    var dontEnum = dontEnums[i];
                    if (owns(object, dontEnum)) {
                        keys.push(dontEnum);
                    }
                }
            }
            return keys;
        };

    }
    if (!Date.now) {
        Date.now = function now() {
            return new Date().getTime();
        };
    }
    var ws = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003" +
        "\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028" +
        "\u2029\uFEFF";
    if (!String.prototype.trim || ws.trim()) {
        ws = "[" + ws + "]";
        var trimBeginRegexp = new RegExp("^" + ws + ws + "*"),
            trimEndRegexp = new RegExp(ws + ws + "*$");
        String.prototype.trim = function trim() {
            return String(this).replace(trimBeginRegexp, "").replace(trimEndRegexp, "");
        };
    }

    function toInteger(n) {
        n = +n;
        if (n !== n) { // isNaN
            n = 0;
        } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
            n = (n > 0 || -1) * Math.floor(Math.abs(n));
        }
        return n;
    }

    function isPrimitive(input) {
        var type = typeof input;
        return (
            input === null ||
            type === "undefined" ||
            type === "boolean" ||
            type === "number" ||
            type === "string"
        );
    }

    function toPrimitive(input) {
        var val, valueOf, toString;
        if (isPrimitive(input)) {
            return input;
        }
        valueOf = input.valueOf;
        if (typeof valueOf === "function") {
            val = valueOf.call(input);
            if (isPrimitive(val)) {
                return val;
            }
        }
        toString = input.toString;
        if (typeof toString === "function") {
            val = toString.call(input);
            if (isPrimitive(val)) {
                return val;
            }
        }
        throw new TypeError();
    }
    var toObject = function(o) {
        if (o == null) { // this matches both null and undefined
            throw new TypeError("can't convert " + o + " to object");
        }
        return Object(o);
    };

});