mediawiki-extensions-CodeEd.../modules/ace/worker-css.js
Derk-Jan Hartman 05e6be051c CodeEditor: Update to package 12.02.2013 (December 2013)
ACE defaults to a white background now. I have no preference in
this, but it is caused by a change in the TextMate theme. There is a
bugreport (Bug 55423) about the old blue background.

This fixes an annoying problem with Safari 7, where characters are no
longer properly measured by ACE.
https://github.com/ajaxorg/ace/issues/1534

Fixes double-click to select:
https://github.com/ajaxorg/ace/issues/956

Set proper basePath so that require works, allowing conditional loading
of ACE resources. Need for Find to work after this update

Bug: 55423
Bug: 45876
Bug: 58521
Change-Id: Ia64b67b4553f77c6ba3d2aefec4bab62d111deb7
2014-01-02 11:11:32 +01:00

8317 lines
276 KiB
JavaScript

"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) {
console.error("Worker " + err.stack);
};
window.normalizeModule = function(parentId, moduleName) {
if (moduleName.indexOf("!") !== -1) {
var chunks = moduleName.split("!");
return window.normalizeModule(parentId, chunks[0]) + "!" + window.normalizeModule(parentId, chunks[1]);
}
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 (!deps.length)
deps = ['require', 'exports', 'module']
if (id.indexOf("text!") === 0)
return;
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) {
case 'require': return req
case 'exports': return module.exports
case 'module': return module
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();
}
window.main = null;
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 = initSender();
var clazz = require(msg.module)[msg.classname];
main = new clazz(sender);
}
else if (msg.event && sender) {
sender._emit(msg.event, msg.data);
}
};
})(this);// https://github.com/kriskowal/es5-shim
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);
};
});
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) {
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");
this.setInfoRules("adjoining-classes|qualified-headings|zero-units|gradients|import|outline-none");
};
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();
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);
});
define('ace/lib/oop', ['require', 'exports', 'module' ], function(require, exports, module) {
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);
};
});
define('ace/lib/lang', ['require', 'exports', 'module' ], function(require, exports, module) {
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;
};
});
define('ace/worker/mirror', ['require', 'exports', 'module' , 'ace/document', 'ace/lib/lang'], function(require, exports, module) {
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);
});
define('ace/document', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/event_emitter', 'ace/range', 'ace/anchor'], function(require, exports, module) {
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.getNewLineCharacter = function() {
switch (this.$newLineMode) {
case "windows":
return "\r\n";
case "unix":
return "\n";
default:
return this.$autoNewLine;
}
};
this.$autoNewLine = "\n";
this.$newLineMode = "auto";
this.setNewLineMode = function(newLineMode) {
if (this.$newLineMode === newLineMode)
return;
this.$newLineMode = newLineMode;
};
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};
if (lines.length > 0xFFFF) {
var end = this._insertLines(row, lines.slice(0xFFFF));
lines = lines.slice(0, 0xFFFF);
}
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._emit("change", { data: delta });
return end || 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._emit("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._emit("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._emit("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._emit("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._emit("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;
});
define('ace/lib/event_emitter', ['require', 'exports', 'module' ], function(require, exports, module) {
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;
});
define('ace/range', ['require', 'exports', 'module' ], function(require, exports, module) {
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;
});
define('ace/anchor', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/event_emitter'], function(require, exports, module) {
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) {
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._emit("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);
});
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.toString();
},
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",
darkgreen :"#006400",
darkkhaki :"#bdb76b",
darkmagenta :"#8b008b",
darkolivegreen :"#556b2f",
darkorange :"#ff8c00",
darkorchid :"#9932cc",
darkred :"#8b0000",
darksalmon :"#e9967a",
darkseagreen :"#8fbc8f",
darkslateblue :"#483d8b",
darkslategray :"#2f4f4f",
darkturquoise :"#00ced1",
darkviolet :"#9400d3",
deeppink :"#ff1493",
deepskyblue :"#00bfff",
dimgray :"#696969",
dodgerblue :"#1e90ff",
firebrick :"#b22222",
floralwhite :"#fffaf0",
forestgreen :"#228b22",
fuchsia :"#ff00ff",
gainsboro :"#dcdcdc",
ghostwhite :"#f8f8ff",
gold :"#ffd700",
goldenrod :"#daa520",
gray :"#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",
lightgreen :"#90ee90",
lightpink :"#ffb6c1",
lightsalmon :"#ffa07a",
lightseagreen :"#20b2aa",
lightskyblue :"#87cefa",
lightslategray :"#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",
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.",
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 (!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();
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();
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(){
var tokenStream = this._tokenStream,
unary = null,
value = 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 (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 = {
"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-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-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",
"box-align" : "start | end | center | baseline | stretch", //http://www.w3.org/TR/2009/WD-css3-flexbox-20090723/
"box-decoration-break" : "slice |clone",
"box-direction" : "normal | reverse | inherit",
"box-flex" : "<number>",
"box-flex-group" : "<integer>",
"box-lines" : "single | multiple",
"box-ordinal-group" : "<integer>",
"box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit",
"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 | box | inline-box | 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",
"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,
"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> | 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>",
"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> | none | inherit",
"max-width" : "<length> | <percentage> | none | inherit",
"min-height" : "<length> | <percentage> | inherit",
"min-width" : "<length> | <percentage> | inherit",
"move-to" : 1,
"nav-down" : 1,
"nav-index" : 1,
"nav-left" : 1,
"nav-right" : 1,
"nav-up" : 1,
"opacity" : "<number> | inherit",
"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-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",
"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 | bidi-override | 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> | auto | inherit" ,
"word-break" : "normal | keep-all | break-all",
"word-spacing" : "<length> | normal | inherit",
"word-wrap" : 1,
"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 "vm":
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)){ //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,
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);
if (tt == -1){
tt = Tokens.CHAR;
}
return this.createToken(tt, c, startLine, startCol);
},
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$|^vm$|^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"},
{ 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",
text: "{"
},
{
name: "RBRACE",
text: "}"
},
{
name: "LBRACKET",
text: "["
},
{
name: "RBRACKET",
text: "]"
},
{
name: "EQUALS",
text: "="
},
{
name: "COLON",
text: ":"
},
{
name: "SEMICOLON",
text: ";"
},
{
name: "LPAREN",
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");
},
"<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";
}
},
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;
}
}
};
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];
}
})();
var CSSLint = (function(){
var rules = [],
formatters = [],
embeddedRuleset = /\/\*csslint([^\*]*)\*\//,
api = new parserlib.util.EventTarget();
api.version = "0.10.0";
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,
len = rules.length,
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 = 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 == "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,
count = 0,
fontFaceRule = false,
firstSrc = true,
ruleFailed = false,
line, col;
parser.addListener("startfontface", function(event){
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(event){
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 (event) {
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 (event) {
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(event){
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(event){
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 == "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){
var name = event.property.text.toLowerCase();
if (event.invalid) {
reporter.report(event.invalid.message, 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(event){
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 == "none" || value == "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 rule = this,
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: "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(event){
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(),
value = event.value.parts[0].value;
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(event){
textIndent = false;
direction = "inherit";
}
function endRule(event){
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 == "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(event){
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,
modifier,
i, j, k;
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, j, 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",
"-moz-user-select" : "user-select",
"-khtml-user-select" : "user-select",
"-webkit-user-select" : "user-select"
};
function startRule(){
properties = {};
num=1;
}
function endRule(event){
var prop,
i, len,
standard,
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, i) {
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, i) {
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 + "\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, i) {
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, i) {
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, i) {
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 are " + messages.length + " problems 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;
}
});
exports.CSSLint = CSSLint;
});