aboutsummaryrefslogtreecommitdiff
path: root/contexts/data/lib/ace/worker-css.js
diff options
context:
space:
mode:
Diffstat (limited to 'contexts/data/lib/ace/worker-css.js')
-rw-r--r--contexts/data/lib/ace/worker-css.js6361
1 files changed, 2292 insertions, 4069 deletions
diff --git a/contexts/data/lib/ace/worker-css.js b/contexts/data/lib/ace/worker-css.js
index 7b17906..f4cc2cb 100644
--- a/contexts/data/lib/ace/worker-css.js
+++ b/contexts/data/lib/ace/worker-css.js
@@ -1,8 +1,16 @@
"no use strict";
+if (typeof window != "undefined" && window.document)
+ throw "atempt to load ace worker into main window instead of webWorker";
+
var console = {
- log: function(msg) {
- postMessage({type: "log", data: msg});
+ log: function() {
+ var msgs = Array.prototype.slice.call(arguments, 0);
+ postMessage({type: "log", data: msgs});
+ },
+ error: function() {
+ var msgs = Array.prototype.slice.call(arguments, 0);
+ postMessage({type: "log", data: msgs});
}
};
var window = {
@@ -18,11 +26,11 @@ var normalizeModule = function(parentId, moduleName) {
// normalize relative requires
if (moduleName.charAt(0) == ".") {
var base = parentId.split("/").slice(0, -1).join("/");
- var moduleName = base + "/" + moduleName;
+ moduleName = base + "/" + moduleName;
while(moduleName.indexOf(".") !== -1 && previous != moduleName) {
var previous = moduleName;
- var moduleName = moduleName.replace(/\/\.\//, "/").replace(/[^\/]+\/\.\.\//, "");
+ moduleName = moduleName.replace(/\/\.\//, "/").replace(/[^\/]+\/\.\.\//, "");
}
}
@@ -30,13 +38,16 @@ var normalizeModule = function(parentId, moduleName) {
};
var require = function(parentId, id) {
- var id = normalizeModule(parentId, id);
-
+ if (!id.charAt)
+ throw new Error("worker.js require() accepts only (parentId, id) as arguments");
+
+ id = normalizeModule(parentId, id);
+
var module = require.modules[id];
if (module) {
if (!module.initialized) {
- module.exports = module.factory().exports;
module.initialized = true;
+ module.exports = module.factory().exports;
}
return module.exports;
}
@@ -56,6 +67,10 @@ require.tlns = {};
var define = function(id, deps, factory) {
if (arguments.length == 2) {
factory = deps;
+ if (typeof id != "string") {
+ deps = id;
+ id = require.id;
+ }
} else if (arguments.length == 1) {
factory = id;
id = require.id;
@@ -123,7 +138,10 @@ var sender;
onmessage = function(e) {
var msg = e.data;
if (msg.command) {
- main[msg.command].apply(main, msg.args);
+ 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);
@@ -137,40 +155,16 @@ onmessage = function(e) {
}
};
// vim:set ts=4 sts=4 sw=4 st:
-// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License
-// -- tlrobinson Tom Robinson Copyright (C) 2009-2010 MIT License (Narwhal Project)
-// -- dantman Daniel Friesen Copyright(C) 2010 XXX No License Specified
-// -- fschaefer Florian Schäfer Copyright (C) 2010 MIT License
-// -- Irakli Gozalishvili Copyright (C) 2010 MIT License
-
-/*!
- Copyright (c) 2009, 280 North Inc. http://280north.com/
- MIT License. http://github.com/280north/narwhal/blob/master/README.md
-*/
define('ace/lib/fixoldbrowsers', ['require', 'exports', 'module' , 'ace/lib/regexp', 'ace/lib/es5-shim'], function(require, exports, module) {
-"use strict";
+
require("./regexp");
require("./es5-shim");
-});/**
- * Based on code from:
- *
- * XRegExp 1.5.0
- * (c) 2007-2010 Steven Levithan
- * MIT License
- * <http://xregexp.com>
- * Provides an augmented, extensible, cross-browser implementation of regular expressions,
- * including support for additional syntax, flags, and methods
- */
+});
define('ace/lib/regexp', ['require', 'exports', 'module' ], function(require, exports, module) {
-"use strict";
-
- //---------------------------------
- // Private variables
- //---------------------------------
var real = {
exec: RegExp.prototype.exec,
@@ -186,25 +180,14 @@ define('ace/lib/regexp', ['require', 'exports', 'module' ], function(require, ex
return !x.lastIndex;
}();
- //---------------------------------
- // Overriden native methods
- //---------------------------------
-
- // Adds named capture support (with backreferences returned as `result.name`), and fixes two
- // cross-browser issues per ES3:
- // - Captured values for nonparticipating capturing groups should be returned as `undefined`,
- // rather than the empty string.
- // - `lastIndex` should not be incremented after zero-length matches.
+ if (compliantLastIndexIncrement && compliantExecNpcg)
+ return;
RegExp.prototype.exec = function (str) {
var match = real.exec.apply(this, arguments),
name, r2;
- if (match) {
- // Fix browsers whose `exec` methods don't consistently return `undefined` for
- // nonparticipating capturing groups
+ if ( typeof(str) == 'string' && match) {
if (!compliantExecNpcg && match.length > 1 && indexOf(match, "") > -1) {
r2 = RegExp(this.source, real.replace.call(getNativeFlags(this), "g", ""));
- // Using `str.slice(match.index)` rather than `match[0]` in case lookahead allowed
- // matching due to characters outside the match
real.replace.call(str.slice(match.index), r2, function () {
for (var i = 1; i < arguments.length - 2; i++) {
if (arguments[i] === undefined)
@@ -212,7 +195,6 @@ define('ace/lib/regexp', ['require', 'exports', 'module' ], function(require, ex
}
});
}
- // Attach named capture properties
if (this._xregexp && this._xregexp.captureNames) {
for (var i = 1; i < match.length; i++) {
name = this._xregexp.captureNames[i - 1];
@@ -220,38 +202,27 @@ define('ace/lib/regexp', ['require', 'exports', 'module' ], function(require, ex
match[name] = match[i];
}
}
- // Fix browsers that increment `lastIndex` after zero-length matches
if (!compliantLastIndexIncrement && this.global && !match[0].length && (this.lastIndex > match.index))
this.lastIndex--;
}
return match;
};
-
- // Don't override `test` if it won't change anything
if (!compliantLastIndexIncrement) {
- // Fix browser bug in native method
RegExp.prototype.test = function (str) {
- // Use the native `exec` to skip some processing overhead, even though the overriden
- // `exec` would take care of the `lastIndex` fix
var match = real.exec.call(this, str);
- // Fix browsers that increment `lastIndex` after zero-length matches
if (match && this.global && !match[0].length && (this.lastIndex > match.index))
this.lastIndex--;
return !!match;
};
}
- //---------------------------------
- // Private helper functions
- //---------------------------------
-
function getNativeFlags (regex) {
return (regex.global ? "g" : "") +
(regex.ignoreCase ? "i" : "") +
(regex.multiline ? "m" : "") +
(regex.extended ? "x" : "") + // Proposed for ES4; included in AS3
(regex.sticky ? "y" : "");
- };
+ }
function indexOf (array, item, from) {
if (Array.prototype.indexOf) // Use the native array method if available
@@ -261,94 +232,21 @@ define('ace/lib/regexp', ['require', 'exports', 'module' ], function(require, ex
return i;
}
return -1;
- };
+ }
-});// vim: ts=4 sts=4 sw=4 expandtab
-// -- kriskowal Kris Kowal Copyright (C) 2009-2011 MIT License
-// -- tlrobinson Tom Robinson Copyright (C) 2009-2010 MIT License (Narwhal Project)
-// -- dantman Daniel Friesen Copyright (C) 2010 XXX TODO License or CLA
-// -- fschaefer Florian Schäfer Copyright (C) 2010 MIT License
-// -- Gozala Irakli Gozalishvili Copyright (C) 2010 MIT License
-// -- kitcambridge Kit Cambridge Copyright (C) 2011 MIT License
-// -- kossnocorp Sasha Koss XXX TODO License or CLA
-// -- bryanforbes Bryan Forbes XXX TODO License or CLA
-// -- killdream Quildreen Motta Copyright (C) 2011 MIT Licence
-// -- michaelficarra Michael Ficarra Copyright (C) 2011 3-clause BSD License
-// -- sharkbrainguy Gerard Paapu Copyright (C) 2011 MIT License
-// -- bbqsrc Brendan Molloy (C) 2011 Creative Commons Zero (public domain)
-// -- iwyg XXX TODO License or CLA
-// -- DomenicDenicola Domenic Denicola Copyright (C) 2011 MIT License
-// -- xavierm02 Montillet Xavier XXX TODO License or CLA
-// -- Raynos Raynos XXX TODO License or CLA
-// -- samsonjs Sami Samhuri Copyright (C) 2010 MIT License
-// -- rwldrn Rick Waldron Copyright (C) 2011 MIT License
-// -- lexer Alexey Zakharov XXX TODO License or CLA
-
-/*!
- Copyright (c) 2009, 280 North Inc. http://280north.com/
- MIT License. http://github.com/280north/narwhal/blob/master/README.md
-*/
+});
define('ace/lib/es5-shim', ['require', 'exports', 'module' ], function(require, exports, module) {
-/**
- * Brings an environment as close to ECMAScript 5 compliance
- * as is possible with the facilities of erstwhile engines.
- *
- * Annotated ES5: http://es5.github.com/ (specific links below)
- * ES5 Spec: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
- *
- * @module
- */
-
-/*whatsupdoc*/
-
-//
-// Function
-// ========
-//
-
-// ES-5 15.3.4.5
-// http://es5.github.com/#x15.3.4.5
-
if (!Function.prototype.bind) {
Function.prototype.bind = function bind(that) { // .length is 1
- // 1. Let Target be the this value.
var target = this;
- // 2. If IsCallable(Target) is false, throw a TypeError exception.
if (typeof target != "function")
throw new TypeError(); // TODO message
- // 3. Let A be a new (possibly empty) internal list of all of the
- // argument values provided after thisArg (arg1, arg2 etc), in order.
- // XXX slicedArgs will stand in for "A" if used
var args = slice.call(arguments, 1); // for normal call
- // 4. Let F be a new native ECMAScript object.
- // 11. Set the [[Prototype]] internal property of F to the standard
- // built-in Function prototype object as specified in 15.3.3.1.
- // 12. Set the [[Call]] internal property of F as described in
- // 15.3.4.5.1.
- // 13. Set the [[Construct]] internal property of F as described in
- // 15.3.4.5.2.
- // 14. Set the [[HasInstance]] internal property of F as described in
- // 15.3.4.5.3.
var bound = function () {
if (this instanceof bound) {
- // 15.3.4.5.2 [[Construct]]
- // When the [[Construct]] internal method of a function object,
- // F that was created using the bind function is called with a
- // list of arguments ExtraArgs, the following steps are taken:
- // 1. Let target be the value of F's [[TargetFunction]]
- // internal property.
- // 2. If target has no [[Construct]] internal method, a
- // TypeError exception is thrown.
- // 3. Let boundArgs be the value of F's [[BoundArgs]] internal
- // property.
- // 4. Let args be a new list containing the same values as the
- // list boundArgs in the same order followed by the same
- // values as the list ExtraArgs in the same order.
- // 5. Return the result of calling the [[Construct]] internal
- // method of target providing args as the arguments.
var F = function(){};
F.prototype = target.prototype;
@@ -363,25 +261,6 @@ if (!Function.prototype.bind) {
return self;
} else {
- // 15.3.4.5.1 [[Call]]
- // When the [[Call]] internal method of a function object, F,
- // which was created using the bind function is called with a
- // this value and a list of arguments ExtraArgs, the following
- // steps are taken:
- // 1. Let boundArgs be the value of F's [[BoundArgs]] internal
- // property.
- // 2. Let boundThis be the value of F's [[BoundThis]] internal
- // property.
- // 3. Let target be the value of F's [[TargetFunction]] internal
- // property.
- // 4. Let args be a new list containing the same values as the
- // list boundArgs in the same order followed by the same
- // values as the list ExtraArgs in the same order.
- // 5. Return the result of calling the [[Call]] internal method
- // of target providing boundThis as the this value and
- // providing args as the arguments.
-
- // equiv: target.call(this, ...boundArgs, ...args)
return target.apply(
that,
args.concat(slice.call(arguments))
@@ -390,53 +269,15 @@ if (!Function.prototype.bind) {
}
};
- // XXX bound.length is never writable, so don't even try
- //
- // 15. If the [[Class]] internal property of Target is "Function", then
- // a. Let L be the length property of Target minus the length of A.
- // b. Set the length own property of F to either 0 or L, whichever is
- // larger.
- // 16. Else set the length own property of F to 0.
- // 17. Set the attributes of the length own property of F to the values
- // specified in 15.3.5.1.
-
- // TODO
- // 18. Set the [[Extensible]] internal property of F to true.
-
- // TODO
- // 19. Let thrower be the [[ThrowTypeError]] function Object (13.2.3).
- // 20. Call the [[DefineOwnProperty]] internal method of F with
- // arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]:
- // thrower, [[Enumerable]]: false, [[Configurable]]: false}, and
- // false.
- // 21. Call the [[DefineOwnProperty]] internal method of F with
- // arguments "arguments", PropertyDescriptor {[[Get]]: thrower,
- // [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false},
- // and false.
-
- // TODO
- // NOTE Function objects created using Function.prototype.bind do not
- // have a prototype property or the [[Code]], [[FormalParameters]], and
- // [[Scope]] internal properties.
- // XXX can't delete prototype in pure-js.
-
- // 22. Return F.
return bound;
};
}
-
-// Shortcut to an often accessed properties, in order to avoid multiple
-// dereference that costs universally.
-// _Please note: Shortcuts are defined after `Function.prototype.bind` as we
-// us it in defining shortcuts.
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);
-
-// If JS engine supports accessors creating shortcuts.
var defineGetter;
var defineSetter;
var lookupGetter;
@@ -448,70 +289,35 @@ if ((supportsAccessors = owns(prototypeOfObject, "__defineGetter__"))) {
lookupGetter = call.bind(prototypeOfObject.__lookupGetter__);
lookupSetter = call.bind(prototypeOfObject.__lookupSetter__);
}
-
-//
-// Array
-// =====
-//
-
-// ES5 15.4.3.2
-// http://es5.github.com/#x15.4.3.2
-// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray
if (!Array.isArray) {
Array.isArray = function isArray(obj) {
return toString(obj) == "[object Array]";
};
}
-
-// The IsCallable() check in the Array functions
-// has been replaced with a strict check on the
-// internal class of the object to trap cases where
-// the provided function was actually a regular
-// expression literal, which in V8 and
-// JavaScriptCore is a typeof "function". Only in
-// V8 are regular expression literals permitted as
-// reduce parameters, so it is desirable in the
-// general case for the shim to match the more
-// strict and common behavior of rejecting regular
-// expressions.
-
-// ES5 15.4.4.18
-// http://es5.github.com/#x15.4.4.18
-// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/forEach
if (!Array.prototype.forEach) {
Array.prototype.forEach = function forEach(fun /*, thisp*/) {
var self = toObject(this),
thisp = arguments[1],
i = 0,
length = self.length >>> 0;
-
- // If no callback function or if callback is not a callable function
if (toString(fun) != "[object Function]") {
throw new TypeError(); // TODO message
}
while (i < length) {
if (i in self) {
- // Invoke the callback function with call, passing arguments:
- // context, property value, property key, thisArg object context
fun.call(thisp, self[i], i, self);
}
i++;
}
};
}
-
-// ES5 15.4.4.19
-// http://es5.github.com/#x15.4.4.19
-// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map
if (!Array.prototype.map) {
Array.prototype.map = function map(fun /*, thisp*/) {
var self = toObject(this),
length = self.length >>> 0,
result = Array(length),
thisp = arguments[1];
-
- // If no callback function or if callback is not a callable function
if (toString(fun) != "[object Function]") {
throw new TypeError(); // TODO message
}
@@ -523,18 +329,12 @@ if (!Array.prototype.map) {
return result;
};
}
-
-// ES5 15.4.4.20
-// http://es5.github.com/#x15.4.4.20
-// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter
if (!Array.prototype.filter) {
Array.prototype.filter = function filter(fun /*, thisp */) {
var self = toObject(this),
length = self.length >>> 0,
result = [],
thisp = arguments[1];
-
- // If no callback function or if callback is not a callable function
if (toString(fun) != "[object Function]") {
throw new TypeError(); // TODO message
}
@@ -546,17 +346,11 @@ if (!Array.prototype.filter) {
return result;
};
}
-
-// ES5 15.4.4.16
-// http://es5.github.com/#x15.4.4.16
-// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every
if (!Array.prototype.every) {
Array.prototype.every = function every(fun /*, thisp */) {
var self = toObject(this),
length = self.length >>> 0,
thisp = arguments[1];
-
- // If no callback function or if callback is not a callable function
if (toString(fun) != "[object Function]") {
throw new TypeError(); // TODO message
}
@@ -568,17 +362,11 @@ if (!Array.prototype.every) {
return true;
};
}
-
-// ES5 15.4.4.17
-// http://es5.github.com/#x15.4.4.17
-// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some
if (!Array.prototype.some) {
Array.prototype.some = function some(fun /*, thisp */) {
var self = toObject(this),
length = self.length >>> 0,
thisp = arguments[1];
-
- // If no callback function or if callback is not a callable function
if (toString(fun) != "[object Function]") {
throw new TypeError(); // TODO message
}
@@ -590,21 +378,13 @@ if (!Array.prototype.some) {
return false;
};
}
-
-// ES5 15.4.4.21
-// http://es5.github.com/#x15.4.4.21
-// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce
if (!Array.prototype.reduce) {
Array.prototype.reduce = function reduce(fun /*, initial*/) {
var self = toObject(this),
length = self.length >>> 0;
-
- // If no callback function or if callback is not a callable function
if (toString(fun) != "[object Function]") {
throw new TypeError(); // TODO message
}
-
- // no value to return if no initial value and an empty array
if (!length && arguments.length == 1)
throw new TypeError(); // TODO message
@@ -618,8 +398,6 @@ if (!Array.prototype.reduce) {
result = self[i++];
break;
}
-
- // if array contains no values, no initial value to return
if (++i >= length)
throw new TypeError(); // TODO message
} while (true);
@@ -633,21 +411,13 @@ if (!Array.prototype.reduce) {
return result;
};
}
-
-// ES5 15.4.4.22
-// http://es5.github.com/#x15.4.4.22
-// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight
if (!Array.prototype.reduceRight) {
Array.prototype.reduceRight = function reduceRight(fun /*, initial*/) {
var self = toObject(this),
length = self.length >>> 0;
-
- // If no callback function or if callback is not a callable function
if (toString(fun) != "[object Function]") {
throw new TypeError(); // TODO message
}
-
- // no value to return if no initial value, empty array
if (!length && arguments.length == 1)
throw new TypeError(); // TODO message
@@ -660,8 +430,6 @@ if (!Array.prototype.reduceRight) {
result = self[i--];
break;
}
-
- // if array contains no values, no initial value to return
if (--i < 0)
throw new TypeError(); // TODO message
} while (true);
@@ -675,10 +443,6 @@ if (!Array.prototype.reduceRight) {
return result;
};
}
-
-// ES5 15.4.4.14
-// http://es5.github.com/#x15.4.4.14
-// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function indexOf(sought /*, fromIndex */ ) {
var self = toObject(this),
@@ -690,8 +454,6 @@ if (!Array.prototype.indexOf) {
var i = 0;
if (arguments.length > 1)
i = toInteger(arguments[1]);
-
- // handle negative indices
i = i >= 0 ? i : Math.max(0, length + i);
for (; i < length; i++) {
if (i in self && self[i] === sought) {
@@ -701,10 +463,6 @@ if (!Array.prototype.indexOf) {
return -1;
};
}
-
-// ES5 15.4.4.15
-// http://es5.github.com/#x15.4.4.15
-// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/lastIndexOf
if (!Array.prototype.lastIndexOf) {
Array.prototype.lastIndexOf = function lastIndexOf(sought /*, fromIndex */) {
var self = toObject(this),
@@ -715,7 +473,6 @@ if (!Array.prototype.lastIndexOf) {
var i = length - 1;
if (arguments.length > 1)
i = Math.min(i, toInteger(arguments[1]));
- // handle negative indices
i = i >= 0 ? i : length - Math.abs(i);
for (; i >= 0; i--) {
if (i in self && sought === self[i])
@@ -724,18 +481,7 @@ if (!Array.prototype.lastIndexOf) {
return -1;
};
}
-
-//
-// Object
-// ======
-//
-
-// ES5 15.2.3.2
-// http://es5.github.com/#x15.2.3.2
if (!Object.getPrototypeOf) {
- // https://github.com/kriskowal/es5-shim/issues#issue/2
- // http://ejohn.org/blog/objectgetprototypeof/
- // recommended by fschaefer on github
Object.getPrototypeOf = function getPrototypeOf(object) {
return object.__proto__ || (
object.constructor ?
@@ -744,84 +490,73 @@ if (!Object.getPrototypeOf) {
);
};
}
-
-// ES5 15.2.3.3
-// http://es5.github.com/#x15.2.3.3
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 object does not owns property return undefined immediately.
if (!owns(object, property))
return;
var descriptor, getter, setter;
-
- // If object has a property then it's for sure both `enumerable` and
- // `configurable`.
descriptor = { enumerable: true, configurable: true };
-
- // If JS engine supports accessor properties then property may be a
- // getter or setter.
if (supportsAccessors) {
- // Unfortunately `__lookupGetter__` will return a getter even
- // if object has own non getter property along with a same named
- // inherited getter. To avoid misbehavior we temporary remove
- // `__proto__` so that `__lookupGetter__` will return getter only
- // if it's owned by an object.
var prototype = object.__proto__;
object.__proto__ = prototypeOfObject;
var getter = lookupGetter(object, property);
var setter = lookupSetter(object, property);
-
- // Once we have getter and setter we can put values back.
object.__proto__ = prototype;
if (getter || setter) {
if (getter) descriptor.get = getter;
if (setter) descriptor.set = setter;
-
- // If it was accessor property we're done and return here
- // in order to avoid adding `value` to the descriptor.
return descriptor;
}
}
-
- // If we got this far we know that object has an own property that is
- // not an accessor so we set it as a value and return descriptor.
descriptor.value = object[property];
return descriptor;
};
}
-
-// ES5 15.2.3.4
-// http://es5.github.com/#x15.2.3.4
if (!Object.getOwnPropertyNames) {
Object.getOwnPropertyNames = function getOwnPropertyNames(object) {
return Object.keys(object);
};
}
-
-// ES5 15.2.3.5
-// http://es5.github.com/#x15.2.3.5
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 = { "__proto__": 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();
- // IE has no built-in implementation of `Object.getPrototypeOf`
- // neither `__proto__`, but this manually setting `__proto__` will
- // guarantee that `Object.getPrototypeOf` will work as expected with
- // objects created using `Object.create`
object.__proto__ = prototype;
}
if (properties !== void 0)
@@ -830,29 +565,13 @@ if (!Object.create) {
};
}
-// ES5 15.2.3.6
-// http://es5.github.com/#x15.2.3.6
-
-// Patch for WebKit and IE8 standard mode
-// Designed by hax <hax.github.com>
-// related issue: https://github.com/kriskowal/es5-shim/issues#issue/5
-// IE8 Reference:
-// http://msdn.microsoft.com/en-us/library/dd282900.aspx
-// http://msdn.microsoft.com/en-us/library/dd229916.aspx
-// WebKit Bugs:
-// https://bugs.webkit.org/show_bug.cgi?id=36423
-
function doesDefinePropertyWork(object) {
try {
Object.defineProperty(object, "sentinel", {});
return "sentinel" in object;
} catch (exception) {
- // returns falsy
}
}
-
-// check whether defineProperty works if it's given. Otherwise,
-// shim partially.
if (Object.defineProperty) {
var definePropertyWorksOnObject = doesDefinePropertyWork({});
var definePropertyWorksOnDom = typeof document == "undefined" ||
@@ -873,48 +592,21 @@ if (!Object.defineProperty || definePropertyFallback) {
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);
-
- // make a valiant attempt to use the real defineProperty
- // for I8's DOM elements.
if (definePropertyFallback) {
try {
return definePropertyFallback.call(Object, object, property, descriptor);
} catch (exception) {
- // try the shim if the real one doesn't work
}
}
-
- // If it's a data property.
if (owns(descriptor, "value")) {
- // fail silently if "writable", "enumerable", or "configurable"
- // are requested but not supported
- /*
- // alternate approach:
- if ( // can't implement these features; allow false but not true
- !(owns(descriptor, "writable") ? descriptor.writable : true) ||
- !(owns(descriptor, "enumerable") ? descriptor.enumerable : true) ||
- !(owns(descriptor, "configurable") ? descriptor.configurable : true)
- )
- throw new RangeError(
- "This implementation of Object.defineProperty does not " +
- "support configurable, enumerable, or writable."
- );
- */
if (supportsAccessors && (lookupGetter(object, property) ||
lookupSetter(object, property)))
{
- // As accessors are supported only on engines implementing
- // `__proto__` we can safely override `__proto__` while defining
- // a property to make sure that we don't hit an inherited
- // accessor.
var prototype = object.__proto__;
object.__proto__ = prototypeOfObject;
- // Deleting a property anyway since getter / setter may be
- // defined on object itself.
delete object[property];
object[property] = descriptor.value;
- // Setting original `__proto__` back now.
object.__proto__ = prototype;
} else {
object[property] = descriptor.value;
@@ -922,7 +614,6 @@ if (!Object.defineProperty || definePropertyFallback) {
} else {
if (!supportsAccessors)
throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED);
- // If we got that far then getters and setters can be defined !!
if (owns(descriptor, "get"))
defineGetter(object, property, descriptor.get);
if (owns(descriptor, "set"))
@@ -932,9 +623,6 @@ if (!Object.defineProperty || definePropertyFallback) {
return object;
};
}
-
-// ES5 15.2.3.7
-// http://es5.github.com/#x15.2.3.7
if (!Object.defineProperties) {
Object.defineProperties = function defineProperties(object, properties) {
for (var property in properties) {
@@ -944,30 +632,16 @@ if (!Object.defineProperties) {
return object;
};
}
-
-// ES5 15.2.3.8
-// http://es5.github.com/#x15.2.3.8
if (!Object.seal) {
Object.seal = function seal(object) {
- // this is misleading and breaks feature-detection, but
- // allows "securable" code to "gracefully" degrade to working
- // but insecure code.
return object;
};
}
-
-// ES5 15.2.3.9
-// http://es5.github.com/#x15.2.3.9
if (!Object.freeze) {
Object.freeze = function freeze(object) {
- // this is misleading and breaks feature-detection, but
- // allows "securable" code to "gracefully" degrade to working
- // but insecure code.
return object;
};
}
-
-// detect a Rhino bug and patch it
try {
Object.freeze(function () {});
} catch (exception) {
@@ -981,43 +655,26 @@ try {
};
})(Object.freeze);
}
-
-// ES5 15.2.3.10
-// http://es5.github.com/#x15.2.3.10
if (!Object.preventExtensions) {
Object.preventExtensions = function preventExtensions(object) {
- // this is misleading and breaks feature-detection, but
- // allows "securable" code to "gracefully" degrade to working
- // but insecure code.
return object;
};
}
-
-// ES5 15.2.3.11
-// http://es5.github.com/#x15.2.3.11
if (!Object.isSealed) {
Object.isSealed = function isSealed(object) {
return false;
};
}
-
-// ES5 15.2.3.12
-// http://es5.github.com/#x15.2.3.12
if (!Object.isFrozen) {
Object.isFrozen = function isFrozen(object) {
return false;
};
}
-
-// ES5 15.2.3.13
-// http://es5.github.com/#x15.2.3.13
if (!Object.isExtensible) {
Object.isExtensible = function isExtensible(object) {
- // 1. If Type(O) is not Object throw a TypeError exception.
if (Object(object) === object) {
throw new TypeError(); // TODO message
}
- // 2. Return the Boolean value of the [[Extensible]] internal property of O.
var name = '';
while (owns(object, name)) {
name += '?';
@@ -1028,11 +685,7 @@ if (!Object.isExtensible) {
return returnValue;
};
}
-
-// ES5 15.2.3.14
-// http://es5.github.com/#x15.2.3.14
if (!Object.keys) {
- // http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation
var hasDontEnumBug = true,
dontEnums = [
"toString",
@@ -1073,26 +726,11 @@ if (!Object.keys) {
};
}
-
-//
-// Date
-// ====
-//
-
-// ES5 15.9.5.43
-// http://es5.github.com/#x15.9.5.43
-// This function returns a String value represent the instance in time
-// represented by this Date object. The format of the String is the Date Time
-// string format defined in 15.9.1.15. All fields are present in the String.
-// The time zone is always UTC, denoted by the suffix Z. If the time value of
-// this object is not a finite Number a RangeError exception is thrown.
if (!Date.prototype.toISOString || (new Date(-62198755200000).toISOString().indexOf('-000001') === -1)) {
Date.prototype.toISOString = function toISOString() {
var result, length, value, year;
if (!isFinite(this))
throw new RangeError;
-
- // the date time string format is specified in 15.9.1.15.
result = [this.getUTCMonth() + 1, this.getUTCDate(),
this.getUTCHours(), this.getUTCMinutes(), this.getUTCSeconds()];
year = this.getUTCFullYear();
@@ -1101,76 +739,32 @@ if (!Date.prototype.toISOString || (new Date(-62198755200000).toISOString().inde
length = result.length;
while (length--) {
value = result[length];
- // pad months, days, hours, minutes, and seconds to have two digits.
if (value < 10)
result[length] = "0" + value;
}
- // pad milliseconds to have three digits.
return year + "-" + result.slice(0, 2).join("-") + "T" + result.slice(2).join(":") + "." +
("000" + this.getUTCMilliseconds()).slice(-3) + "Z";
}
}
-
-// ES5 15.9.4.4
-// http://es5.github.com/#x15.9.4.4
if (!Date.now) {
Date.now = function now() {
return new Date().getTime();
};
}
-
-// ES5 15.9.5.44
-// http://es5.github.com/#x15.9.5.44
-// This function provides a String representation of a Date object for use by
-// JSON.stringify (15.12.3).
if (!Date.prototype.toJSON) {
Date.prototype.toJSON = function toJSON(key) {
- // When the toJSON method is called with argument key, the following
- // steps are taken:
-
- // 1. Let O be the result of calling ToObject, giving it the this
- // value as its argument.
- // 2. Let tv be ToPrimitive(O, hint Number).
- // 3. If tv is a Number and is not finite, return null.
- // XXX
- // 4. Let toISO be the result of calling the [[Get]] internal method of
- // O with argument "toISOString".
- // 5. If IsCallable(toISO) is false, throw a TypeError exception.
if (typeof this.toISOString != "function")
throw new TypeError(); // TODO message
- // 6. Return the result of calling the [[Call]] internal method of
- // toISO with O as the this value and an empty argument list.
return this.toISOString();
-
- // NOTE 1 The argument is ignored.
-
- // NOTE 2 The toJSON function is intentionally generic; it does not
- // require that its this value be a Date object. Therefore, it can be
- // transferred to other kinds of objects for use as a method. However,
- // it does require that any such object have a toISOString method. An
- // object is free to use the argument key to filter its
- // stringification.
};
}
-
-// ES5 15.9.4.2
-// http://es5.github.com/#x15.9.4.2
-// based on work shared by Daniel Friesen (dantman)
-// http://gist.github.com/303249
if (Date.parse("+275760-09-13T00:00:00.000Z") !== 8.64e15) {
- // XXX global assignment won't work in embeddings that use
- // an alternate object for the context.
Date = (function(NativeDate) {
-
- // Date.length === 7
var Date = function Date(Y, M, D, h, m, s, ms) {
var length = arguments.length;
if (this instanceof NativeDate) {
var date = length == 1 && String(Y) === Y ? // isString(Y)
- // We explicitly pass it through parse:
new NativeDate(Date.parse(Y)) :
- // We have to manually make calls depending on argument
- // length here
length >= 7 ? new NativeDate(Y, M, D, h, m, s, ms) :
length >= 6 ? new NativeDate(Y, M, D, h, m, s) :
length >= 5 ? new NativeDate(Y, M, D, h, m) :
@@ -1179,14 +773,11 @@ if (Date.parse("+275760-09-13T00:00:00.000Z") !== 8.64e15) {
length >= 2 ? new NativeDate(Y, M) :
length >= 1 ? new NativeDate(Y) :
new NativeDate();
- // Prevent mixups with unfixed Date object
date.constructor = Date;
return date;
}
return NativeDate.apply(this, arguments);
};
-
- // 15.9.1.15 Date Time String Format.
var isoDateExpression = new RegExp("^" +
"(\\d{4}|[\+\-]\\d{6})" + // four-digit year capture or sign + 6-digit extended year
"(?:-(\\d{2})" + // optional month capture
@@ -1207,59 +798,33 @@ if (Date.parse("+275760-09-13T00:00:00.000Z") !== 8.64e15) {
")" +
")?)?)?)?" +
"$");
-
- // Copy any custom methods a 3rd party library may have added
for (var key in NativeDate)
Date[key] = NativeDate[key];
-
- // Copy "native" methods explicitly; they may be non-enumerable
Date.now = NativeDate.now;
Date.UTC = NativeDate.UTC;
Date.prototype = NativeDate.prototype;
Date.prototype.constructor = Date;
-
- // Upgrade Date.parse to handle simplified ISO 8601 strings
Date.parse = function parse(string) {
var match = isoDateExpression.exec(string);
if (match) {
match.shift(); // kill match[0], the full match
- // parse months, days, hours, minutes, seconds, and milliseconds
for (var i = 1; i < 7; i++) {
- // provide default values if necessary
match[i] = +(match[i] || (i < 3 ? 1 : 0));
- // match[1] is the month. Months are 0-11 in JavaScript
- // `Date` objects, but 1-12 in ISO notation, so we
- // decrement.
if (i == 1)
match[i]--;
}
-
- // parse the UTC offset component
var minuteOffset = +match.pop(), hourOffset = +match.pop(), sign = match.pop();
-
- // compute the explicit time zone offset if specified
var offset = 0;
if (sign) {
- // detect invalid offsets and return early
if (hourOffset > 23 || minuteOffset > 59)
return NaN;
-
- // express the provided time zone offset in minutes. The offset is
- // negative for time zones west of UTC; positive otherwise.
offset = (hourOffset * 60 + minuteOffset) * 6e4 * (sign == "+" ? -1 : 1);
}
-
- // Date.UTC for years between 0 and 99 converts year to 1900 + year
- // The Gregorian calendar has a 400-year cycle, so
- // to Date.UTC(year + 400, .... ) - 12622780800000 == Date.UTC(year, ...),
- // where 12622780800000 - number of milliseconds in Gregorian calendar 400 years
var year = +match[0];
if (0 <= year && year <= 99) {
match[0] = year + 400;
return NativeDate.UTC.apply(this, match) + offset - 12622780800000;
}
-
- // compute a new UTC date value, accounting for the optional offset
return NativeDate.UTC.apply(this, match) + offset;
}
return NativeDate.parse.apply(this, arguments);
@@ -1268,20 +833,10 @@ if (Date.parse("+275760-09-13T00:00:00.000Z") !== 8.64e15) {
return Date;
})(Date);
}
-
-//
-// String
-// ======
-//
-
-// ES5 15.5.4.20
-// http://es5.github.com/#x15.5.4.20
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()) {
- // http://blog.stevenlevithan.com/archives/faster-trim-javascript
- // http://perfectionkills.com/whitespace-deviations/
ws = "[" + ws + "]";
var trimBeginRegexp = new RegExp("^" + ws + ws + "*"),
trimEndRegexp = new RegExp(ws + ws + "*$");
@@ -1289,15 +844,6 @@ if (!String.prototype.trim || ws.trim()) {
return String(this).replace(trimBeginRegexp, "").replace(trimEndRegexp, "");
};
}
-
-//
-// Util
-// ======
-//
-
-// ES5 9.4
-// http://es5.github.com/#x9.4
-// http://jsperf.com/to-integer
var toInteger = function (n) {
n = +n;
if (n !== n) // isNaN
@@ -1308,61 +854,19 @@ var toInteger = function (n) {
};
var prepareString = "a"[0] != "a",
- // ES5 9.9
- // http://es5.github.com/#x9.9
toObject = function (o) {
if (o == null) { // this matches both null and undefined
throw new TypeError(); // TODO message
}
- // If the implementation doesn't support by-index access of
- // string characters (ex. IE < 7), split the string
if (prepareString && typeof o == "string" && o) {
return o.split("");
}
return Object(o);
};
-});/* vim:ts=4:sts=4:sw=4:
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Ajax.org Code Editor (ACE).
- *
- * The Initial Developer of the Original Code is
- * Ajax.org B.V.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Fabian Jakobs <fabian AT ajax DOT org>
- * Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)
- * Mike de Boer <mike AT ajax DOT org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+});
define('ace/lib/event_emitter', ['require', 'exports', 'module' ], function(require, exports, module) {
-"use strict";
+
var EventEmitter = {};
@@ -1376,8 +880,11 @@ EventEmitter._dispatchEvent = function(eventName, e) {
if (!listeners.length && !defaultHandler)
return;
- e = e || {};
- e.type = eventName;
+ if (typeof e != "object" || !e)
+ e = {};
+
+ if (!e.type)
+ e.type = eventName;
if (!e.stopPropagation) {
e.stopPropagation = function() {
@@ -1398,7 +905,7 @@ EventEmitter._dispatchEvent = function(eventName, e) {
}
if (defaultHandler && !e.defaultPrevented)
- defaultHandler(e);
+ return defaultHandler(e);
};
EventEmitter.setDefaultHandler = function(eventName, callback) {
@@ -1416,7 +923,7 @@ EventEmitter.addEventListener = function(eventName, callback) {
var listeners = this._eventRegistry[eventName];
if (!listeners)
- var listeners = this._eventRegistry[eventName] = [];
+ listeners = this._eventRegistry[eventName] = [];
if (listeners.indexOf(callback) == -1)
listeners.push(callback);
@@ -1441,45 +948,10 @@ EventEmitter.removeAllListeners = function(eventName) {
exports.EventEmitter = EventEmitter;
-});/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Ajax.org Code Editor (ACE).
- *
- * The Initial Developer of the Original Code is
- * Ajax.org B.V.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Fabian Jakobs <fabian AT ajax DOT org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+});
define('ace/lib/oop', ['require', 'exports', 'module' ], function(require, exports, module) {
-"use strict";
+
exports.inherits = (function() {
var tempCtor = function() {};
@@ -1502,73 +974,242 @@ exports.implement = function(proto, mixin) {
};
});
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Ajax.org Code Editor (ACE).
- *
- * The Initial Developer of the Original Code is
- * Ajax.org B.V.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Fabian Jakobs <fabian AT ajax DOT org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define('ace/mode/css_worker', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/worker/mirror', 'ace/mode/css/csslint'], function(require, exports, module) {
-"use strict";
+
+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(200);
+ 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 result = CSSLint.verify(value);
+ var infoRules = this.infoRules;
+
+ var result = CSSLint.verify(value, this.ruleset);
this.sender.emit("csslint", result.messages.map(function(msg) {
- delete msg.rule;
- return msg;
+ return {
+ row: msg.line - 1,
+ column: msg.col - 1,
+ text: msg.message,
+ type: infoRules[msg.rule.id] ? "info" : msg.type
+ }
}));
};
-
+
}).call(Worker.prototype);
-});define('ace/worker/mirror', ['require', 'exports', 'module' , 'ace/document', 'ace/lib/lang'], function(require, exports, module) {
-"use strict";
+});
+
+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) {
+ return new Array(count + 1).join(string);
+};
+
+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") {
+ return obj;
+ }
+
+ var copy = obj.constructor();
+ for (var key in obj) {
+ if (typeof obj[key] == "object") {
+ copy[key] = this.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;
+ };
+
+ return deferred;
+};
+
+
+exports.delayedCall = function(fcn, defaultTimeout) {
+ var timer = null;
+ var callback = function() {
+ timer = null;
+ fcn();
+ };
+
+ var _self = function(timeout) {
+ timer && clearTimeout(timer);
+ timer = setTimeout(callback, timeout || defaultTimeout);
+ };
+
+ _self.delay = _self;
+ _self.schedule = function(timeout) {
+ if (timer == null)
+ timer = setTimeout(callback, timeout || 0);
+ };
+
+ _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");
@@ -1604,50 +1245,14 @@ var Mirror = exports.Mirror = function(sender) {
};
this.onUpdate = function() {
- // abstract method
};
}).call(Mirror.prototype);
-});/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Ajax.org Code Editor (ACE).
- *
- * The Initial Developer of the Original Code is
- * Ajax.org B.V.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Fabian Jakobs <fabian AT ajax DOT org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
+});
define('ace/document', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/event_emitter', 'ace/range', 'ace/anchor'], function(require, exports, module) {
-"use strict";
+
var oop = require("./lib/oop");
var EventEmitter = require("./lib/event_emitter").EventEmitter;
@@ -1656,14 +1261,10 @@ var Anchor = require("./anchor").Anchor;
var Document = function(text) {
this.$lines = [];
-
- if (Array.isArray(text)) {
- this.insertLines(0, text);
- }
- // There has to be one line at least in the document. If you pass an empty
- // string to the insert function, nothing will happen. Workaround.
- else if (text.length == 0) {
+ if (text.length == 0) {
this.$lines = [""];
+ } else if (Array.isArray(text)) {
+ this.insertLines(0, text);
} else {
this.insert({row: 0, column:0}, text);
}
@@ -1672,22 +1273,17 @@ var Document = function(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);
};
-
- // check for IE split bug
if ("aaa".split(/a/).length == 0)
this.$split = function(text) {
return text.replace(/\r\n|\r/g, "\n").split("\n");
@@ -1698,6 +1294,7 @@ var Document = function(text) {
};
+
this.$detectNewLine = function(text) {
var match = text.match(/^.*?(\r\n|\r|\n)/m);
if (match) {
@@ -1706,18 +1303,17 @@ var Document = function(text) {
this.$autoNewLine = "\n";
}
};
-
this.getNewLineCharacter = function() {
- switch (this.$newLineMode) {
+ switch (this.$newLineMode) {
case "windows":
- return "\r\n";
+ return "\r\n";
case "unix":
- return "\n";
+ return "\n";
- case "auto":
- return this.$autoNewLine;
- }
+ default:
+ return this.$autoNewLine;
+ }
};
this.$autoNewLine = "\n";
@@ -1728,48 +1324,33 @@ var Document = function(text) {
this.$newLineMode = newLineMode;
};
-
this.getNewLineMode = function() {
return this.$newLineMode;
};
-
this.isNewLine = function(text) {
return (text == "\r\n" || text == "\r" || text == "\n");
};
-
- /**
- * Get a verbatim copy of the given line as it is in the document
- */
this.getLine = function(row) {
return this.$lines[row] || "";
};
-
this.getLines = function(firstRow, lastRow) {
return this.$lines.slice(firstRow, lastRow + 1);
};
-
- /**
- * Returns all lines in the document as string array. Warning: The caller
- * should not modify this array!
- */
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.$lines[range.start.row].substring(range.start.column,
range.end.column);
}
else {
- var lines = [];
- lines.push(this.$lines[range.start.row].substring(range.start.column));
- lines.push.apply(lines, this.getLines(range.start.row+1, range.end.row-1));
- lines.push(this.$lines[range.end.row].substring(0, range.end.column));
+ var lines = this.getLines(range.start.row+1, range.end.row-1);
+ lines.unshift((this.$lines[range.start.row] || "").substring(range.start.column));
+ lines.push((this.$lines[range.end.row] || "").substring(0, range.end.column));
return lines.join(this.getNewLineCharacter());
}
};
@@ -1782,13 +1363,11 @@ var Document = function(text) {
}
return position;
};
-
this.insert = function(position, text) {
- if (text.length == 0)
+ if (!text || text.length === 0)
return position;
position = this.$clipPosition(position);
-
if (this.getLength() <= 1)
this.$detectNewLine(text);
@@ -1804,10 +1383,13 @@ var Document = function(text) {
}
return position;
};
-
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);
@@ -1820,9 +1402,8 @@ var Document = function(text) {
lines: lines
};
this._emit("change", { data: delta });
- return range.end;
+ return end || range.end;
};
-
this.insertNewLine = function(position) {
position = this.$clipPosition(position);
var line = this.$lines[position.row] || "";
@@ -1844,7 +1425,6 @@ var Document = function(text) {
return end;
};
-
this.insertInLine = function(position, text) {
if (text.length == 0)
return position;
@@ -1868,9 +1448,7 @@ var Document = function(text) {
return end;
};
-
this.remove = function(range) {
- // clip to document
range.start = this.$clipPosition(range.start);
range.end = this.$clipPosition(range.end);
@@ -1900,7 +1478,6 @@ var Document = function(text) {
}
return range.start;
};
-
this.removeInLine = function(row, startColumn, endColumn) {
if (startColumn == endColumn)
return;
@@ -1919,14 +1496,6 @@ var Document = function(text) {
this._emit("change", { data: delta });
return range.start;
};
-
- /**
- * Removes a range of full lines
- *
- * @param firstRow {Integer} The first row to be removed
- * @param lastRow {Integer} The last row to be removed
- * @return {String[]} The removed lines
- */
this.removeLines = function(firstRow, lastRow) {
var range = new Range(firstRow, 0, lastRow + 1, 0);
var removed = this.$lines.splice(firstRow, lastRow - firstRow + 1);
@@ -1940,7 +1509,6 @@ var Document = function(text) {
this._emit("change", { data: delta });
return removed;
};
-
this.removeNewLine = function(row) {
var firstLine = this.getLine(row);
var secondLine = this.getLine(row+1);
@@ -1957,13 +1525,9 @@ var Document = function(text) {
};
this._emit("change", { data: delta });
};
-
this.replace = function(range, text) {
if (text.length == 0 && range.isEmpty())
return range.start;
-
- // Shortcut: If the text we want to insert is the same as it is already
- // in the document, we don't have to replace anything.
if (text == this.getTextRange(range))
return range.end;
@@ -1977,7 +1541,6 @@ var Document = function(text) {
return end;
};
-
this.applyDeltas = function(deltas) {
for (var i=0; i<deltas.length; i++) {
var delta = deltas[i];
@@ -1993,7 +1556,6 @@ var Document = function(text) {
this.remove(range);
}
};
-
this.revertDeltas = function(deltas) {
for (var i=deltas.length-1; i>=0; i--) {
var delta = deltas[i];
@@ -2010,51 +1572,33 @@ var Document = function(text) {
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;
+
+ return index + newlineLength * i + pos.column;
+ };
}).call(Document.prototype);
exports.Document = Document;
});
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Ajax.org Code Editor (ACE).
- *
- * The Initial Developer of the Original Code is
- * Ajax.org B.V.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Fabian Jakobs <fabian AT ajax DOT org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
define('ace/range', ['require', 'exports', 'module' ], function(require, exports, module) {
-"use strict";
-
var Range = function(startRow, startColumn, endRow, endColumn) {
this.start = {
row: startRow,
@@ -2067,36 +1611,21 @@ var Range = function(startRow, startColumn, endRow, endColumn) {
};
};
-(function() {
- this.isEequal = function(range) {
+(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;
- };
-
- /**
- * Compares this range (A) with another range (B), where B is the passed in
- * range.
- *
- * Return values:
- * -2: (B) is infront of (A) and doesn't intersect with (A)
- * -1: (B) begins before (A) but ends inside of (A)
- * 0: (B) is completly inside of (A) OR (A) is complety inside of (B)
- * +1: (B) begins inside of (A) but ends outside of (A)
- * +2: (B) is after (A) and doesn't intersect with (A)
- *
- * 42: FTW state: (B) ends in (A) but starts outside of (A)
- */
+ };
this.compareRange = function(range) {
var cmp,
end = range.end,
@@ -2124,24 +1653,23 @@ var Range = function(startRow, startColumn, endRow, endColumn) {
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;
@@ -2150,8 +1678,7 @@ var Range = function(startRow, startColumn, endRow, endColumn) {
this.start.row = row;
this.start.column = column;
}
- }
-
+ };
this.setEnd = function(row, column) {
if (typeof row == "object") {
this.end.column = row.column;
@@ -2160,8 +1687,7 @@ var Range = function(startRow, startColumn, endRow, endColumn) {
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)) {
@@ -2171,8 +1697,7 @@ var Range = function(startRow, startColumn, endRow, endColumn) {
}
}
return false;
- }
-
+ };
this.insideStart = function(row, column) {
if (this.compare(row, column) == 0) {
if (this.isEnd(row, column)) {
@@ -2182,8 +1707,7 @@ var Range = function(startRow, startColumn, endRow, endColumn) {
}
}
return false;
- }
-
+ };
this.insideEnd = function(row, column) {
if (this.compare(row, column) == 0) {
if (this.isStart(row, column)) {
@@ -2193,8 +1717,7 @@ var Range = function(startRow, startColumn, endRow, endColumn) {
}
}
return false;
- }
-
+ };
this.compare = function(row, column) {
if (!this.isMultiLine()) {
if (row === this.start.row) {
@@ -2216,29 +1739,20 @@ var Range = function(startRow, startColumn, endRow, endColumn) {
return 0;
};
-
- /**
- * Like .compare(), but if isStart is true, return -1;
- */
this.compareStart = function(row, column) {
if (this.start.row == row && this.start.column == column) {
return -1;
} else {
return this.compare(row, column);
}
- }
-
- /**
- * Like .compare(), but if isEnd is true, return 1;
- */
+ };
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;
@@ -2247,8 +1761,7 @@ var Range = function(startRow, startColumn, endRow, endColumn) {
} else {
return this.compare(row, column);
}
- }
-
+ };
this.clipRows = function(firstRow, lastRow) {
if (this.end.row > lastRow) {
var end = {
@@ -2279,7 +1792,6 @@ var Range = function(startRow, startColumn, endRow, endColumn) {
}
return Range.fromPoints(start || this.start, end || this.end);
};
-
this.extend = function(row, column) {
var cmp = this.compare(row, column);
@@ -2296,22 +1808,18 @@ var Range = function(startRow, startColumn, endRow, endColumn) {
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);
@@ -2325,61 +1833,19 @@ var Range = function(startRow, startColumn, endRow, endColumn) {
};
}).call(Range.prototype);
-
-
Range.fromPoints = function(start, end) {
return new Range(start.row, start.column, end.row, end.column);
};
exports.Range = Range;
});
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Ajax.org Code Editor (ACE).
- *
- * The Initial Developer of the Original Code is
- * Ajax.org B.V.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Fabian Jakobs <fabian AT ajax DOT org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
define('ace/anchor', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/lib/event_emitter'], function(require, exports, module) {
-"use strict";
+
var oop = require("./lib/oop");
var EventEmitter = require("./lib/event_emitter").EventEmitter;
-/**
- * An Anchor is a floating pointer in the document. Whenever text is inserted or
- * deleted before the cursor, the position of the cursor is updated
- */
var Anchor = exports.Anchor = function(doc, row, column) {
this.document = doc;
@@ -2395,15 +1861,15 @@ var Anchor = exports.Anchor = function(doc, row, column) {
(function() {
oop.implement(this, EventEmitter);
-
+
this.getPosition = function() {
return this.$clipPositionToDocument(this.row, this.column);
};
-
+
this.getDocument = function() {
return this.document;
};
-
+
this.onChange = function(e) {
var delta = e.data;
var range = delta.range;
@@ -2496,11 +1962,10 @@ var Anchor = exports.Anchor = function(doc, row, column) {
value: pos
});
};
-
+
this.detach = function() {
this.document.removeEventListener("change", this.$onChange);
};
-
this.$clipPositionToDocument = function(row, column) {
var pos = {};
@@ -2526,280 +1991,41 @@ var Anchor = exports.Anchor = function(doc, row, column) {
}).call(Anchor.prototype);
});
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Ajax.org Code Editor (ACE).
- *
- * The Initial Developer of the Original Code is
- * Ajax.org B.V.
- * Portions created by the Initial Developer are Copyright (C) 2010
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Fabian Jakobs <fabian AT ajax DOT org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-define('ace/lib/lang', ['require', 'exports', 'module' ], function(require, exports, module) {
-"use strict";
-
-exports.stringReverse = function(string) {
- return string.split("").reverse().join("");
-};
-
-exports.stringRepeat = function (string, count) {
- return new Array(count + 1).join(string);
-};
-
-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") {
- return obj;
- }
-
- var copy = obj.constructor();
- for (var key in obj) {
- if (typeof obj[key] == "object") {
- copy[key] = this.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;
-
-};
-
-/**
- * splice out of 'array' anything that === 'value'
- */
-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.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;
- };
-
- return deferred;
-};
-
-});
-/*
-CSSLint
-Copyright (c) 2011 Nicole Sullivan and Nicholas C. Zakas. All rights reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-*/
define('ace/mode/css/csslint', ['require', 'exports', 'module' ], function(require, exports, module) {
-/*!
-Parser-Lib
-Copyright (c) 2009-2011 Nicholas C. Zakas. All rights reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-*/
-/* Build time: 13-July-2011 04:35:28 */
var parserlib = {};
(function(){
-
-/**
- * A generic base to inherit from for any object
- * that needs event handling.
- * @class EventTarget
- * @constructor
- */
function EventTarget(){
-
- /**
- * The array of listeners for various events.
- * @type Object
- * @property _listeners
- * @private
- */
this._listeners = {};
}
EventTarget.prototype = {
-
- //restore constructor
constructor: EventTarget,
-
- /**
- * Adds a listener for a given event type.
- * @param {String} type The type of event to add a listener for.
- * @param {Function} listener The function to call when the event occurs.
- * @return {void}
- * @method addListener
- */
addListener: function(type, listener){
if (!this._listeners[type]){
this._listeners[type] = [];
}
this._listeners[type].push(listener);
- },
-
- /**
- * Fires an event based on the passed-in object.
- * @param {Object|String} event An object with at least a 'type' attribute
- * or a string indicating the event name.
- * @return {void}
- * @method fire
- */
+ },
fire: function(event){
if (typeof event == "string"){
event = { type: event };
}
- if (!event.target){
+ if (typeof event.target != "undefined"){
event.target = this;
}
- if (!event.type){
+ if (typeof event.type == "undefined"){
throw new Error("Event object missing 'type' property.");
}
if (this._listeners[event.type]){
-
- //create a copy of the array and use that so listeners can't chane
var listeners = this._listeners[event.type].concat();
for (var i=0, len=listeners.length; i < len; i++){
listeners[i].call(this, event);
}
}
},
-
- /**
- * Removes a listener for a given event type.
- * @param {String} type The type of event to remove a listener from.
- * @param {Function} listener The function to remove from the event.
- * @return {void}
- * @method removeListener
- */
removeListener: function(type, listener){
if (this._listeners[type]){
var listeners = this._listeners[type];
@@ -2814,147 +2040,47 @@ EventTarget.prototype = {
}
}
};
-/**
- * Convenient way to read through strings.
- * @namespace parserlib.util
- * @class StringReader
- * @constructor
- * @param {String} text The text to read.
- */
function StringReader(text){
-
- /**
- * The input text with line endings normalized.
- * @property _input
- * @type String
- * @private
- */
this._input = text.replace(/\n\r?/g, "\n");
-
-
- /**
- * The row for the character to be read next.
- * @property _line
- * @type int
- * @private
- */
this._line = 1;
-
-
- /**
- * The column for the character to be read next.
- * @property _col
- * @type int
- * @private
- */
this._col = 1;
-
- /**
- * The index of the character in the input to be read next.
- * @property _cursor
- * @type int
- * @private
- */
this._cursor = 0;
}
StringReader.prototype = {
-
- //restore constructor
constructor: StringReader,
-
- //-------------------------------------------------------------------------
- // Position info
- //-------------------------------------------------------------------------
-
- /**
- * Returns the column of the character to be read next.
- * @return {int} The column of the character to be read next.
- * @method getCol
- */
getCol: function(){
return this._col;
},
-
- /**
- * Returns the row of the character to be read next.
- * @return {int} The row of the character to be read next.
- * @method getLine
- */
getLine: function(){
return this._line ;
},
-
- /**
- * Determines if you're at the end of the input.
- * @return {Boolean} True if there's no more input, false otherwise.
- * @method eof
- */
eof: function(){
return (this._cursor == this._input.length);
},
-
- //-------------------------------------------------------------------------
- // Basic reading
- //-------------------------------------------------------------------------
-
- /**
- * Reads the next character without advancing the cursor.
- * @param {int} count How many characters to look ahead (default is 1).
- * @return {String} The next character or null if there is no next character.
- * @method peek
- */
peek: function(count){
var c = null;
count = (typeof count == "undefined" ? 1 : count);
-
- //if we're not at the end of the input...
if (this._cursor < this._input.length){
-
- //get character and increment cursor and column
c = this._input.charAt(this._cursor + count - 1);
}
return c;
},
-
- /**
- * Reads the next character from the input and adjusts the row and column
- * accordingly.
- * @return {String} The next character or null if there is no next character.
- * @method read
- */
read: function(){
var c = null;
-
- //if we're not at the end of the input...
if (this._cursor < this._input.length){
-
- //if the last character was a newline, increment row count
- //and reset column count
if (this._input.charAt(this._cursor) == "\n"){
this._line++;
this._col=1;
} else {
this._col++;
}
-
- //get character and increment cursor and column
c = this._input.charAt(this._cursor++);
}
return c;
},
-
- //-------------------------------------------------------------------------
- // Misc
- //-------------------------------------------------------------------------
-
- /**
- * Saves the current location so it can be returned to later.
- * @method mark
- * @return {void}
- */
mark: function(){
this._bookmark = {
cursor: this._cursor,
@@ -2971,29 +2097,10 @@ StringReader.prototype = {
delete this._bookmark;
}
},
-
- //-------------------------------------------------------------------------
- // Advanced reading
- //-------------------------------------------------------------------------
-
- /**
- * Reads up to and including the given string. Throws an error if that
- * string is not found.
- * @param {String} pattern The string to read.
- * @return {String} The string when it is found.
- * @throws Error when the string pattern is not found.
- * @method readTo
- */
readTo: function(pattern){
var buffer = "",
c;
-
- /*
- * First, buffer must be the same length as the pattern.
- * Then, buffer must end with the pattern or else reach the
- * end of the input.
- */
while (buffer.length < pattern.length || buffer.lastIndexOf(pattern) != buffer.length - pattern.length){
c = this.read();
if (c){
@@ -3006,17 +2113,6 @@ StringReader.prototype = {
return buffer;
},
-
- /**
- * Reads characters while each character causes the given
- * filter function to return true. The function is passed
- * in each character and either returns true to continue
- * reading or false to stop.
- * @param {Function} filter The function to read on each character.
- * @return {String} The string made up of all characters that passed the
- * filter check.
- * @method readWhile
- */
readWhile: function(filter){
var buffer = "",
@@ -3030,25 +2126,10 @@ StringReader.prototype = {
return buffer;
},
-
- /**
- * Reads characters that match either text or a regular expression and
- * returns those characters. If a match is found, the row and column
- * are adjusted; if no match is found, the reader's state is unchanged.
- * reading or false to stop.
- * @param {String|RegExp} matchter If a string, then the literal string
- * value is searched for. If a regular expression, then any string
- * matching the pattern is search for.
- * @return {String} The string made up of all characters that matched or
- * null if there was no match.
- * @method readMatch
- */
readMatch: function(matcher){
var source = this._input.substring(this._cursor),
value = null;
-
- //if it's a string, just do a straight match
if (typeof matcher == "string"){
if (source.indexOf(matcher) === 0){
value = this.readCount(matcher.length);
@@ -3061,15 +2142,6 @@ StringReader.prototype = {
return value;
},
-
-
- /**
- * Reads a given number of characters. If the end of the input is reached,
- * it reads only the remaining characters and does not throw an error.
- * @param {int} count The number of characters to read.
- * @return {String} The string made up the read characters.
- * @method readCount
- */
readCount: function(count){
var buffer = "";
@@ -3081,187 +2153,52 @@ StringReader.prototype = {
}
};
-/**
- * Type to use when a syntax error occurs.
- * @class SyntaxError
- * @namespace parserlib.util
- * @constructor
- * @param {String} message The error message.
- * @param {int} line The line at which the error occurred.
- * @param {int} col The column at which the error occurred.
- */
function SyntaxError(message, line, col){
-
- /**
- * The column at which the error occurred.
- * @type int
- * @property col
- */
this.col = col;
-
- /**
- * The line at which the error occurred.
- * @type int
- * @property line
- */
this.line = line;
-
- /**
- * The text representation of the unit.
- * @type String
- * @property text
- */
this.message = message;
}
-
-//inherit from Error
SyntaxError.prototype = new Error();
-/**
- * Base type to represent a single syntactic unit.
- * @class SyntaxUnit
- * @namespace parserlib.util
- * @constructor
- * @param {String} text The text of the unit.
- * @param {int} line The line of text on which the unit resides.
- * @param {int} col The column of text on which the unit resides.
- */
-function SyntaxUnit(text, line, col){
-
-
- /**
- * The column of text on which the unit resides.
- * @type int
- * @property col
- */
+function SyntaxUnit(text, line, col, type){
this.col = col;
-
- /**
- * The line of text on which the unit resides.
- * @type int
- * @property line
- */
this.line = line;
-
- /**
- * The text representation of the unit.
- * @type String
- * @property text
- */
this.text = text;
-
+ this.type = type;
}
-
-/**
- * Create a new syntax unit based solely on the given token.
- * Convenience method for creating a new syntax unit when
- * it represents a single token instead of multiple.
- * @param {Object} token The token object to represent.
- * @return {parserlib.util.SyntaxUnit} The object representing the token.
- * @static
- * @method fromToken
- */
SyntaxUnit.fromToken = function(token){
return new SyntaxUnit(token.value, token.startLine, token.startCol);
};
SyntaxUnit.prototype = {
-
- //restore constructor
constructor: SyntaxUnit,
-
- /**
- * Returns the text representation of the unit.
- * @return {String} The text representation of the unit.
- * @method valueOf
- */
valueOf: function(){
return this.toString();
},
-
- /**
- * Returns the text representation of the unit.
- * @return {String} The text representation of the unit.
- * @method toString
- */
toString: function(){
return this.text;
}
};
-/**
- * Generic TokenStream providing base functionality.
- * @class TokenStreamBase
- * @namespace parserlib.util
- * @constructor
- * @param {String|StringReader} input The text to tokenize or a reader from
- * which to read the input.
- */
function TokenStreamBase(input, tokenData){
-
- /**
- * The string reader for easy access to the text.
- * @type StringReader
- * @property _reader
- * @private
- */
- //this._reader = (typeof input == "string") ? new StringReader(input) : input;
this._reader = input ? new StringReader(input.toString()) : null;
-
- /**
- * Token object for the last consumed token.
- * @type Token
- * @property _token
- * @private
- */
- this._token = null;
-
- /**
- * The array of token information.
- * @type Array
- * @property _tokenData
- * @private
- */
+ this._token = null;
this._tokenData = tokenData;
-
- /**
- * Lookahead token buffer.
- * @type Array
- * @property _lt
- * @private
- */
this._lt = [];
-
- /**
- * Lookahead token buffer index.
- * @type int
- * @property _ltIndex
- * @private
- */
this._ltIndex = 0;
this._ltIndexCache = [];
}
-
-/**
- * Accepts an array of token information and outputs
- * an array of token data containing key-value mappings
- * and matching functions that the TokenStream needs.
- * @param {Array} tokens An array of token descriptors.
- * @return {Array} An array of processed token data.
- * @method createTokenData
- * @static
- */
TokenStreamBase.createTokenData = function(tokens){
- var nameMap = [],
- typeMap = {},
- tokenData = tokens.concat([]),
- i = 0,
- len = tokenData.length+1;
+ var nameMap = [],
+ typeMap = {},
+ tokenData = tokens.concat([]),
+ i = 0,
+ len = tokenData.length+1;
tokenData.UNKNOWN = -1;
- tokenData.unshift({name:"EOF"});
+ tokenData.unshift({name:"EOF"});
for (; i < len; i++){
nameMap.push(tokenData[i].name);
@@ -3278,36 +2215,13 @@ TokenStreamBase.createTokenData = function(tokens){
tokenData.type = function(c){
return typeMap[c];
};
-
- return tokenData;
+
+ return tokenData;
};
TokenStreamBase.prototype = {
-
- //restore constructor
constructor: TokenStreamBase,
-
- //-------------------------------------------------------------------------
- // Matching methods
- //-------------------------------------------------------------------------
-
- /**
- * Determines if the next token matches the given token type.
- * If so, that token is consumed; if not, the token is placed
- * back onto the token stream. You can pass in any number of
- * token types and this will return true if any of the token
- * types is found.
- * @param {int|int[]} tokenTypes Either a single token type or an array of
- * token types that the next token might be. If an array is passed,
- * it's assumed that the token can be any of these.
- * @param {variant} channel (Optional) The channel to read from. If not
- * provided, reads from the default (unnamed) channel.
- * @return {Boolean} True if the token type matches, false if not.
- * @method match
- */
match: function(tokenTypes, channel){
-
- //always convert to an array, makes things easier
if (!(tokenTypes instanceof Array)){
tokenTypes = [tokenTypes];
}
@@ -3321,26 +2235,12 @@ TokenStreamBase.prototype = {
return true;
}
}
-
- //no match found, put the token back
this.unget();
return false;
- },
-
- /**
- * Determines if the next token matches the given token type.
- * If so, that token is consumed; if not, an error is thrown.
- * @param {int|int[]} tokenTypes Either a single token type or an array of
- * token types that the next token should be. If an array is passed,
- * it's assumed that the token must be one of these.
- * @param {variant} channel (Optional) The channel to read from. If not
- * provided, reads from the default (unnamed) channel.
- * @return {void}
- * @method mustMatch
- */
+ },
mustMatch: function(tokenTypes, channel){
- //always convert to an array, makes things easier
+ var token;
if (!(tokenTypes instanceof Array)){
tokenTypes = [tokenTypes];
}
@@ -3351,36 +2251,14 @@ TokenStreamBase.prototype = {
" at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
}
},
-
- //-------------------------------------------------------------------------
- // Consuming methods
- //-------------------------------------------------------------------------
-
- /**
- * Keeps reading from the token stream until either one of the specified
- * token types is found or until the end of the input is reached.
- * @param {int|int[]} tokenTypes Either a single token type or an array of
- * token types that the next token should be. If an array is passed,
- * it's assumed that the token must be one of these.
- * @param {variant} channel (Optional) The channel to read from. If not
- * provided, reads from the default (unnamed) channel.
- * @return {void}
- * @method advance
- */
advance: function(tokenTypes, channel){
- while(this.LA(0) != 0 && !this.match(tokenTypes, channel)){
+ while(this.LA(0) !== 0 && !this.match(tokenTypes, channel)){
this.get();
}
return this.LA(0);
- },
-
- /**
- * Consumes the next token from the token stream.
- * @return {int} The token type of the token that was just consumed.
- * @method get
- */
+ },
get: function(channel){
var tokenInfo = this._tokenData,
@@ -3391,102 +2269,57 @@ TokenStreamBase.prototype = {
found = false,
token,
info;
-
- //check the lookahead buffer first
if (this._lt.length && this._ltIndex >= 0 && this._ltIndex < this._lt.length){
i++;
this._token = this._lt[this._ltIndex++];
info = tokenInfo[this._token.type];
-
- //obey channels logic
while((info.channel !== undefined && channel !== info.channel) &&
this._ltIndex < this._lt.length){
this._token = this._lt[this._ltIndex++];
info = tokenInfo[this._token.type];
i++;
}
-
- //here be dragons
if ((info.channel === undefined || channel === info.channel) &&
this._ltIndex <= this._lt.length){
this._ltIndexCache.push(i);
return this._token.type;
}
}
-
- //call token retriever method
- token = this._getToken();
-
- //if it should be hidden, don't save a token
+ token = this._getToken();
if (token.type > -1 && !tokenInfo[token.type].hide){
-
- //apply token channel
token.channel = tokenInfo[token.type].channel;
-
- //save for later
this._token = token;
this._lt.push(token);
-
- //save space that will be moved (must be done before array is truncated)
this._ltIndexCache.push(this._lt.length - this._ltIndex + i);
-
- //keep the buffer under 5 items
if (this._lt.length > 5){
this._lt.shift();
}
-
- //also keep the shift buffer under 5 items
if (this._ltIndexCache.length > 5){
this._ltIndexCache.shift();
}
-
- //update lookahead index
this._ltIndex = this._lt.length;
}
-
- /*
- * Skip to the next token if:
- * 1. The token type is marked as hidden.
- * 2. The token type has a channel specified and it isn't the current channel.
- */
info = tokenInfo[token.type];
if (info &&
(info.hide ||
(info.channel !== undefined && channel !== info.channel))){
return this.get(channel);
} else {
- //return just the type
return token.type;
}
},
-
- /**
- * Looks ahead a certain number of tokens and returns the token type at
- * that position. This will throw an error if you lookahead past the
- * end of input, past the size of the lookahead buffer, or back past
- * the first token in the lookahead buffer.
- * @param {int} The index of the token type to retrieve. 0 for the
- * current token, 1 for the next, -1 for the previous, etc.
- * @return {int} The token type of the token in the given position.
- * @method LA
- */
LA: function(index){
var total = index,
tt;
if (index > 0){
- //TODO: Store 5 somewhere
if (index > 5){
throw new Error("Too much lookahead.");
}
-
- //get all those tokens
while(total){
tt = this.get();
total--;
}
-
- //unget all those tokens
while(total < index){
this.unget();
total++;
@@ -3505,78 +2338,28 @@ TokenStreamBase.prototype = {
return tt;
- },
-
- /**
- * Looks ahead a certain number of tokens and returns the token at
- * that position. This will throw an error if you lookahead past the
- * end of input, past the size of the lookahead buffer, or back past
- * the first token in the lookahead buffer.
- * @param {int} The index of the token type to retrieve. 0 for the
- * current token, 1 for the next, -1 for the previous, etc.
- * @return {Object} The token of the token in the given position.
- * @method LA
- */
+ },
LT: function(index){
-
- //lookahead first to prime the token buffer
this.LA(index);
-
- //now find the token, subtract one because _ltIndex is already at the next index
return this._lt[this._ltIndex+index-1];
},
-
- /**
- * Returns the token type for the next token in the stream without
- * consuming it.
- * @return {int} The token type of the next token in the stream.
- * @method peek
- */
peek: function(){
return this.LA(1);
},
-
- /**
- * Returns the actual token object for the last consumed token.
- * @return {Token} The token object for the last consumed token.
- * @method token
- */
token: function(){
return this._token;
},
-
- /**
- * Returns the name of the token for the given token type.
- * @param {int} tokenType The type of token to get the name of.
- * @return {String} The name of the token or "UNKNOWN_TOKEN" for any
- * invalid token type.
- * @method tokenName
- */
tokenName: function(tokenType){
if (tokenType < 0 || tokenType > this._tokenData.length){
return "UNKNOWN_TOKEN";
} else {
return this._tokenData[tokenType].name;
}
- },
-
- /**
- * Returns the token type value for the given token name.
- * @param {String} tokenName The name of the token whose value should be returned.
- * @return {int} The token type value for the given token name or -1
- * for an unknown token.
- * @method tokenName
- */
+ },
tokenType: function(tokenName){
return this._tokenData[tokenName] || -1;
- },
-
- /**
- * Returns the last consumed token to the token stream.
- * @method unget
- */
+ },
unget: function(){
- //if (this._ltIndex > -1){
if (this._ltIndexCache.length){
this._ltIndex -= this._ltIndexCache.pop();//--;
this._token = this._lt[this._ltIndex - 1];
@@ -3589,6 +2372,7 @@ TokenStreamBase.prototype = {
+
parserlib.util = {
StringReader: StringReader,
SyntaxError : SyntaxError,
@@ -3597,31 +2381,6 @@ EventTarget : EventTarget,
TokenStreamBase : TokenStreamBase
};
})();
-
-/*
-Parser-Lib
-Copyright (c) 2009-2011 Nicholas C. Zakas. All rights reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-*/
-/* Build time: 13-July-2011 04:35:28 */
(function(){
var EventTarget = parserlib.util.EventTarget,
TokenStreamBase = parserlib.util.TokenStreamBase,
@@ -3629,6 +2388,7 @@ StringReader = parserlib.util.StringReader,
SyntaxError = parserlib.util.SyntaxError,
SyntaxUnit = parserlib.util.SyntaxUnit;
+
var Colors = {
aliceblue :"#f0f8ff",
antiquewhite :"#faebd7",
@@ -3697,7 +2457,7 @@ var Colors = {
lightcoral :"#f08080",
lightcyan :"#e0ffff",
lightgoldenrodyellow :"#fafad2",
- lightgrey :"#d3d3d3",
+ lightgray :"#d3d3d3",
lightgreen :"#90ee90",
lightpink :"#ffb6c1",
lightsalmon :"#ffa07a",
@@ -3771,28 +2531,10 @@ var Colors = {
yellow :"#ffff00",
yellowgreen :"#9acd32"
};
-/**
- * Represents a selector combinator (whitespace, +, >).
- * @namespace parserlib.css
- * @class Combinator
- * @extends parserlib.util.SyntaxUnit
- * @constructor
- * @param {String} text The text representation of the unit.
- * @param {int} line The line of text on which the unit resides.
- * @param {int} col The column of text on which the unit resides.
- */
function Combinator(text, line, col){
- SyntaxUnit.call(this, text, line, col);
-
- /**
- * The type of modifier.
- * @type String
- * @property type
- */
+ SyntaxUnit.call(this, text, line, col, Parser.COMBINATOR_TYPE);
this.type = "unknown";
-
- //pretty simple
if (/^\s+$/.test(text)){
this.type = "descendant";
} else if (text == ">"){
@@ -3807,274 +2549,27 @@ function Combinator(text, line, col){
Combinator.prototype = new SyntaxUnit();
Combinator.prototype.constructor = Combinator;
-
-
-
-var Level1Properties = {
-
- "background": 1,
- "background-attachment": 1,
- "background-color": 1,
- "background-image": 1,
- "background-position": 1,
- "background-repeat": 1,
-
- "border": 1,
- "border-bottom": 1,
- "border-bottom-width": 1,
- "border-color": 1,
- "border-left": 1,
- "border-left-width": 1,
- "border-right": 1,
- "border-right-width": 1,
- "border-style": 1,
- "border-top": 1,
- "border-top-width": 1,
- "border-width": 1,
-
- "clear": 1,
- "color": 1,
- "display": 1,
- "float": 1,
-
- "font": 1,
- "font-family": 1,
- "font-size": 1,
- "font-style": 1,
- "font-variant": 1,
- "font-weight": 1,
-
- "height": 1,
- "letter-spacing": 1,
- "line-height": 1,
-
- "list-style": 1,
- "list-style-image": 1,
- "list-style-position": 1,
- "list-style-type": 1,
-
- "margin": 1,
- "margin-bottom": 1,
- "margin-left": 1,
- "margin-right": 1,
- "margin-top": 1,
-
- "padding": 1,
- "padding-bottom": 1,
- "padding-left": 1,
- "padding-right": 1,
- "padding-top": 1,
-
- "text-align": 1,
- "text-decoration": 1,
- "text-indent": 1,
- "text-transform": 1,
-
- "vertical-align": 1,
- "white-space": 1,
- "width": 1,
- "word-spacing": 1
-
-};
-
-var Level2Properties = {
-
- //Aural
- "azimuth": 1,
- "cue-after": 1,
- "cue-before": 1,
- "cue": 1,
- "elevation": 1,
- "pause-after": 1,
- "pause-before": 1,
- "pause": 1,
- "pitch-range": 1,
- "pitch": 1,
- "play-during": 1,
- "richness": 1,
- "speak-header": 1,
- "speak-numeral": 1,
- "speak-punctuation": 1,
- "speak": 1,
- "speech-rate": 1,
- "stress": 1,
- "voice-family": 1,
- "volume": 1,
-
- //Paged
- "orphans": 1,
- "page-break-after": 1,
- "page-break-before": 1,
- "page-break-inside": 1,
- "widows": 1,
-
- //Interactive
- "cursor": 1,
- "outline-color": 1,
- "outline-style": 1,
- "outline-width": 1,
- "outline": 1,
-
- //Visual
- "background-attachment": 1,
- "background-color": 1,
- "background-image": 1,
- "background-position": 1,
- "background-repeat": 1,
- "background": 1,
- "border-collapse": 1,
- "border-color": 1,
- "border-spacing": 1,
- "border-style": 1,
- "border-top": 1,
- "border-top-color": 1,
- "border-top-style": 1,
- "border-top-width": 1,
- "border-width": 1,
- "border": 1,
- "bottom": 1,
- "caption-side": 1,
- "clear": 1,
- "clip": 1,
- "color": 1,
- "content": 1,
- "counter-increment": 1,
- "counter-reset": 1,
- "direction": 1,
- "display": 1,
- "empty-cells": 1,
- "float": 1,
- "font-family": 1,
- "font-size": 1,
- "font-style": 1,
- "font-variant": 1,
- "font-weight": 1,
- "font": 1,
- "height": 1,
- "left": 1,
- "letter-spacing": 1,
- "line-height": 1,
- "list-style-image": 1,
- "list-style-position": 1,
- "list-style-type": 1,
- "list-style": 1,
- "margin-right": 1,
- "margin-top": 1,
- "margin": 1,
- "max-height": 1,
- "max-width": 1,
- "min-height": 1,
- "min-width": 1,
- "overflow": 1,
- "padding-top": 1,
- "padding": 1,
- "position": 1,
- "quotes": 1,
- "right": 1,
- "table-layout": 1,
- "text-align": 1,
- "text-decoration": 1,
- "text-indent": 1,
- "text-transform": 1,
- "top": 1,
- "unicode-bidi": 1,
- "vertical-align": 1,
- "visibility": 1,
- "white-space": 1,
- "width": 1,
- "word-spacing": 1,
- "z-index": 1
-};
-/**
- * Represents a media feature, such as max-width:500.
- * @namespace parserlib.css
- * @class MediaFeature
- * @extends parserlib.util.SyntaxUnit
- * @constructor
- * @param {SyntaxUnit} name The name of the feature.
- * @param {SyntaxUnit} value The value of the feature or null if none.
- */
function MediaFeature(name, value){
- SyntaxUnit.call(this, "(" + name + (value !== null ? ":" + value : "") + ")", name.startLine, name.startCol);
-
- /**
- * The name of the media feature
- * @type String
- * @property name
- */
+ SyntaxUnit.call(this, "(" + name + (value !== null ? ":" + value : "") + ")", name.startLine, name.startCol, Parser.MEDIA_FEATURE_TYPE);
this.name = name;
-
- /**
- * The value for the feature or null if there is none.
- * @type SyntaxUnit
- * @property value
- */
this.value = value;
}
MediaFeature.prototype = new SyntaxUnit();
MediaFeature.prototype.constructor = MediaFeature;
-
-
-/**
- * Represents an individual media query.
- * @namespace parserlib.css
- * @class MediaQuery
- * @extends parserlib.util.SyntaxUnit
- * @constructor
- * @param {String} modifier The modifier "not" or "only" (or null).
- * @param {String} mediaType The type of media (i.e., "print").
- * @param {Array} parts Array of selectors parts making up this selector.
- * @param {int} line The line of text on which the unit resides.
- * @param {int} col The column of text on which the unit resides.
- */
function MediaQuery(modifier, mediaType, features, line, col){
- SyntaxUnit.call(this, (modifier ? modifier + " ": "") + (mediaType ? mediaType + " " : "") + features.join(" and "), line, col);
-
- /**
- * The media modifier ("not" or "only")
- * @type String
- * @property modifier
- */
+ SyntaxUnit.call(this, (modifier ? modifier + " ": "") + (mediaType ? mediaType + " " : "") + features.join(" and "), line, col, Parser.MEDIA_QUERY_TYPE);
this.modifier = modifier;
-
- /**
- * The mediaType (i.e., "print")
- * @type String
- * @property mediaType
- */
- this.mediaType = mediaType;
-
- /**
- * The parts that make up the selector.
- * @type Array
- * @property features
- */
+ this.mediaType = mediaType;
this.features = features;
}
MediaQuery.prototype = new SyntaxUnit();
MediaQuery.prototype.constructor = MediaQuery;
-
-
-/**
- * A CSS3 parser.
- * @namespace parserlib.css
- * @class Parser
- * @constructor
- * @param {Object} options (Optional) Various options for the parser:
- * starHack (true|false) to allow IE6 star hack as valid,
- * underscoreHack (true|false) to interpret leading underscores
- * as IE6-7 targeting for known properties, ieFilters (true|false)
- * to indicate that IE < 8 filters should be accepted and not throw
- * syntax errors.
- */
function Parser(options){
-
- //inherit event functionality
EventTarget.call(this);
@@ -4082,59 +2577,55 @@ function Parser(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 = {
-
- //restore constructor
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,
- //-----------------------------------------------------------------
- // Grammar
- //-----------------------------------------------------------------
-
- _stylesheet: function(){
-
- /*
- * stylesheet
- * : [ CHARSET_SYM S* STRING S* ';' ]?
- * [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
- * [ namespace [S|CDO|CDC]* ]*
- * [ [ ruleset | media | page | font_face | keyframes ] [S|CDO|CDC]* ]*
- * ;
- */
+ _stylesheet: function(){
var tokenStream = this._tokenStream,
charset = null,
+ count,
token,
tt;
this.fire("startstylesheet");
-
- //try to read character set
this._charset();
this._skipCruft();
-
- //try to read imports - may be more than one
while (tokenStream.peek() == Tokens.IMPORT_SYM){
this._import();
this._skipCruft();
}
-
- //try to read namespaces - may be more than one
while (tokenStream.peek() == Tokens.NAMESPACE_SYM){
this._namespace();
this._skipCruft();
}
-
- //get the next token
tt = tokenStream.peek();
-
- //try to read the rest
while(tt > Tokens.EOF){
try {
@@ -4155,14 +2646,36 @@ Parser.prototype = function(){
case Tokens.KEYFRAMES_SYM:
this._keyframes();
this._skipCruft();
- break;
+ 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()){
-
- //error handling for known issues
switch(tt){
case Tokens.CHARSET_SYM:
token = tokenStream.LT(1);
@@ -4238,34 +2751,23 @@ Parser.prototype = function(){
}
},
- _import: function(emit){
- /*
- * import
- * : IMPORT_SYM S*
- * [STRING|URI] S* media_query_list? ';' S*
- */
+ _import: function(emit){
var tokenStream = this._tokenStream,
tt,
uri,
importToken,
mediaList = [];
-
- //read import symbol
tokenStream.mustMatch(Tokens.IMPORT_SYM);
importToken = tokenStream.token();
this._readWhitespace();
tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
-
- //grab the URI value
uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1");
this._readWhitespace();
mediaList = this._media_query_list();
-
- //must end with a semicolon
tokenStream.mustMatch(Tokens.SEMICOLON);
this._readWhitespace();
@@ -4281,41 +2783,26 @@ Parser.prototype = function(){
},
- _namespace: function(emit){
- /*
- * namespace
- * : NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
- */
+ _namespace: function(emit){
var tokenStream = this._tokenStream,
line,
col,
prefix,
uri;
-
- //read import symbol
tokenStream.mustMatch(Tokens.NAMESPACE_SYM);
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
this._readWhitespace();
-
- //it's a namespace prefix - no _namespace_prefix() method because it's just an IDENT
if (tokenStream.match(Tokens.IDENT)){
prefix = tokenStream.token().value;
this._readWhitespace();
}
tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
- /*if (!tokenStream.match(Tokens.STRING)){
- tokenStream.mustMatch(Tokens.URI);
- }*/
-
- //grab the URI value
uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1");
this._readWhitespace();
-
- //must end with a semicolon
tokenStream.mustMatch(Tokens.SEMICOLON);
this._readWhitespace();
@@ -4332,17 +2819,10 @@ Parser.prototype = function(){
},
_media: function(){
- /*
- * media
- * : MEDIA_SYM S* media_query_list S* '{' S* ruleset* '}' S*
- * ;
- */
var tokenStream = this._tokenStream,
line,
col,
mediaList;// = [];
-
- //look for @media
tokenStream.mustMatch(Tokens.MEDIA_SYM);
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
@@ -4379,15 +2859,7 @@ Parser.prototype = function(){
col: col
});
},
-
-
- //CSS3 Media Queries
_media_query_list: function(){
- /*
- * media_query_list
- * : S* [media_query [ ',' S* media_query ]* ]?
- * ;
- */
var tokenStream = this._tokenStream,
mediaList = [];
@@ -4405,19 +2877,7 @@ Parser.prototype = function(){
return mediaList;
},
-
- /*
- * Note: "expression" in the grammar maps to the _media_expression
- * method.
-
- */
_media_query: function(){
- /*
- * media_query
- * : [ONLY | NOT]? S* media_type S* [ AND S* expression ]*
- * | expression [ AND S* expression ]*
- * ;
- */
var tokenStream = this._tokenStream,
type = null,
ident = null,
@@ -4426,8 +2886,6 @@ Parser.prototype = function(){
if (tokenStream.match(Tokens.IDENT)){
ident = tokenStream.token().value.toLowerCase();
-
- //since there's no custom tokens for these, need to manually check
if (ident != "only" && ident != "not"){
tokenStream.unget();
ident = null;
@@ -4466,31 +2924,10 @@ Parser.prototype = function(){
return new MediaQuery(ident, type, expressions, token.startLine, token.startCol);
},
-
- //CSS3 Media Queries
_media_type: function(){
- /*
- * media_type
- * : IDENT
- * ;
- */
return this._media_feature();
},
-
- /**
- * Note: in CSS3 Media Queries, this is called "expression".
- * Renamed here to avoid conflict with CSS3 Selectors
- * definition of "expression". Also note that "expr" in the
- * grammar now maps to "expression" from CSS3 selectors.
- * @method _media_expression
- * @private
- */
_media_expression: function(){
- /*
- * expression
- * : '(' S* media_feature S* [ ':' S* expr ]? ')' S*
- * ;
- */
var tokenStream = this._tokenStream,
feature = null,
token,
@@ -4512,36 +2949,19 @@ Parser.prototype = function(){
return new MediaFeature(feature, (expression ? new SyntaxUnit(expression, token.startLine, token.startCol) : null));
},
-
- //CSS3 Media Queries
_media_feature: function(){
- /*
- * media_feature
- * : IDENT
- * ;
- */
var tokenStream = this._tokenStream;
tokenStream.mustMatch(Tokens.IDENT);
return SyntaxUnit.fromToken(tokenStream.token());
},
-
- //CSS3 Paged Media
- _page: function(){
- /*
- * page:
- * PAGE_SYM S* IDENT? pseudo_page? S*
- * '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
- * ;
- */
+ _page: function(){
var tokenStream = this._tokenStream,
line,
col,
identifier = null,
pseudoPage = null;
-
- //look for @page
tokenStream.mustMatch(Tokens.PAGE_SYM);
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
@@ -4550,14 +2970,10 @@ Parser.prototype = function(){
if (tokenStream.match(Tokens.IDENT)){
identifier = tokenStream.token().value;
-
- //The value 'auto' may not be used as a page name and MUST be treated as a syntax error.
if (identifier.toLowerCase() === "auto"){
this._unexpectedToken(tokenStream.token());
}
}
-
- //see if there's a colon upcoming
if (tokenStream.peek() == Tokens.COLON){
pseudoPage = this._pseudo_page();
}
@@ -4583,14 +2999,7 @@ Parser.prototype = function(){
});
},
-
- //CSS3 Paged Media
_margin: function(){
- /*
- * margin :
- * margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S*
- * ;
- */
var tokenStream = this._tokenStream,
line,
col,
@@ -4620,31 +3029,8 @@ Parser.prototype = function(){
return false;
}
},
-
- //CSS3 Paged Media
_margin_sym: function(){
- /*
- * margin_sym :
- * TOPLEFTCORNER_SYM |
- * TOPLEFT_SYM |
- * TOPCENTER_SYM |
- * TOPRIGHT_SYM |
- * TOPRIGHTCORNER_SYM |
- * BOTTOMLEFTCORNER_SYM |
- * BOTTOMLEFT_SYM |
- * BOTTOMCENTER_SYM |
- * BOTTOMRIGHT_SYM |
- * BOTTOMRIGHTCORNER_SYM |
- * LEFTTOP_SYM |
- * LEFTMIDDLE_SYM |
- * LEFTBOTTOM_SYM |
- * RIGHTTOP_SYM |
- * RIGHTMIDDLE_SYM |
- * RIGHTBOTTOM_SYM
- * ;
- */
-
var tokenStream = this._tokenStream;
if(tokenStream.match([Tokens.TOPLEFTCORNER_SYM, Tokens.TOPLEFT_SYM,
@@ -4663,34 +3049,19 @@ Parser.prototype = function(){
},
_pseudo_page: function(){
- /*
- * pseudo_page
- * : ':' IDENT
- * ;
- */
var tokenStream = this._tokenStream;
tokenStream.mustMatch(Tokens.COLON);
tokenStream.mustMatch(Tokens.IDENT);
- //TODO: CSS3 Paged Media says only "left", "center", and "right" are allowed
-
return tokenStream.token().value;
},
- _font_face: function(){
- /*
- * font_face
- * : FONT_FACE_SYM S*
- * '{' S* declaration [ ';' S* declaration ]* '}' S*
- * ;
- */
+ _font_face: function(){
var tokenStream = this._tokenStream,
line,
col;
-
- //look for @page
tokenStream.mustMatch(Tokens.FONT_FACE_SYM);
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
@@ -4712,13 +3083,7 @@ Parser.prototype = function(){
});
},
- _operator: function(){
-
- /*
- * operator
- * : '/' S* | ',' S* | /( empty )/
- * ;
- */
+ _operator: function(){
var tokenStream = this._tokenStream,
token = null;
@@ -4731,13 +3096,7 @@ Parser.prototype = function(){
},
- _combinator: function(){
-
- /*
- * combinator
- * : PLUS S* | GREATER S* | TILDE S* | S+
- * ;
- */
+ _combinator: function(){
var tokenStream = this._tokenStream,
value = null,
@@ -4753,12 +3112,6 @@ Parser.prototype = function(){
},
_unary_operator: function(){
-
- /*
- * unary_operator
- * : '-' | '+'
- * ;
- */
var tokenStream = this._tokenStream;
@@ -4770,12 +3123,6 @@ Parser.prototype = function(){
},
_property: function(){
-
- /*
- * property
- * : IDENT S*
- * ;
- */
var tokenStream = this._tokenStream,
value = null,
@@ -4784,8 +3131,6 @@ Parser.prototype = function(){
token,
line,
col;
-
- //check for star hack - throws error if not allowed
if (tokenStream.peek() == Tokens.STAR && this.options.starHack){
tokenStream.get();
token = tokenStream.token();
@@ -4797,8 +3142,6 @@ Parser.prototype = function(){
if(tokenStream.match(Tokens.IDENT)){
token = tokenStream.token();
tokenValue = token.value;
-
- //check for underscore hack - no error if not allowed because it's valid CSS syntax
if (tokenValue.charAt(0) == "_" && this.options.underscoreHack){
hack = "_";
tokenValue = tokenValue.substring(1);
@@ -4810,31 +3153,15 @@ Parser.prototype = function(){
return value;
},
-
- //Augmented with CSS3 Selectors
- _ruleset: function(){
- /*
- * ruleset
- * : selectors_group
- * '{' S* declaration? [ ';' S* declaration? ]* '}' S*
- * ;
- */
+ _ruleset: function(){
var tokenStream = this._tokenStream,
tt,
selectors;
-
-
- /*
- * Error Recovery: If even a single selector fails to parse,
- * then the entire ruleset should be thrown away.
- */
try {
selectors = this._selectors_group();
} catch (ex){
if (ex instanceof SyntaxError && !this.options.strict){
-
- //fire error event
this.fire({
type: "error",
error: ex,
@@ -4842,26 +3169,17 @@ Parser.prototype = function(){
line: ex.line,
col: ex.col
});
-
- //skip over everything until closing brace
tt = tokenStream.advance([Tokens.RBRACE]);
if (tt == Tokens.RBRACE){
- //if there's a right brace, the rule is finished so don't do anything
} else {
- //otherwise, rethrow the error because it wasn't handled properly
throw ex;
}
} else {
- //not a syntax error, rethrow it
throw ex;
}
-
- //trigger parser to continue
return true;
}
-
- //if it got here, all selectors parsed
if (selectors){
this.fire({
@@ -4885,15 +3203,7 @@ Parser.prototype = function(){
return selectors;
},
-
- //CSS3 Selectors
- _selectors_group: function(){
-
- /*
- * selectors_group
- * : selector [ COMMA S* selector ]*
- * ;
- */
+ _selectors_group: function(){
var tokenStream = this._tokenStream,
selectors = [],
selector;
@@ -4915,22 +3225,13 @@ Parser.prototype = function(){
return selectors.length ? selectors : null;
},
-
- //CSS3 Selectors
_selector: function(){
- /*
- * selector
- * : simple_selector_sequence [ combinator simple_selector_sequence ]*
- * ;
- */
var tokenStream = this._tokenStream,
selector = [],
nextSelector = null,
combinator = null,
ws = null;
-
- //if there's no simple selector, then there's no selector
nextSelector = this._simple_selector_sequence();
if (nextSelector === null){
return null;
@@ -4939,34 +3240,20 @@ Parser.prototype = function(){
selector.push(nextSelector);
do {
-
- //look for a combinator
combinator = this._combinator();
if (combinator !== null){
selector.push(combinator);
nextSelector = this._simple_selector_sequence();
-
- //there must be a next selector
if (nextSelector === null){
- this._unexpectedToken(this.LT(1));
+ this._unexpectedToken(tokenStream.LT(1));
} else {
-
- //nextSelector is an instance of SelectorPart
selector.push(nextSelector);
}
} else {
-
- //if there's not whitespace, we're done
if (this._readWhitespace()){
-
- //add whitespace separator
ws = new Combinator(tokenStream.token().value, tokenStream.token().startLine, tokenStream.token().startCol);
-
- //combinator is not required
combinator = this._combinator();
-
- //selector is required if there's a combinator
nextSelector = this._simple_selector_sequence();
if (nextSelector === null){
if (combinator !== null){
@@ -4991,29 +3278,13 @@ Parser.prototype = function(){
return new Selector(selector, selector[0].line, selector[0].col);
},
-
- //CSS3 Selectors
_simple_selector_sequence: function(){
- /*
- * simple_selector_sequence
- * : [ type_selector | universal ]
- * [ HASH | class | attrib | pseudo | negation ]*
- * | [ HASH | class | attrib | pseudo | negation ]+
- * ;
- */
var tokenStream = this._tokenStream,
-
- //parts of a simple selector
elementName = null,
modifiers = [],
-
- //complete selector text
selectorText= "",
-
- //the different parts after the element name to search for
components = [
- //HASH
function(){
return tokenStream.match(Tokens.HASH) ?
new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
@@ -5030,9 +3301,6 @@ Parser.prototype = function(){
found = false,
line,
col;
-
-
- //get starting line and column for the selector
line = tokenStream.LT(1).startLine;
col = tokenStream.LT(1).startCol;
@@ -5046,20 +3314,14 @@ Parser.prototype = function(){
}
while(true){
-
- //whitespace means we're done
if (tokenStream.peek() === Tokens.S){
break;
}
-
- //check for each component
while(i < len && component === null){
component = components[i++].call(this);
}
if (component === null){
-
- //we don't have a selector
if (selectorText === ""){
return null;
} else {
@@ -5078,26 +3340,13 @@ Parser.prototype = function(){
new SelectorPart(elementName, modifiers, selectorText, line, col) :
null;
},
-
- //CSS3 Selectors
_type_selector: function(){
- /*
- * type_selector
- * : [ namespace_prefix ]? element_name
- * ;
- */
var tokenStream = this._tokenStream,
ns = this._namespace_prefix(),
elementName = this._element_name();
if (!elementName){
- /*
- * Need to back out the namespace that was read due to both
- * type_selector and universal reading namespace_prefix
- * first. Kind of hacky, but only way I can figure out
- * right now how to not change the grammar.
- */
if (ns){
tokenStream.unget();
if (ns.length > 1){
@@ -5114,14 +3363,7 @@ Parser.prototype = function(){
return elementName;
}
},
-
- //CSS3 Selectors
- _class: function(){
- /*
- * class
- * : '.' IDENT
- * ;
- */
+ _class: function(){
var tokenStream = this._tokenStream,
token;
@@ -5135,14 +3377,7 @@ Parser.prototype = function(){
}
},
-
- //CSS3 Selectors
- _element_name: function(){
- /*
- * element_name
- * : IDENT
- * ;
- */
+ _element_name: function(){
var tokenStream = this._tokenStream,
token;
@@ -5155,18 +3390,9 @@ Parser.prototype = function(){
return null;
}
},
-
- //CSS3 Selectors
_namespace_prefix: function(){
- /*
- * namespace_prefix
- * : [ IDENT | '*' ]? '|'
- * ;
- */
var tokenStream = this._tokenStream,
value = "";
-
- //verify that this is a namespace prefix
if (tokenStream.LA(1) === Tokens.PIPE || tokenStream.LA(2) === Tokens.PIPE){
if(tokenStream.match([Tokens.IDENT, Tokens.STAR])){
@@ -5180,14 +3406,7 @@ Parser.prototype = function(){
return value.length ? value : null;
},
-
- //CSS3 Selectors
_universal: function(){
- /*
- * universal
- * : [ namespace_prefix ]? '*'
- * ;
- */
var tokenStream = this._tokenStream,
value = "",
ns;
@@ -5204,21 +3423,7 @@ Parser.prototype = function(){
return value.length ? value : null;
},
-
- //CSS3 Selectors
_attrib: function(){
- /*
- * attrib
- * : '[' S* [ namespace_prefix ]? IDENT S*
- * [ [ PREFIXMATCH |
- * SUFFIXMATCH |
- * SUBSTRINGMATCH |
- * '=' |
- * INCLUDES |
- * DASHMATCH ] S* [ IDENT | STRING ] S*
- * ]? ']'
- * ;
- */
var tokenStream = this._tokenStream,
value = null,
@@ -5258,15 +3463,7 @@ Parser.prototype = function(){
return null;
}
},
-
- //CSS3 Selectors
- _pseudo: function(){
-
- /*
- * pseudo
- * : ':' ':'? [ IDENT | functional_pseudo ]
- * ;
- */
+ _pseudo: function(){
var tokenStream = this._tokenStream,
pseudo = null,
@@ -5297,14 +3494,7 @@ Parser.prototype = function(){
return pseudo;
},
-
- //CSS3 Selectors
- _functional_pseudo: function(){
- /*
- * functional_pseudo
- * : FUNCTION S* expression ')'
- * ;
- */
+ _functional_pseudo: function(){
var tokenStream = this._tokenStream,
value = null;
@@ -5319,14 +3509,7 @@ Parser.prototype = function(){
return value;
},
-
- //CSS3 Selectors
_expression: function(){
- /*
- * expression
- * : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
- * ;
- */
var tokenStream = this._tokenStream,
value = "";
@@ -5343,14 +3526,7 @@ Parser.prototype = function(){
return value.length ? value : null;
},
-
- //CSS3 Selectors
_negation: function(){
- /*
- * negation
- * : NOT S* negation_arg S* ')'
- * ;
- */
var tokenStream = this._tokenStream,
line,
@@ -5376,14 +3552,7 @@ Parser.prototype = function(){
return subpart;
},
-
- //CSS3 Selectors
- _negation_arg: function(){
- /*
- * negation_arg
- * : type_selector | universal | HASH | class | attrib | pseudo
- * ;
- */
+ _negation_arg: function(){
var tokenStream = this._tokenStream,
args = [
@@ -5414,13 +3583,9 @@ Parser.prototype = function(){
arg = args[i].call(this);
i++;
}
-
- //must be a negation arg
if (arg === null){
this._unexpectedToken(tokenStream.LT(1));
}
-
- //it's an element name
if (arg.type == "elementName"){
part = new SelectorPart(arg, [], arg.toString(), line, col);
} else {
@@ -5430,19 +3595,15 @@ Parser.prototype = function(){
return part;
},
- _declaration: function(){
-
- /*
- * declaration
- * : property ':' S* expr prio?
- * | /( empty )/
- * ;
- */
+ _declaration: function(){
var tokenStream = this._tokenStream,
property = null,
expr = null,
- prio = null;
+ prio = null,
+ error = null,
+ invalid = null,
+ propertyName= "";
property = this._property();
if (property !== null){
@@ -5451,13 +3612,23 @@ Parser.prototype = function(){
this._readWhitespace();
expr = this._expr();
-
- //if there's no parts for the value, it's an error
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",
@@ -5465,7 +3636,8 @@ Parser.prototype = function(){
value: expr,
important: prio,
line: property.line,
- col: property.col
+ col: property.col,
+ invalid: invalid
});
return true;
@@ -5475,11 +3647,6 @@ Parser.prototype = function(){
},
_prio: function(){
- /*
- * prio
- * : IMPORTANT_SYM S*
- * ;
- */
var tokenStream = this._tokenStream,
result = tokenStream.match(Tokens.IMPORTANT_SYM);
@@ -5489,15 +3656,9 @@ Parser.prototype = function(){
},
_expr: function(){
- /*
- * expr
- * : term [ operator term ]*
- * ;
- */
var tokenStream = this._tokenStream,
values = [],
- //valueParts = [],
value = null,
operator = null;
@@ -5508,12 +3669,9 @@ Parser.prototype = function(){
do {
operator = this._operator();
-
- //if there's an operator, keep building up the value parts
if (operator){
values.push(operator);
} /*else {
- //if there's not an operator, you have a full value
values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
valueParts = [];
}*/
@@ -5527,40 +3685,23 @@ Parser.prototype = function(){
}
} while(true);
}
-
- //cleanup
- /*if (valueParts.length){
- values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
- }*/
- return values.length > 0 ? new PropertyValue(values, values[0].startLine, values[0].startCol) : null;
+ return values.length > 0 ? new PropertyValue(values, values[0].line, values[0].col) : null;
},
- _term: function(){
-
- /*
- * term
- * : unary_operator?
- * [ NUMBER S* | PERCENTAGE S* | LENGTH S* | ANGLE S* |
- * TIME S* | FREQ S* | function | ie_function ]
- * | STRING S* | IDENT S* | URI S* | UNICODERANGE S* | hexcolor
- * ;
- */
+ _term: function(){
var tokenStream = this._tokenStream,
unary = null,
value = null,
+ token,
line,
col;
-
- //returns the operator or null
unary = this._unary_operator();
if (unary !== null){
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
}
-
- //exception for IE filters
if (tokenStream.peek() == Tokens.IE_FUNCTION && this.options.ieFilters){
value = this._ie_function();
@@ -5568,8 +3709,6 @@ Parser.prototype = function(){
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
}
-
- //see if there's a simple match
} 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])){
@@ -5581,40 +3720,25 @@ Parser.prototype = function(){
}
this._readWhitespace();
} else {
-
- //see if it's a color
- value = this._hexcolor();
- if (value === null){
-
- //if there's no unary, get the start of the next token for line/col info
+ token = this._hexcolor();
+ if (token === null){
if (unary === null){
line = tokenStream.LT(1).startLine;
col = tokenStream.LT(1).startCol;
}
-
- //has to be a function
if (value === null){
-
- /*
- * This checks for alpha(opacity=0) style of IE
- * functions. IE_FUNCTION only presents progid: style.
- */
if (tokenStream.LA(3) == Tokens.EQUALS && this.options.ieFilters){
value = this._ie_function();
} else {
value = this._function();
}
}
-
- /*if (value === null){
- return null;
- //throw new Error("Expected identifier at line " + tokenStream.token().startLine + ", character " + tokenStream.token().startCol + ".");
- }*/
} else {
+ value = token.value;
if (unary === null){
- line = tokenStream.token().startLine;
- col = tokenStream.token().startCol;
+ line = token.startLine;
+ col = token.startCol;
}
}
@@ -5627,24 +3751,43 @@ Parser.prototype = function(){
},
_function: function(){
-
- /*
- * function
- * : FUNCTION S* expr ')' S*
- * ;
- */
var tokenStream = this._tokenStream,
functionText = null,
- expr = null;
+ expr = null,
+ lt;
if (tokenStream.match(Tokens.FUNCTION)){
functionText = tokenStream.token().value;
this._readWhitespace();
expr = this._expr();
+ 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 += expr + ")";
+ functionText += ")";
this._readWhitespace();
}
@@ -5652,19 +3795,11 @@ Parser.prototype = function(){
},
_ie_function: function(){
-
- /* (My own extension)
- * ie_function
- * : IE_FUNCTION S* IDENT '=' term [S* ','? IDENT '=' term]+ ')' S*
- * ;
- */
var tokenStream = this._tokenStream,
functionText = null,
expr = null,
lt;
-
- //IE function can begin like a regular function, too
if (tokenStream.match([Tokens.IE_FUNCTION, Tokens.FUNCTION])){
functionText = tokenStream.token().value;
@@ -5673,8 +3808,6 @@ Parser.prototype = function(){
if (this._readWhitespace()){
functionText += tokenStream.token().value;
}
-
- //might be second time in the loop
if (tokenStream.LA(0) == Tokens.COMMA){
functionText += tokenStream.token().value;
}
@@ -5684,8 +3817,6 @@ Parser.prototype = function(){
tokenStream.match(Tokens.EQUALS);
functionText += tokenStream.token().value;
-
- //functionText += this._term();
lt = tokenStream.peek();
while(lt != Tokens.COMMA && lt != Tokens.S && lt != Tokens.RPAREN){
tokenStream.get();
@@ -5703,23 +3834,12 @@ Parser.prototype = function(){
},
_hexcolor: function(){
- /*
- * There is a constraint on the color that it must
- * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
- * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
- *
- * hexcolor
- * : HASH S*
- * ;
- */
var tokenStream = this._tokenStream,
- token,
- color = null;
-
+ token = null,
+ color;
+
if(tokenStream.match(Tokens.HASH)){
-
- //need to do some validation here
token = tokenStream.token();
color = token.value;
@@ -5729,26 +3849,22 @@ Parser.prototype = function(){
this._readWhitespace();
}
- return color;
+ return token;
},
- //-----------------------------------------------------------------
- // Animations methods
- //-----------------------------------------------------------------
-
_keyframes: function(){
-
- /*
- * keyframes:
- * : KEYFRAMES_SYM S* keyframe_name S* '{' S* keyframe_rule* '}' {
- * ;
- */
var tokenStream = this._tokenStream,
token,
tt,
- name;
+ name,
+ prefix = "";
tokenStream.mustMatch(Tokens.KEYFRAMES_SYM);
+ token = tokenStream.token();
+ if (/^@\-([^\-]+)\-/.test(token.value)) {
+ prefix = RegExp.$1;
+ }
+
this._readWhitespace();
name = this._keyframe_name();
@@ -5758,14 +3874,13 @@ Parser.prototype = function(){
this.fire({
type: "startkeyframes",
name: name,
- line: name.line,
- col: name.col
+ prefix: prefix,
+ line: token.startLine,
+ col: token.startCol
});
this._readWhitespace();
tt = tokenStream.peek();
-
- //check for key
while(tt == Tokens.IDENT || tt == Tokens.PERCENTAGE) {
this._keyframe_rule();
this._readWhitespace();
@@ -5775,8 +3890,9 @@ Parser.prototype = function(){
this.fire({
type: "endkeyframes",
name: name,
- line: name.line,
- col: name.col
+ prefix: prefix,
+ line: token.startLine,
+ col: token.startCol
});
this._readWhitespace();
@@ -5785,13 +3901,6 @@ Parser.prototype = function(){
},
_keyframe_name: function(){
-
- /*
- * keyframe_name:
- * : IDENT
- * | STRING
- * ;
- */
var tokenStream = this._tokenStream,
token;
@@ -5800,13 +3909,6 @@ Parser.prototype = function(){
},
_keyframe_rule: function(){
-
- /*
- * keyframe_rule:
- * : key_list S*
- * '{' S* declaration [ ';' S* declaration ]* '}' S*
- * ;
- */
var tokenStream = this._tokenStream,
token,
keyList = this._key_list();
@@ -5830,18 +3932,10 @@ Parser.prototype = function(){
},
_key_list: function(){
-
- /*
- * key_list:
- * : key [ S* ',' S* key]*
- * ;
- */
var tokenStream = this._tokenStream,
token,
key,
keyList = [];
-
- //must be least one key
keyList.push(this._key());
this._readWhitespace();
@@ -5856,14 +3950,6 @@ Parser.prototype = function(){
},
_key: function(){
- /*
- * There is a restriction that IDENT can be only "from" or "to".
- *
- * key
- * : PERCENTAGE
- * | IDENT
- * ;
- */
var tokenStream = this._tokenStream,
token;
@@ -5879,50 +3965,13 @@ Parser.prototype = function(){
tokenStream.unget();
}
-
- //if it gets here, there wasn't a valid token, so time to explode
this._unexpectedToken(tokenStream.LT(1));
},
-
- //-----------------------------------------------------------------
- // Helper methods
- //-----------------------------------------------------------------
-
- /**
- * Not part of CSS grammar, but useful for skipping over
- * combination of white space and HTML-style comments.
- * @return {void}
- * @method _skipCruft
- * @private
- */
_skipCruft: function(){
while(this._tokenStream.match([Tokens.S, Tokens.CDO, Tokens.CDC])){
- //noop
}
},
-
- /**
- * Not part of CSS grammar, but this pattern occurs frequently
- * in the official CSS grammar. Split out here to eliminate
- * duplicate code.
- * @param {Boolean} checkStart Indicates if the rule should check
- * for the left brace at the beginning.
- * @param {Boolean} readMargins Indicates if the rule should check
- * for margin patterns.
- * @return {void}
- * @method _readDeclarations
- * @private
- */
_readDeclarations: function(checkStart, readMargins){
- /*
- * Reads the pattern
- * S* '{' S* declaration [ ';' S* declaration ]* '}' S*
- * or
- * S* '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
- * Note that this is how it is described in CSS3 Paged Media, but is actually incorrect.
- * A semicolon is only necessary following a delcaration is there's another declaration
- * or margin afterwards.
- */
var tokenStream = this._tokenStream,
tt;
@@ -5939,8 +3988,7 @@ Parser.prototype = function(){
while(true){
- if (readMargins && this._margin()){
- //noop
+ if (tokenStream.match(Tokens.SEMICOLON) || (readMargins && this._margin())){
} else if (this._declaration()){
if (!tokenStream.match(Tokens.SEMICOLON)){
break;
@@ -5948,10 +3996,6 @@ Parser.prototype = function(){
} else {
break;
}
-
- //if ((!this._margin() && !this._declaration()) || !tokenStream.match(Tokens.SEMICOLON)){
- // break;
- //}
this._readWhitespace();
}
@@ -5960,8 +4004,6 @@ Parser.prototype = function(){
} catch (ex) {
if (ex instanceof SyntaxError && !this.options.strict){
-
- //fire error event
this.fire({
type: "error",
error: ex,
@@ -5969,36 +4011,19 @@ Parser.prototype = function(){
line: ex.line,
col: ex.col
});
-
- //see if there's another declaration
tt = tokenStream.advance([Tokens.SEMICOLON, Tokens.RBRACE]);
if (tt == Tokens.SEMICOLON){
- //if there's a semicolon, then there might be another declaration
- this._readDeclarations(false, readMargins);
- } else if (tt == Tokens.RBRACE){
- //if there's a right brace, the rule is finished so don't do anything
- } else {
- //otherwise, rethrow the error because it wasn't handled properly
+ this._readDeclarations(false, readMargins);
+ } else if (tt != Tokens.RBRACE){
throw ex;
}
} else {
- //not a syntax error, rethrow it
throw ex;
}
}
},
-
- /**
- * In some cases, you can end up with two white space tokens in a
- * row. Instead of making a change in every function that looks for
- * white space, this function is used to match as much white space
- * as necessary.
- * @method _readWhitespace
- * @return {String} The white space if found, empty string if not.
- * @private
- */
_readWhitespace: function(){
var tokenStream = this._tokenStream,
@@ -6010,34 +4035,17 @@ Parser.prototype = function(){
return ws;
},
-
-
- /**
- * Throws an error when an unexpected token is found.
- * @param {Object} token The token that was found.
- * @method _unexpectedToken
- * @return {void}
- * @private
- */
_unexpectedToken: function(token){
throw new SyntaxError("Unexpected token '" + token.value + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
},
-
- /**
- * Helper method used for parsing subparts of a style sheet.
- * @return {void}
- * @method _verifyEnd
- * @private
- */
_verifyEnd: function(){
if (this._tokenStream.LA(1) != Tokens.EOF){
this._unexpectedToken(this._tokenStream.LT(1));
}
},
-
- //-----------------------------------------------------------------
- // Parsing methods
- //-----------------------------------------------------------------
+ _validateProperty: function(property, value){
+ Validation.validate(property, value);
+ },
parse: function(input){
this._tokenStream = new TokenStream(input, Tokens);
@@ -6045,202 +4053,558 @@ Parser.prototype = function(){
},
parseStyleSheet: function(input){
- //just passthrough
return this.parse(input);
},
parseMediaQuery: function(input){
this._tokenStream = new TokenStream(input, Tokens);
var result = this._media_query();
-
- //if there's anything more, then it's an invalid selector
this._verifyEnd();
-
- //otherwise return result
return result;
- },
-
- /**
- * Parses a property value (everything after the semicolon).
- * @return {parserlib.css.PropertyValue} The property value.
- * @throws parserlib.util.SyntaxError If an unexpected token is found.
- * @method parserPropertyValue
- */
+ },
parsePropertyValue: function(input){
this._tokenStream = new TokenStream(input, Tokens);
this._readWhitespace();
var result = this._expr();
-
- //okay to have a trailing white space
this._readWhitespace();
-
- //if there's anything more, then it's an invalid selector
this._verifyEnd();
-
- //otherwise return result
return result;
},
-
- /**
- * Parses a complete CSS rule, including selectors and
- * properties.
- * @param {String} input The text to parser.
- * @return {Boolean} True if the parse completed successfully, false if not.
- * @method parseRule
- */
parseRule: function(input){
this._tokenStream = new TokenStream(input, Tokens);
-
- //skip any leading white space
this._readWhitespace();
var result = this._ruleset();
-
- //skip any trailing white space
this._readWhitespace();
-
- //if there's anything more, then it's an invalid selector
this._verifyEnd();
-
- //otherwise return result
return result;
},
-
- /**
- * Parses a single CSS selector (no comma)
- * @param {String} input The text to parse as a CSS selector.
- * @return {Selector} An object representing the selector.
- * @throws parserlib.util.SyntaxError If an unexpected token is found.
- * @method parseSelector
- */
parseSelector: function(input){
this._tokenStream = new TokenStream(input, Tokens);
-
- //skip any leading white space
this._readWhitespace();
var result = this._selector();
-
- //skip any trailing white space
this._readWhitespace();
-
- //if there's anything more, then it's an invalid selector
this._verifyEnd();
-
- //otherwise return result
return result;
+ },
+ parseStyleAttribute: function(input){
+ input += "}"; // for error recovery in _readDeclarations()
+ this._tokenStream = new TokenStream(input, Tokens);
+ this._readDeclarations();
}
-
};
-
- //copy over onto prototype
for (prop in additions){
- proto[prop] = additions[prop];
+ if (additions.hasOwnProperty(prop)){
+ proto[prop] = additions[prop];
+ }
}
return proto;
}();
-
-
-/*
-nth
- : S* [ ['-'|'+']? INTEGER? {N} [ S* ['-'|'+'] S* INTEGER ]? |
- ['-'|'+']? INTEGER | {O}{D}{D} | {E}{V}{E}{N} ] S*
- ;
-*/
-/**
- * Represents a selector combinator (whitespace, +, >).
- * @namespace parserlib.css
- * @class PropertyName
- * @extends parserlib.util.SyntaxUnit
- * @constructor
- * @param {String} text The text representation of the unit.
- * @param {String} hack The type of IE hack applied ("*", "_", or null).
- * @param {int} line The line of text on which the unit resides.
- * @param {int} col The column of text on which the unit resides.
- */
+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 | 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>",
+ "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,
+ numeric = "<length> | <percentage>",
+ slash = false,
+ fill = false,
+ count = 0,
+ max = 8,
+ part;
+
+ while (expression.hasNext() && count < max) {
+ valid = ValidationTypes.isAny(expression, numeric);
+ if (!valid) {
+
+ if (expression.peek() == "/" && count > 1 && !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",
+ "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" : "<percentage> | <length> | baseline | sub | super | top | text-top | middle | bottom | text-bottom | inherit",
+ "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",
+ "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, (hack||"") + text, line, col);
-
- /**
- * The type of IE hack applied ("*", "_", or null).
- * @type String
- * @property hack
- */
+ SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_NAME_TYPE);
this.hack = hack;
}
PropertyName.prototype = new SyntaxUnit();
PropertyName.prototype.constructor = PropertyName;
-
-
-/**
- * Represents a single part of a CSS property value, meaning that it represents
- * just everything single part between ":" and ";". If there are multiple values
- * separated by commas, this type represents just one of the values.
- * @param {String[]} parts An array of value parts making up this value.
- * @param {int} line The line of text on which the unit resides.
- * @param {int} col The column of text on which the unit resides.
- * @namespace parserlib.css
- * @class PropertyValue
- * @extends parserlib.util.SyntaxUnit
- * @constructor
- */
+PropertyName.prototype.toString = function(){
+ return (this.hack ? this.hack : "") + this.text;
+};
function PropertyValue(parts, line, col){
- SyntaxUnit.call(this, parts.join(" "), line, col);
-
- /**
- * The parts that make up the selector.
- * @type Array
- * @property parts
- */
+ SyntaxUnit.call(this, parts.join(" "), line, col, Parser.PROPERTY_VALUE_TYPE);
this.parts = parts;
}
PropertyValue.prototype = new SyntaxUnit();
PropertyValue.prototype.constructor = PropertyValue;
-
-
-/**
- * Represents a single part of a CSS property value, meaning that it represents
- * just one part of the data between ":" and ";".
- * @param {String} text The text representation of the unit.
- * @param {int} line The line of text on which the unit resides.
- * @param {int} col The column of text on which the unit resides.
- * @namespace parserlib.css
- * @class PropertyValuePart
- * @extends parserlib.util.SyntaxUnit
- * @constructor
- */
+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.apply(this,arguments);
-
- /**
- * Indicates the type of value unit.
- * @type String
- * @property type
- */
+ SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_VALUE_PART_TYPE);
this.type = "unknown";
-
- //figure out what type of data it is
var temp;
-
- //it is a measurement?
if (/^([+\-]?[\d\.]+)([a-z]+)$/i.test(text)){ //dimension
this.type = "dimension";
this.value = +RegExp.$1;
this.units = RegExp.$2;
-
- //try to narrow down
switch(this.units.toLowerCase()){
case "em":
@@ -6252,6 +4616,7 @@ function PropertyValuePart(text, line, col){
case "in":
case "pt":
case "pc":
+ case "ch":
this.type = "length";
break;
@@ -6276,8 +4641,6 @@ function PropertyValuePart(text, line, col){
this.type = "resolution";
break;
- //default
-
}
} else if (/^([+\-]?[\d\.]+)%$/i.test(text)){ //percentage
@@ -6315,9 +4678,36 @@ function PropertyValuePart(text, line, col){
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);
@@ -6338,163 +4728,170 @@ function PropertyValuePart(text, line, col){
}
PropertyValuePart.prototype = new SyntaxUnit();
-PropertyValuePart.prototype.constructor = PropertyValue;
-
-/**
- * Create a new syntax unit based solely on the given token.
- * Convenience method for creating a new syntax unit when
- * it represents a single token instead of multiple.
- * @param {Object} token The token object to represent.
- * @return {parserlib.css.PropertyValuePart} The object representing the token.
- * @static
- * @method fromToken
- */
+PropertyValuePart.prototype.constructor = PropertyValuePart;
PropertyValuePart.fromToken = function(token){
return new PropertyValuePart(token.value, token.startLine, token.startCol);
};
-/**
- * Represents an entire single selector, including all parts but not
- * including multiple selectors (those separated by commas).
- * @namespace parserlib.css
- * @class Selector
- * @extends parserlib.util.SyntaxUnit
- * @constructor
- * @param {Array} parts Array of selectors parts making up this selector.
- * @param {int} line The line of text on which the unit resides.
- * @param {int} col The column of text on which the unit resides.
- */
+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);
-
- /**
- * The parts that make up the selector.
- * @type Array
- * @property parts
- */
+ 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;
-
-
-/**
- * Represents a single part of a selector string, meaning a single set of
- * element name and modifiers. This does not include combinators such as
- * spaces, +, >, etc.
- * @namespace parserlib.css
- * @class SelectorPart
- * @extends parserlib.util.SyntaxUnit
- * @constructor
- * @param {String} elementName The element name in the selector or null
- * if there is no element name.
- * @param {Array} modifiers Array of individual modifiers for the element.
- * May be empty if there are none.
- * @param {String} text The text representation of the unit.
- * @param {int} line The line of text on which the unit resides.
- * @param {int} col The column of text on which the unit resides.
- */
function SelectorPart(elementName, modifiers, text, line, col){
- SyntaxUnit.call(this, text, line, col);
-
- /**
- * The tag name of the element to which this part
- * of the selector affects.
- * @type String
- * @property elementName
- */
+ SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_PART_TYPE);
this.elementName = elementName;
-
- /**
- * The parts that come after the element name, such as class names, IDs,
- * pseudo classes/elements, etc.
- * @type Array
- * @property modifiers
- */
this.modifiers = modifiers;
}
SelectorPart.prototype = new SyntaxUnit();
SelectorPart.prototype.constructor = SelectorPart;
-
-
-/**
- * Represents a selector modifier string, meaning a class name, element name,
- * element ID, pseudo rule, etc.
- * @namespace parserlib.css
- * @class SelectorSubPart
- * @extends parserlib.util.SyntaxUnit
- * @constructor
- * @param {String} text The text representation of the unit.
- * @param {String} type The type of selector modifier.
- * @param {int} line The line of text on which the unit resides.
- * @param {int} col The column of text on which the unit resides.
- */
function SelectorSubPart(text, type, line, col){
- SyntaxUnit.call(this, text, line, col);
-
- /**
- * The type of modifier.
- * @type String
- * @property type
- */
+ SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_SUB_PART_TYPE);
this.type = type;
-
- /**
- * Some subparts have arguments, this represents them.
- * @type Array
- * @property args
- */
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/;
-//-----------------------------------------------------------------------------
-// Helper functions
-//-----------------------------------------------------------------------------
-
function isHexDigit(c){
- return c != null && h.test(c);
+ return c !== null && h.test(c);
}
function isDigit(c){
- return c != null && /\d/.test(c);
+ return c !== null && /\d/.test(c);
}
function isWhitespace(c){
- return c != null && /\s/.test(c);
+ return c !== null && /\s/.test(c);
}
function isNewLine(c){
- return c != null && nl.test(c);
+ return c !== null && nl.test(c);
}
function isNameStart(c){
- return c != null && (/[a-z_\u0080-\uFFFF\\]/i.test(c));
+ return c !== null && (/[a-z_\u0080-\uFFFF\\]/i.test(c));
}
function isNameChar(c){
- return c != null && (isNameStart(c) || /[0-9\-\\]/.test(c));
+ return c !== null && (isNameStart(c) || /[0-9\-\\]/.test(c));
}
function isIdentStart(c){
- return c != null && (isNameStart(c) || /\-\\/.test(c));
+ return c !== null && (isNameStart(c) || /\-\\/.test(c));
}
function mix(receiver, supplier){
@@ -6505,34 +4902,11 @@ function mix(receiver, supplier){
}
return receiver;
}
-
-//-----------------------------------------------------------------------------
-// CSS Token Stream
-//-----------------------------------------------------------------------------
-
-
-/**
- * A token stream that produces CSS tokens.
- * @param {String|Reader} input The source of text to tokenize.
- * @constructor
- * @class TokenStream
- * @namespace parserlib.css
- */
function TokenStream(input){
TokenStreamBase.call(this, input, Tokens);
}
TokenStream.prototype = mix(new TokenStreamBase(), {
-
- /**
- * Overrides the TokenStreamBase method of the same name
- * to produce CSS tokens.
- * @param {variant} channel The name of the channel to use
- * for the next token.
- * @return {Object} A token object representing the next token.
- * @method _getToken
- * @private
- */
_getToken: function(channel){
var c,
@@ -6546,13 +4920,6 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
while(c){
switch(c){
-
- /*
- * Potential tokens:
- * - COMMENT
- * - SLASH
- * - CHAR
- */
case "/":
if(reader.peek() == "*"){
@@ -6561,16 +4928,6 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
token = this.charToken(c, startLine, startCol);
}
break;
-
- /*
- * Potential tokens:
- * - DASHMATCH
- * - INCLUDES
- * - PREFIXMATCH
- * - SUFFIXMATCH
- * - SUBSTRINGMATCH
- * - CHAR
- */
case "|":
case "~":
case "^":
@@ -6582,22 +4939,10 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
token = this.charToken(c, startLine, startCol);
}
break;
-
- /*
- * Potential tokens:
- * - STRING
- * - INVALID
- */
case "\"":
case "'":
token = this.stringToken(c, startLine, startCol);
break;
-
- /*
- * Potential tokens:
- * - HASH
- * - CHAR
- */
case "#":
if (isNameChar(reader.peek())){
token = this.hashToken(c, startLine, startCol);
@@ -6605,14 +4950,6 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
token = this.charToken(c, startLine, startCol);
}
break;
-
- /*
- * Potential tokens:
- * - DOT
- * - NUMBER
- * - DIMENSION
- * - PERCENTAGE
- */
case ".":
if (isDigit(reader.peek())){
token = this.numberToken(c, startLine, startCol);
@@ -6620,15 +4957,6 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
token = this.charToken(c, startLine, startCol);
}
break;
-
- /*
- * Potential tokens:
- * - CDC
- * - MINUS
- * - NUMBER
- * - DIMENSION
- * - PERCENTAGE
- */
case "-":
if (reader.peek() == "-"){ //could be closing HTML-style comment
token = this.htmlCommentEndToken(c, startLine, startCol);
@@ -6638,93 +4966,34 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
token = this.charToken(c, startLine, startCol);
}
break;
-
- /*
- * Potential tokens:
- * - IMPORTANT_SYM
- * - CHAR
- */
case "!":
token = this.importantToken(c, startLine, startCol);
break;
-
- /*
- * Any at-keyword or CHAR
- */
case "@":
token = this.atRuleToken(c, startLine, startCol);
break;
-
- /*
- * Potential tokens:
- * - NOT
- * - CHAR
- */
case ":":
token = this.notToken(c, startLine, startCol);
break;
-
- /*
- * Potential tokens:
- * - CDO
- * - CHAR
- */
case "<":
token = this.htmlCommentStartToken(c, startLine, startCol);
break;
-
- /*
- * Potential tokens:
- * - UNICODE_RANGE
- * - URL
- * - CHAR
- */
case "U":
case "u":
if (reader.peek() == "+"){
token = this.unicodeRangeToken(c, startLine, startCol);
break;
}
- /*falls through*/
-
default:
-
- /*
- * Potential tokens:
- * - NUMBER
- * - DIMENSION
- * - LENGTH
- * - FREQ
- * - TIME
- * - EMS
- * - EXS
- * - ANGLE
- */
if (isDigit(c)){
token = this.numberToken(c, startLine, startCol);
} else
-
- /*
- * Potential tokens:
- * - S
- */
if (isWhitespace(c)){
token = this.whitespaceToken(c, startLine, startCol);
} else
-
- /*
- * Potential tokens:
- * - IDENT
- */
if (isIdentStart(c)){
token = this.identOrFunctionToken(c, startLine, startCol);
} else
-
- /*
- * Potential tokens:
- * - CHAR
- * - PLUS
- */
{
token = this.charToken(c, startLine, startCol);
}
@@ -6735,40 +5004,15 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
}
-
- //make sure this token is wanted
- //TODO: check channel
break;
-
- c = reader.read();
}
- if (!token && c == null){
+ if (!token && c === null){
token = this.createToken(Tokens.EOF,null,startLine,startCol);
}
return token;
},
-
- //-------------------------------------------------------------------------
- // Methods to create tokens
- //-------------------------------------------------------------------------
-
- /**
- * Produces a token based on available data and the current
- * reader position information. This method is called by other
- * private methods to create tokens and is never called directly.
- * @param {int} tt The token type.
- * @param {String} value The text value of the token.
- * @param {int} startLine The beginning line for the character.
- * @param {int} startCol The beginning column for the character.
- * @param {Object} options (Optional) Specifies a channel property
- * to indicate that a different channel should be scanned
- * and/or a hide property indicating that the token should
- * be hidden.
- * @return {Object} A token object.
- * @method createToken
- */
createToken: function(tt, value, startLine, startCol, options){
var reader = this._reader;
options = options || {};
@@ -6784,20 +5028,6 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
endCol: reader.getCol()
};
},
-
- //-------------------------------------------------------------------------
- // Methods to create specific tokens
- //-------------------------------------------------------------------------
-
- /**
- * Produces a token for any at-rule. If the at-rule is unknown, then
- * the token is for a single "@" character.
- * @param {String} first The first character for the token.
- * @param {int} startLine The beginning line for the character.
- * @param {int} startCol The beginning column for the character.
- * @return {Object} A token object.
- * @method atRuleToken
- */
atRuleToken: function(first, startLine, startCol){
var rule = first,
reader = this._reader,
@@ -6805,41 +5035,22 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
valid = false,
ident,
c;
-
- /*
- * First, mark where we are. There are only four @ rules,
- * so anything else is really just an invalid token.
- * Basically, if this doesn't match one of the known @
- * rules, just return '@' as an unknown token and allow
- * parsing to continue after that point.
- */
reader.mark();
-
- //try to find the at-keyword
ident = this.readName();
rule = first + ident;
tt = Tokens.type(rule.toLowerCase());
-
- //if it's not valid, use the first character only and reset the reader
if (tt == Tokens.CHAR || tt == Tokens.UNKNOWN){
- tt = Tokens.CHAR;
- rule = first;
- reader.reset();
+ if (rule.length > 1){
+ tt = Tokens.UNKNOWN_SYM;
+ } else {
+ tt = Tokens.CHAR;
+ rule = first;
+ reader.reset();
+ }
}
return this.createToken(tt, rule, startLine, startCol);
},
-
- /**
- * Produces a character token based on the given character
- * and location in the stream. If there's a special (non-standard)
- * token name, this is used; otherwise CHAR is used.
- * @param {String} c The character for the token.
- * @param {int} startLine The beginning line for the character.
- * @param {int} startCol The beginning column for the character.
- * @return {Object} A token object.
- * @method charToken
- */
charToken: function(c, startLine, startCol){
var tt = Tokens.type(c);
@@ -6849,34 +5060,12 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
return this.createToken(tt, c, startLine, startCol);
},
-
- /**
- * Produces a character token based on the given character
- * and location in the stream. If there's a special (non-standard)
- * token name, this is used; otherwise CHAR is used.
- * @param {String} first The first character for the token.
- * @param {int} startLine The beginning line for the character.
- * @param {int} startCol The beginning column for the character.
- * @return {Object} A token object.
- * @method commentToken
- */
commentToken: function(first, startLine, startCol){
var reader = this._reader,
comment = this.readComment(first);
return this.createToken(Tokens.COMMENT, comment, startLine, startCol);
},
-
- /**
- * Produces a comparison token based on the given character
- * and location in the stream. The next character must be
- * read and is already known to be an equals sign.
- * @param {String} c The character for the token.
- * @param {int} startLine The beginning line for the character.
- * @param {int} startCol The beginning column for the character.
- * @return {Object} A token object.
- * @method comparisonToken
- */
comparisonToken: function(c, startLine, startCol){
var reader = this._reader,
comparison = c + reader.read(),
@@ -6884,34 +5073,12 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
return this.createToken(tt, comparison, startLine, startCol);
},
-
- /**
- * Produces a hash token based on the specified information. The
- * first character provided is the pound sign (#) and then this
- * method reads a name afterward.
- * @param {String} first The first character (#) in the hash name.
- * @param {int} startLine The beginning line for the character.
- * @param {int} startCol The beginning column for the character.
- * @return {Object} A token object.
- * @method hashToken
- */
hashToken: function(first, startLine, startCol){
var reader = this._reader,
name = this.readName(first);
return this.createToken(Tokens.HASH, name, startLine, startCol);
},
-
- /**
- * Produces a CDO or CHAR token based on the specified information. The
- * first character is provided and the rest is read by the function to determine
- * the correct token to create.
- * @param {String} first The first character in the token.
- * @param {int} startLine The beginning line for the character.
- * @param {int} startCol The beginning column for the character.
- * @return {Object} A token object.
- * @method htmlCommentStartToken
- */
htmlCommentStartToken: function(first, startLine, startCol){
var reader = this._reader,
text = first;
@@ -6926,17 +5093,6 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
return this.charToken(first, startLine, startCol);
}
},
-
- /**
- * Produces a CDC or CHAR token based on the specified information. The
- * first character is provided and the rest is read by the function to determine
- * the correct token to create.
- * @param {String} first The first character in the token.
- * @param {int} startLine The beginning line for the character.
- * @param {int} startCol The beginning column for the character.
- * @return {Object} A token object.
- * @method htmlCommentEndToken
- */
htmlCommentEndToken: function(first, startLine, startCol){
var reader = this._reader,
text = first;
@@ -6951,30 +5107,15 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
return this.charToken(first, startLine, startCol);
}
},
-
- /**
- * Produces an IDENT or FUNCTION token based on the specified information. The
- * first character is provided and the rest is read by the function to determine
- * the correct token to create.
- * @param {String} first The first character in the identifier.
- * @param {int} startLine The beginning line for the character.
- * @param {int} startCol The beginning column for the character.
- * @return {Object} A token object.
- * @method identOrFunctionToken
- */
identOrFunctionToken: function(first, startLine, startCol){
var reader = this._reader,
ident = this.readName(first),
tt = Tokens.IDENT;
-
- //if there's a left paren immediately after, it's a URI or function
if (reader.peek() == "("){
ident += reader.read();
if (ident.toLowerCase() == "url("){
tt = Tokens.URI;
ident = this.readURI(ident);
-
- //didn't find a valid URL or there's no closing paren
if (ident.toLowerCase() == "url("){
tt = Tokens.FUNCTION;
}
@@ -6982,8 +5123,6 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
tt = Tokens.FUNCTION;
}
} else if (reader.peek() == ":"){ //might be an IE function
-
- //IE-specific functions always being with progid:
if (ident.toLowerCase() == "progid"){
ident += reader.readTo("(");
tt = Tokens.IE_FUNCTION;
@@ -6992,17 +5131,6 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
return this.createToken(tt, ident, startLine, startCol);
},
-
- /**
- * Produces an IMPORTANT_SYM or CHAR token based on the specified information. The
- * first character is provided and the rest is read by the function to determine
- * the correct token to create.
- * @param {String} first The first character in the token.
- * @param {int} startLine The beginning line for the character.
- * @param {int} startCol The beginning column for the character.
- * @return {Object} A token object.
- * @method importantToken
- */
importantToken: function(first, startLine, startCol){
var reader = this._reader,
important = first,
@@ -7014,16 +5142,12 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
c = reader.read();
while(c){
-
- //there can be a comment in here
if (c == "/"){
-
- //if the next character isn't a star, then this isn't a valid !important token
if (reader.peek() != "*"){
break;
} else {
temp = this.readComment(c);
- if (temp == ""){ //broken!
+ if (temp === ""){ //broken!
break;
}
}
@@ -7053,17 +5177,6 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
},
-
- /**
- * Produces a NOT or CHAR token based on the specified information. The
- * first character is provided and the rest is read by the function to determine
- * the correct token to create.
- * @param {String} first The first character in the token.
- * @param {int} startLine The beginning line for the character.
- * @param {int} startCol The beginning column for the character.
- * @return {Object} A token object.
- * @method notToken
- */
notToken: function(first, startLine, startCol){
var reader = this._reader,
text = first;
@@ -7078,18 +5191,6 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
return this.charToken(first, startLine, startCol);
}
},
-
- /**
- * Produces a number token based on the given character
- * and location in the stream. This may return a token of
- * NUMBER, EMS, EXS, LENGTH, ANGLE, TIME, FREQ, DIMENSION,
- * or PERCENTAGE.
- * @param {String} first The first character for the token.
- * @param {int} startLine The beginning line for the character.
- * @param {int} startCol The beginning column for the character.
- * @return {Object} A token object.
- * @method numberToken
- */
numberToken: function(first, startLine, startCol){
var reader = this._reader,
value = this.readNumber(first),
@@ -7122,20 +5223,6 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
return this.createToken(tt, value, startLine, startCol);
},
-
- /**
- * Produces a string token based on the given character
- * and location in the stream. Since strings may be indicated
- * by single or double quotes, a failure to match starting
- * and ending quotes results in an INVALID token being generated.
- * The first character in the string is passed in and then
- * the rest are read up to and including the final quotation mark.
- * @param {String} first The first character in the string.
- * @param {int} startLine The beginning line for the character.
- * @param {int} startCol The beginning column for the character.
- * @return {Object} A token object.
- * @method stringToken
- */
stringToken: function(first, startLine, startCol){
var delim = first,
string = first,
@@ -7146,25 +5233,17 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
while(c){
string += c;
-
- //if the delimiter is found with an escapement, we're done.
if (c == delim && prev != "\\"){
break;
}
-
- //if there's a newline without an escapement, it's an invalid string
if (isNewLine(reader.peek()) && c != "\\"){
tt = Tokens.INVALID;
break;
}
-
- //save previous and get next
prev = c;
c = reader.read();
}
-
- //if c is null, that means we're out of input and the string was never closed
- if (c == null){
+ if (c === null){
tt = Tokens.INVALID;
}
@@ -7176,29 +5255,21 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
value = first,
temp,
tt = Tokens.CHAR;
-
- //then it should be a unicode range
if (reader.peek() == "+"){
reader.mark();
value += reader.read();
value += this.readUnicodeRangePart(true);
-
- //ensure there's an actual unicode range here
if (value.length == 2){
reader.reset();
} else {
tt = Tokens.UNICODE_RANGE;
-
- //if there's a ? in the first part, there can't be a second part
if (value.indexOf("?") == -1){
if (reader.peek() == "-"){
reader.mark();
temp = reader.read();
temp += this.readUnicodeRangePart(false);
-
- //if there's not another value, back up and just take the first
if (temp.length == 1){
reader.reset();
} else {
@@ -7212,43 +5283,21 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
return this.createToken(tt, value, startLine, startCol);
},
-
- /**
- * Produces a S token based on the specified information. Since whitespace
- * may have multiple characters, this consumes all whitespace characters
- * into a single token.
- * @param {String} first The first character in the token.
- * @param {int} startLine The beginning line for the character.
- * @param {int} startCol The beginning column for the character.
- * @return {Object} A token object.
- * @method whitespaceToken
- */
whitespaceToken: function(first, startLine, startCol){
var reader = this._reader,
value = first + this.readWhitespace();
return this.createToken(Tokens.S, value, startLine, startCol);
},
-
-
-
- //-------------------------------------------------------------------------
- // Methods to read values from the string stream
- //-------------------------------------------------------------------------
-
readUnicodeRangePart: function(allowQuestionMark){
var reader = this._reader,
part = "",
c = reader.peek();
-
- //first read hex digits
while(isHexDigit(c) && part.length < 6){
reader.read();
part += c;
c = reader.peek();
}
-
- //then read question marks if allowed
if (allowQuestionMark){
while(c == "?" && part.length < 6){
reader.read();
@@ -7257,8 +5306,6 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
}
}
- //there can't be any other characters after this point
-
return part;
},
@@ -7311,25 +5358,17 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
while(c){
c = reader.read();
string += c;
-
- //if the delimiter is found with an escapement, we're done.
if (c == delim && prev != "\\"){
break;
}
-
- //if there's a newline without an escapement, it's an invalid string
if (isNewLine(reader.peek()) && c != "\\"){
string = "";
break;
}
-
- //save previous and get next
prev = c;
c = reader.peek();
}
-
- //if c is null, that means we're out of input and the string was never closed
- if (c == null){
+ if (c === null){
string = "";
}
@@ -7342,14 +5381,10 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
c = reader.peek();
reader.mark();
-
- //skip whitespace before
while(c && isWhitespace(c)){
reader.read();
c = reader.peek();
}
-
- //it's a string
if (c == "'" || c == "\""){
inner = this.readString();
} else {
@@ -7357,15 +5392,11 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
}
c = reader.peek();
-
- //skip whitespace after
while(c && isWhitespace(c)){
reader.read();
c = reader.peek();
}
-
- //if there was no inner value or the next character isn't closing paren, it's not a URI
- if (inner == "" || c != ")"){
+ if (inner === "" || c != ")"){
uri = first;
reader.reset();
} else {
@@ -7378,8 +5409,6 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
var reader = this._reader,
url = "",
c = reader.peek();
-
- //TODO: Check for escape and nonascii
while (/^[!#$%&\\*-~]$/.test(c)){
url += reader.read();
c = reader.peek();
@@ -7439,9 +5468,7 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
if (c == "*"){
while(c){
comment += c;
-
- //look for end of comment
- if (c == "*" && reader.peek() == "/"){
+ if (comment.length > 2 && c == "*" && reader.peek() == "/"){
comment += reader.read();
break;
}
@@ -7459,47 +5486,27 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
var Tokens = [
-
- /*
- * The following token names are defined in CSS3 Grammar: http://www.w3.org/TR/css3-syntax/#lexical
- */
-
- //HTML-style comments
{ name: "CDO"},
{ name: "CDC"},
-
- //ignorables
{ name: "S", whitespace: true/*, channel: "ws"*/},
{ name: "COMMENT", comment: true, hide: true, channel: "comment" },
-
- //attribute equality
{ name: "INCLUDES", text: "~="},
{ name: "DASHMATCH", text: "|="},
{ name: "PREFIXMATCH", text: "^="},
{ name: "SUFFIXMATCH", text: "$="},
{ name: "SUBSTRINGMATCH", text: "*="},
-
- //identifier types
{ name: "STRING"},
{ name: "IDENT"},
{ name: "HASH"},
-
- //at-keywords
{ 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: "ATKEYWORD"},
-
- //CSS3 animations
- { name: "KEYFRAMES_SYM", text: [ "@keyframes", "@-webkit-keyframes", "@-moz-keyframes" ] },
-
- //important symbol
+ { name: "UNKNOWN_SYM" },
+ { name: "KEYFRAMES_SYM", text: [ "@keyframes", "@-webkit-keyframes", "@-moz-keyframes", "@-o-keyframes" ] },
{ name: "IMPORTANT_SYM"},
-
- //measurements
{ name: "LENGTH"},
{ name: "ANGLE"},
{ name: "TIME"},
@@ -7507,33 +5514,15 @@ var Tokens = [
{ name: "DIMENSION"},
{ name: "PERCENTAGE"},
{ name: "NUMBER"},
-
- //functions
{ name: "URI"},
{ name: "FUNCTION"},
-
- //Unicode ranges
- { name: "UNICODE_RANGE"},
-
- /*
- * The following token names are defined in CSS3 Selectors: http://www.w3.org/TR/css3-selectors/#selector-syntax
- */
-
- //invalid string
+ { name: "UNICODE_RANGE"},
{ name: "INVALID"},
-
- //combinators
{ name: "PLUS", text: "+" },
{ name: "GREATER", text: ">"},
{ name: "COMMA", text: ","},
{ name: "TILDE", text: "~"},
-
- //modifier
{ name: "NOT"},
-
- /*
- * Defined in CSS3 Paged Media
- */
{ name: "TOPLEFTCORNER_SYM", text: "@top-left-corner"},
{ name: "TOPLEFT_SYM", text: "@top-left"},
{ name: "TOPCENTER_SYM", text: "@top-center"},
@@ -7550,27 +5539,9 @@ var Tokens = [
{ name: "RIGHTTOP_SYM", text: "@right-top"},
{ name: "RIGHTMIDDLE_SYM", text: "@right-middle"},
{ name: "RIGHTBOTTOM_SYM", text: "@right-bottom"},
-
- /*
- * The following token names are defined in CSS3 Media Queries: http://www.w3.org/TR/css3-mediaqueries/#syntax
- */
- /*{ name: "MEDIA_ONLY", state: "media"},
- { name: "MEDIA_NOT", state: "media"},
- { name: "MEDIA_AND", state: "media"},*/
{ name: "RESOLUTION", state: "media"},
-
- /*
- * The following token names are not defined in any CSS specification but are used by the lexer.
- */
-
- //not a real token, but useful for stupid IE filters
{ name: "IE_FUNCTION" },
-
- //part of CSS3 grammar but not the Flex code
{ name: "CHAR" },
-
- //TODO: Needed?
- //Not defined as tokens, but might as well be
{
name: "PIPE",
text: "|"
@@ -7661,9 +5632,472 @@ var Tokens = [
};
})();
+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){
+ 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 | center | right",
+ yDir = "top | center | bottom",
+ part,
+ i, len;
+
+ if (ValidationTypes.isAny(expression, "top | bottom")) {
+ result = true;
+ } else {
+ if (ValidationTypes.isAny(expression, numeric)){
+ if (expression.hasNext()){
+ result = ValidationTypes.isAny(expression, numeric + " | " + yDir);
+ }
+ } else if (ValidationTypes.isAny(expression, xDir)){
+ if (expression.hasNext()){
+ if (ValidationTypes.isAny(expression, yDir)){
+ result = true;
+
+ ValidationTypes.isAny(expression, numeric);
+
+ } else if (ValidationTypes.isAny(expression, numeric)){
+ if (ValidationTypes.isAny(expression, yDir)){
+ ValidationTypes.isAny(expression, numeric);
+ }
+
+ result = true;
+ }
+ }
+ }
+ }
+
+ 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,
+ count = 0,
+ numeric = "<length> | <percentage>",
+ part;
+
+ if (ValidationTypes.isAny(expression, numeric)){
+ result = true;
+
+ ValidationTypes.isAny(expression, numeric);
+ }
+
+ return result;
+ }
+ }
+};
parserlib.css = {
@@ -7678,206 +6112,123 @@ MediaQuery :MediaQuery,
Selector :Selector,
SelectorPart :SelectorPart,
SelectorSubPart :SelectorSubPart,
+Specificity :Specificity,
TokenStream :TokenStream,
-Tokens :Tokens
+Tokens :Tokens,
+ValidationError :ValidationError
};
})();
-
-/**
- * Main CSSLint object.
- * @class CSSLint
- * @static
- * @extends parserlib.util.EventTarget
- */
var CSSLint = (function(){
var rules = [],
formatters = [],
api = new parserlib.util.EventTarget();
- api.version = "@VERSION@";
-
- //-------------------------------------------------------------------------
- // Rule Management
- //-------------------------------------------------------------------------
-
- /**
- * Adds a new rule to the engine.
- * @param {Object} rule The rule to add.
- * @method addRule
- */
+ api.version = "0.9.9";
api.addRule = function(rule){
rules.push(rule);
rules[rule.id] = rule;
};
-
- /**
- * Clears all rule from the engine.
- * @method clearRules
- */
api.clearRules = function(){
rules = [];
};
-
- //-------------------------------------------------------------------------
- // Formatters
- //-------------------------------------------------------------------------
-
- /**
- * Adds a new formatter to the engine.
- * @param {Object} formatter The formatter to add.
- * @method addFormatter
- */
+ 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;
+ };
api.addFormatter = function(formatter) {
- // formatters.push(formatter);
formatters[formatter.id] = formatter;
};
-
- /**
- * Retrieves a formatter for use.
- * @param {String} formatId The name of the format to retrieve.
- * @return {Object} The formatter or undefined.
- * @method getFormatter
- */
api.getFormatter = function(formatId){
return formatters[formatId];
};
-
- /**
- * Formats the results in a particular format for a single file.
- * @param {Object} result The results returned from CSSLint.verify().
- * @param {String} filename The filename for which the results apply.
- * @param {String} formatId The name of the formatter to use.
- * @return {String} A formatted string for the results.
- * @method format
- */
- api.format = function(results, filename, 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);
+ result += formatter.formatResults(results, filename, options || {});
result += formatter.endFormat();
}
return result;
- }
-
- /**
- * Indicates if the given format is supported.
- * @param {String} formatId The ID of the format to check.
- * @return {Boolean} True if the format exists, false if not.
- * @method hasFormat
- */
+ };
api.hasFormat = function(formatId){
return formatters.hasOwnProperty(formatId);
};
-
- //-------------------------------------------------------------------------
- // Verification
- //-------------------------------------------------------------------------
-
- /**
- * Starts the verification process for the given CSS text.
- * @param {String} text The CSS text to verify.
- * @param {Object} ruleset (Optional) List of rules to apply. If null, then
- * all rules are used.
- * @return {Object} Results of the verification.
- * @method verify
- */
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.split(/\n\r?/g);
- reporter = new Reporter(lines);
-
+ lines = text.replace(/\n\r?/g, "$split$").split('$split$');
+
if (!ruleset){
- while (i < len){
- rules[i++].init(parser, reporter);
- }
- } else {
- ruleset.errors = 1; //always report parsing errors
- for (i in ruleset){
- if(ruleset.hasOwnProperty(i)){
- if (rules[i]){
- rules[i].init(parser, reporter);
- }
+ ruleset = this.getRuleset();
+ }
+
+ reporter = new Reporter(lines, ruleset);
+
+ ruleset.errors = 2; //always report parsing errors as errors
+ for (i in ruleset){
+ if(ruleset.hasOwnProperty(i)){
+ if (rules[i]){
+ rules[i].init(parser, reporter);
}
}
}
-
- //capture most horrible error type
try {
parser.parse(text);
} catch (ex) {
- reporter.error("Fatal error, cannot continue: " + ex.message, ex.line, ex.col);
+ reporter.error("Fatal error, cannot continue: " + ex.message, ex.line, ex.col, {});
}
- return {
+ report = {
messages : reporter.messages,
stats : reporter.stats
};
+ 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;
};
- //-------------------------------------------------------------------------
- // Publish the API
- //-------------------------------------------------------------------------
-
return api;
})();
-/**
- * An instance of Report is used to report results of the
- * verification back to the main API.
- * @class Reporter
- * @constructor
- * @param {String[]} lines The text lines of the source.
- */
-function Reporter(lines){
-
- /**
- * List of messages being reported.
- * @property messages
- * @type String[]
- */
+function Reporter(lines, ruleset){
this.messages = [];
-
- /**
- * List of statistics being reported.
- * @property stats
- * @type String[]
- */
this.stats = [];
-
- /**
- * Lines of code being reported on. Used to provide contextual information
- * for messages.
- * @property lines
- * @type String[]
- */
this.lines = lines;
+ this.ruleset = ruleset;
}
Reporter.prototype = {
-
- //restore constructor
constructor: Reporter,
-
- /**
- * Report an error.
- * @param {String} message The message to store.
- * @param {int} line The line number.
- * @param {int} col The column number.
- * @param {Object} rule The rule this message relates to.
- * @method error
- */
error: function(message, line, col, rule){
this.messages.push({
type : "error",
@@ -7885,21 +6236,15 @@ Reporter.prototype = {
col : col,
message : message,
evidence: this.lines[line-1],
- rule : rule
+ rule : rule || {}
});
},
-
- /**
- * Report an warning.
- * @param {String} message The message to store.
- * @param {int} line The line number.
- * @param {int} col The column number.
- * @param {Object} rule The rule this message relates to.
- * @method warn
- */
warn: function(message, line, col, rule){
+ this.report(message, line, col, rule);
+ },
+ report: function(message, line, col, rule){
this.messages.push({
- type : "warning",
+ type : this.ruleset[rule.id] == 2 ? "error" : "warning",
line : line,
col : col,
message : message,
@@ -7907,15 +6252,6 @@ Reporter.prototype = {
rule : rule
});
},
-
- /**
- * Report some informational text.
- * @param {String} message The message to store.
- * @param {int} line The line number.
- * @param {int} col The column number.
- * @param {Object} rule The rule this message relates to.
- * @method info
- */
info: function(message, line, col, rule){
this.messages.push({
type : "info",
@@ -7926,13 +6262,6 @@ Reporter.prototype = {
rule : rule
});
},
-
- /**
- * Report some rollup error information.
- * @param {String} message The message to store.
- * @param {Object} rule The rule this message relates to.
- * @method rollupError
- */
rollupError: function(message, rule){
this.messages.push({
type : "error",
@@ -7941,13 +6270,6 @@ Reporter.prototype = {
rule : rule
});
},
-
- /**
- * Report some rollup warning information.
- * @param {String} message The message to store.
- * @param {Object} rule The rule this message relates to.
- * @method rollupWarn
- */
rollupWarn: function(message, rule){
this.messages.push({
type : "warning",
@@ -7956,71 +6278,50 @@ Reporter.prototype = {
rule : rule
});
},
-
- /**
- * Report a statistic.
- * @param {String} name The name of the stat to store.
- * @param {Variant} value The value of the stat.
- * @method stat
- */
stat: function(name, value){
this.stats[name] = value;
}
};
-/*
- * Utility functions that make life easier.
- */
-
-/*
- * Adds all properties from supplier onto receiver,
- * overwriting if the same name already exists on
- * reciever.
- * @param {Object} The object to receive the properties.
- * @param {Object} The object to provide the properties.
- * @return {Object} The receiver
- */
-function mix(reciever, supplier){
- var prop;
-
- for (prop in supplier){
- if (supplier.hasOwnProperty(prop)){
- receiver[prop] = supplier[prop];
+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;
-}
-
-/*
- * Polyfill for array indexOf() method.
- * @param {Array} values The array to search.
- * @param {Variant} value The value to search for.
- * @return {int} The index of the value if found, -1 if not.
- */
-function indexOf(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 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);
}
}
- return -1;
}
-}
-/*
- * Rule: Don't use adjoining classes (.foo.bar).
- */
+};
CSSLint.addRule({
-
- //rule information
id: "adjoining-classes",
- name: "Adjoining Classes",
+ name: "Disallow adjoining classes",
desc: "Don't use adjoining classes.",
browsers: "IE6",
-
- //initialization
init: function(parser, reporter){
var rule = this;
parser.addListener("startrule", function(event){
@@ -8035,7 +6336,7 @@ CSSLint.addRule({
selector = selectors[i];
for (j=0; j < selector.parts.length; j++){
part = selector.parts[j];
- if (part instanceof parserlib.css.SelectorPart){
+ if (part.type == parser.SELECTOR_PART_TYPE){
classCount = 0;
for (k=0; k < part.modifiers.length; k++){
modifier = part.modifiers[k];
@@ -8043,7 +6344,7 @@ CSSLint.addRule({
classCount++;
}
if (classCount > 1){
- reporter.warn("Don't use adjoining classes.", part.line, part.col, rule);
+ reporter.report("Don't use adjoining classes.", part.line, part.col, rule);
}
}
}
@@ -8053,18 +6354,11 @@ CSSLint.addRule({
}
});
-/*
- * Rule: Don't use width or height when using padding or border.
- */
CSSLint.addRule({
-
- //rule information
id: "box-model",
- name: "Box Model",
+ name: "Beware of broken box size",
desc: "Don't use width or height when using padding or border.",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this,
widthProperties = {
@@ -8083,12 +6377,48 @@ CSSLint.addRule({
"padding-bottom": 1,
"padding-top": 1
},
- properties;
+ properties,
+ boxSizing = false;
- parser.addListener("startrule", function(){
- properties = {
- };
- });
+ 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();
@@ -8098,60 +6428,47 @@ CSSLint.addRule({
properties[name] = { line: event.property.line, col: event.property.col, value: event.value };
}
} else {
- if (name == "width" || name == "height"){
+ 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", function(){
- var prop;
- if (properties["height"]){
- for (prop in heightProperties){
- if (heightProperties.hasOwnProperty(prop) && properties[prop]){
-
- //special case for padding
- if (prop == "padding" && properties[prop].value.parts.length == 2 && properties[prop].value.parts[0].value == 0){
- //noop
- } else {
- reporter.warn("Broken box model: using height with " + prop + ".", properties[prop].line, properties[prop].col, rule);
- }
- }
- }
- }
+ parser.addListener("endrule", endRule);
+ parser.addListener("endfontface", endRule);
+ parser.addListener("endpage", endRule);
+ parser.addListener("endpagemargin", endRule);
+ parser.addListener("endkeyframerule", endRule);
+ }
- if (properties["width"]){
- for (prop in widthProperties){
- if (widthProperties.hasOwnProperty(prop) && properties[prop]){
+});
+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;
- if (prop == "padding" && properties[prop].value.parts.length == 2 && properties[prop].value.parts[1].value == 0){
- //noop
- } else {
- reporter.warn("Broken box model: using width with " + prop + ".", properties[prop].line, properties[prop].col, rule);
- }
- }
- }
+ 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);
}
-
- });
+ });
}
-});
-/*
- * Rule: Include all compatible vendor prefixes to reach a wider
- * range of users.
- */
-/*global CSSLint*/
+});
CSSLint.addRule({
-
- //rule information
id: "compatible-vendor-prefixes",
- name: "Compatible Vendor Prefixes",
+ name: "Require compatible vendor prefixes",
desc: "Include all compatible vendor prefixes to reach a wider range of users.",
browsers: "All",
-
- //initialization
init: function (parser, reporter) {
var rule = this,
compatiblePrefixes,
@@ -8161,10 +6478,9 @@ CSSLint.addRule({
prefixed,
i,
len,
+ inKeyFrame = false,
arrayPush = Array.prototype.push,
applyTo = [];
-
- // See http://peter.sh/experiments/vendor-prefixed-css-property-overview/ for details
compatiblePrefixes = {
"animation" : "webkit moz",
"animation-delay" : "webkit moz",
@@ -8195,13 +6511,13 @@ CSSLint.addRule({
"box-pack" : "webkit moz ms",
"box-sizing" : "webkit moz",
"box-shadow" : "webkit moz",
- "column-count" : "webkit moz",
- "column-gap" : "webkit moz",
- "column-rule" : "webkit moz",
- "column-rule-color" : "webkit moz",
- "column-rule-style" : "webkit moz",
- "column-rule-width" : "webkit moz",
- "column-width" : "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",
@@ -8220,11 +6536,12 @@ CSSLint.addRule({
"transition-property" : "webkit moz o",
"transition-timing-function" : "webkit moz o",
"user-modify" : "webkit moz",
- "user-select" : "webkit moz",
+ "user-select" : "webkit moz ms",
"word-break" : "epub ms",
"writing-mode" : "epub ms"
};
+
for (prop in compatiblePrefixes) {
if (compatiblePrefixes.hasOwnProperty(prop)) {
variations = [];
@@ -8236,14 +6553,26 @@ CSSLint.addRule({
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.text;
- if (applyTo.indexOf(name) > -1) {
- properties.push(name);
+ 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);
+ }
}
});
@@ -8270,15 +6599,17 @@ CSSLint.addRule({
for (prop in compatiblePrefixes) {
if (compatiblePrefixes.hasOwnProperty(prop)) {
variations = compatiblePrefixes[prop];
- if (variations.indexOf(name) > -1) {
- if (propertyGroups[prop] === undefined) {
+ if (CSSLint.Util.indexOf(variations, name.text) > -1) {
+ if (!propertyGroups[prop]) {
propertyGroups[prop] = {
full : variations.slice(0),
- actual : []
+ actual : [],
+ actualNodes: []
};
}
- if (propertyGroups[prop].actual.indexOf(name) === -1) {
- propertyGroups[prop].actual.push(name);
+ if (CSSLint.Util.indexOf(propertyGroups[prop].actual, name.text) === -1) {
+ propertyGroups[prop].actual.push(name.text);
+ propertyGroups[prop].actualNodes.push(name);
}
}
}
@@ -8294,9 +6625,9 @@ CSSLint.addRule({
if (full.length > actual.length) {
for (i = 0, len = full.length; i < len; i++) {
item = full[i];
- if (actual.indexOf(item) === -1) {
+ if (CSSLint.Util.indexOf(actual, item) === -1) {
propertiesSpecified = (actual.length === 1) ? actual[0] : (actual.length == 2) ? actual.join(" and ") : actual.join(", ");
- reporter.warn("The property " + item + " is compatible with " + propertiesSpecified + " and should be included as well.", event.selectors[0].line, event.selectors[0].col, rule);
+ reporter.report("The property " + item + " is compatible with " + propertiesSpecified + " and should be included as well.", value.actualNodes[0].line, value.actualNodes[0].col, rule);
}
}
@@ -8306,22 +6637,11 @@ CSSLint.addRule({
});
}
});
-/*
- * Rule: Certain properties don't play well with certain display values.
- * - float should not be used with inline-block
- * - height, width, margin-top, margin-bottom, float should not be used with inline
- * - vertical-align should not be used with block
- * - margin, float should not be used with table-*
- */
CSSLint.addRule({
-
- //rule information
id: "display-property-grouping",
- name: "Display Property Grouping",
+ name: "Require properties appropriate for display",
desc: "Certain properties shouldn't be used with certain display property values.",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this;
@@ -8344,26 +6664,25 @@ CSSLint.addRule({
},
properties;
- parser.addListener("startrule", function(){
- properties = {};
- });
-
- 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 };
+ 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 = {};
+ }
- parser.addListener("endrule", function(){
+ function endRule(){
var display = properties.display ? properties.display.value : null;
if (display){
switch(display){
case "inline":
- //height, width, margin-top, margin-bottom, float should not be used with inline
reportProperty("height", display);
reportProperty("width", display);
reportProperty("margin", display);
@@ -8373,18 +6692,15 @@ CSSLint.addRule({
break;
case "block":
- //vertical-align should not be used with block
reportProperty("vertical-align", display);
break;
case "inline-block":
- //float should not be used with inline-block
reportProperty("float", display);
break;
default:
- //margin, float should not be used with table
- if (display.indexOf("table-") == 0){
+ if (display.indexOf("table-") === 0){
reportProperty("margin", display);
reportProperty("margin-left", display);
reportProperty("margin-right", display);
@@ -8392,37 +6708,68 @@ CSSLint.addRule({
reportProperty("margin-bottom", display);
reportProperty("float", display);
}
-
- //otherwise do nothing
}
}
- });
+ }
+
+ 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();
- function reportProperty(name, display, msg){
- if (properties[name]){
- if (!(typeof propertiesToCheck[name] == "string") || properties[name].value.toLowerCase() != propertiesToCheck[name]){
- reporter.warn(msg || name + " can't be used with display: " + display + ".", properties[name].line, properties[name].col, rule);
- }
+ 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);
+
}
});
-/*
- * Rule: Duplicate properties must appear one after the other. If an already-defined
- * property appears somewhere else in the rule, then it's likely an error.
- */
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 = {};
- //rule information
+ 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: "Duplicate Properties",
+ name: "Disallow duplicate properties",
desc: "Duplicate properties must appear one after the other.",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this,
properties,
@@ -8435,13 +6782,15 @@ CSSLint.addRule({
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.warn("Duplicate property '" + event.property + "' found.", event.line, event.col, rule);
+ reporter.report("Duplicate property '" + event.property + "' found.", event.line, event.col, rule);
}
properties[name] = event.value.text;
@@ -8453,18 +6802,11 @@ CSSLint.addRule({
}
});
-/*
- * Rule: Style rules without any properties defined should be removed.
- */
CSSLint.addRule({
-
- //rule information
id: "empty-rules",
- name: "Empty Rules",
+ name: "Disallow empty rules",
desc: "Rules without any properties specified should be removed.",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this,
count = 0;
@@ -8479,25 +6821,18 @@ CSSLint.addRule({
parser.addListener("endrule", function(event){
var selectors = event.selectors;
- if (count == 0){
- reporter.warn("Rule is empty.", selectors[0].line, selectors[0].col, rule);
+ if (count === 0){
+ reporter.report("Rule is empty.", selectors[0].line, selectors[0].col, rule);
}
});
}
});
-/*
- * Rule: There should be no syntax errors. (Duh.)
- */
CSSLint.addRule({
-
- //rule information
id: "errors",
name: "Parsing Errors",
desc: "This rule looks for recoverable syntax errors.",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this;
@@ -8508,32 +6843,81 @@ CSSLint.addRule({
}
});
-/*
- * Rule: You shouldn't use more than 10 floats. If you do, there's probably
- * room for some abstraction.
- */
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,
+ "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;
+ });
+
+ }
- //rule information
+});
+CSSLint.addRule({
id: "floats",
- name: "Floats",
+ name: "Disallow too many floats",
desc: "This rule tests if the float property is used too many times",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this;
var count = 0;
-
- //count how many times "float" is used
parser.addListener("property", function(event){
if (event.property.text.toLowerCase() == "float" &&
event.value.text.toLowerCase() != "none"){
count++;
}
});
-
- //report the results
parser.addListener("endstylesheet", function(){
reporter.stat("floats", count);
if (count >= 10){
@@ -8543,18 +6927,11 @@ CSSLint.addRule({
}
});
-/*
- * Rule: Avoid too many @font-face declarations in the same stylesheet.
- */
CSSLint.addRule({
-
- //rule information
id: "font-faces",
- name: "Font Faces",
+ name: "Don't use too many web fonts",
desc: "Too many different web fonts in the same stylesheet.",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this,
count = 0;
@@ -8572,31 +6949,19 @@ CSSLint.addRule({
}
});
-/*
- * Rule: You shouldn't need more than 9 font-size declarations.
- */
-
CSSLint.addRule({
-
- //rule information
id: "font-sizes",
- name: "Font Sizes",
+ name: "Disallow too many font sizes",
desc: "Checks the number of font-size declarations.",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this,
count = 0;
-
- //check for use of "font-size"
parser.addListener("property", function(event){
if (event.property == "font-size"){
count++;
}
});
-
- //report the results
parser.addListener("endstylesheet", function(){
reporter.stat("font-sizes", count);
if (count >= 10){
@@ -8606,18 +6971,11 @@ CSSLint.addRule({
}
});
-/*
- * Rule: When using a vendor-prefixed gradient, make sure to use them all.
- */
CSSLint.addRule({
-
- //rule information
id: "gradients",
- name: "Gradients",
+ name: "Require all gradient definitions",
desc: "When using a vendor-prefixed gradient, make sure to use them all.",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this,
gradients;
@@ -8626,6 +6984,7 @@ CSSLint.addRule({
gradients = {
moz: 0,
webkit: 0,
+ oldWebkit: 0,
ms: 0,
o: 0
};
@@ -8633,8 +6992,10 @@ CSSLint.addRule({
parser.addListener("property", function(event){
- if (/\-(moz|ms|o|webkit)(?:\-(?:linear|radial))\-gradient/.test(event.value)){
+ if (/\-(moz|ms|o|webkit)(?:\-(?:linear|radial))\-gradient/i.test(event.value)){
gradients[RegExp.$1] = 1;
+ } else if (/\-webkit\-gradient/i.test(event.value)){
+ gradients.oldWebkit = 1;
}
});
@@ -8647,7 +7008,11 @@ CSSLint.addRule({
}
if (!gradients.webkit){
- missing.push("Webkit (Safari, Chrome)");
+ missing.push("Webkit (Safari 5+, Chrome)");
+ }
+
+ if (!gradients.oldWebkit){
+ missing.push("Old Webkit (Safari 4+, Chrome)");
}
if (!gradients.ms){
@@ -8658,8 +7023,8 @@ CSSLint.addRule({
missing.push("Opera 11.1+");
}
- if (missing.length && missing.length < 4){
- reporter.warn("Missing vendor-prefixed CSS gradients for " + missing.join(", ") + ".", event.selectors[0].line, event.selectors[0].col, rule);
+ if (missing.length && missing.length < 5){
+ reporter.report("Missing vendor-prefixed CSS gradients for " + missing.join(", ") + ".", event.selectors[0].line, event.selectors[0].col, rule);
}
});
@@ -8667,18 +7032,11 @@ CSSLint.addRule({
}
});
-/*
- * Rule: Don't use IDs for selectors.
- */
CSSLint.addRule({
-
- //rule information
id: "ids",
- name: "IDs",
+ name: "Disallow IDs in selectors",
desc: "Selectors should not contain IDs.",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this;
parser.addListener("startrule", function(event){
@@ -8695,7 +7053,7 @@ CSSLint.addRule({
for (j=0; j < selector.parts.length; j++){
part = selector.parts[j];
- if (part instanceof parserlib.css.SelectorPart){
+ if (part.type == parser.SELECTOR_PART_TYPE){
for (k=0; k < part.modifiers.length; k++){
modifier = part.modifiers[k];
if (modifier.type == "id"){
@@ -8706,9 +7064,9 @@ CSSLint.addRule({
}
if (idCount == 1){
- reporter.warn("Don't use IDs in selectors.", selector.line, selector.col, rule);
+ reporter.report("Don't use IDs in selectors.", selector.line, selector.col, rule);
} else if (idCount > 1){
- reporter.warn(idCount + " IDs in the selector, really?", selector.line, selector.col, rule);
+ reporter.report(idCount + " IDs in the selector, really?", selector.line, selector.col, rule);
}
}
@@ -8716,383 +7074,131 @@ CSSLint.addRule({
}
});
-/*
- * Rule: Don't use @import, use <link> instead.
- */
CSSLint.addRule({
-
- //rule information
id: "import",
- name: "@import",
+ name: "Disallow @import",
desc: "Don't use @import, use <link> instead.",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this;
parser.addListener("import", function(event){
- reporter.warn("@import prevents parallel downloads, use <link> instead.", event.line, event.col, rule);
+ reporter.report("@import prevents parallel downloads, use <link> instead.", event.line, event.col, rule);
});
}
});
-/*
- * Rule: Make sure !important is not overused, this could lead to specificity
- * war. Display a warning on !important declarations, an error if it's
- * used more at least 10 times.
- */
CSSLint.addRule({
-
- //rule information
id: "important",
- name: "Important",
+ name: "Disallow !important",
desc: "Be careful when using !important declaration",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this,
count = 0;
-
- //warn that important is used and increment the declaration counter
parser.addListener("property", function(event){
if (event.important === true){
count++;
- reporter.warn("Use of !important", event.line, event.col, rule);
+ reporter.report("Use of !important", event.line, event.col, rule);
}
});
-
- //if there are more than 10, show an error
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 specifity issues.", rule);
+ reporter.rollupWarn("Too many !important declarations (" + count + "), try to use less than 10 to avoid specificity issues.", rule);
}
});
}
});
-/*
- * Rule: Properties should be known (listed in CSS3 specification) or
- * be a vendor-prefixed property.
- */
CSSLint.addRule({
-
- //rule information
id: "known-properties",
- name: "Known Properties",
- desc: "Properties should be known (listed in CSS specification) or be a vendor-prefixed property.",
+ name: "Require use of known properties",
+ desc: "Properties should be known (listed in CSS3 specification) or be a vendor-prefixed property.",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
- var rule = this,
- properties = {
-
- "alignment-adjust": 1,
- "alignment-baseline": 1,
- "animation": 1,
- "animation-delay": 1,
- "animation-direction": 1,
- "animation-duration": 1,
- "animation-iteration-count": 1,
- "animation-name": 1,
- "animation-play-state": 1,
- "animation-timing-function": 1,
- "appearance": 1,
- "azimuth": 1,
- "backface-visibility": 1,
- "background": 1,
- "background-attachment": 1,
- "background-break": 1,
- "background-clip": 1,
- "background-color": 1,
- "background-image": 1,
- "background-origin": 1,
- "background-position": 1,
- "background-repeat": 1,
- "background-size": 1,
- "baseline-shift": 1,
- "binding": 1,
- "bleed": 1,
- "bookmark-label": 1,
- "bookmark-level": 1,
- "bookmark-state": 1,
- "bookmark-target": 1,
- "border": 1,
- "border-bottom": 1,
- "border-bottom-color": 1,
- "border-bottom-left-radius": 1,
- "border-bottom-right-radius": 1,
- "border-bottom-style": 1,
- "border-bottom-width": 1,
- "border-collapse": 1,
- "border-color": 1,
- "border-image": 1,
- "border-image-outset": 1,
- "border-image-repeat": 1,
- "border-image-slice": 1,
- "border-image-source": 1,
- "border-image-width": 1,
- "border-left": 1,
- "border-left-color": 1,
- "border-left-style": 1,
- "border-left-width": 1,
- "border-radius": 1,
- "border-right": 1,
- "border-right-color": 1,
- "border-right-style": 1,
- "border-right-width": 1,
- "border-spacing": 1,
- "border-style": 1,
- "border-top": 1,
- "border-top-color": 1,
- "border-top-left-radius": 1,
- "border-top-right-radius": 1,
- "border-top-style": 1,
- "border-top-width": 1,
- "border-width": 1,
- "bottom": 1,
- "box-align": 1,
- "box-decoration-break": 1,
- "box-direction": 1,
- "box-flex": 1,
- "box-flex-group": 1,
- "box-lines": 1,
- "box-ordinal-group": 1,
- "box-orient": 1,
- "box-pack": 1,
- "box-shadow": 1,
- "box-sizing": 1,
- "break-after": 1,
- "break-before": 1,
- "break-inside": 1,
- "caption-side": 1,
- "clear": 1,
- "clip": 1,
- "color": 1,
- "color-profile": 1,
- "column-count": 1,
- "column-fill": 1,
- "column-gap": 1,
- "column-rule": 1,
- "column-rule-color": 1,
- "column-rule-style": 1,
- "column-rule-width": 1,
- "column-span": 1,
- "column-width": 1,
- "columns": 1,
- "content": 1,
- "counter-increment": 1,
- "counter-reset": 1,
- "crop": 1,
- "cue": 1,
- "cue-after": 1,
- "cue-before": 1,
- "cursor": 1,
- "direction": 1,
- "display": 1,
- "dominant-baseline": 1,
- "drop-initial-after-adjust": 1,
- "drop-initial-after-align": 1,
- "drop-initial-before-adjust": 1,
- "drop-initial-before-align": 1,
- "drop-initial-size": 1,
- "drop-initial-value": 1,
- "elevation": 1,
- "empty-cells": 1,
- "fit": 1,
- "fit-position": 1,
- "float": 1,
- "float-offset": 1,
- "font": 1,
- "font-family": 1,
- "font-size": 1,
- "font-size-adjust": 1,
- "font-stretch": 1,
- "font-style": 1,
- "font-variant": 1,
- "font-weight": 1,
- "grid-columns": 1,
- "grid-rows": 1,
- "hanging-punctuation": 1,
- "height": 1,
- "hyphenate-after": 1,
- "hyphenate-before": 1,
- "hyphenate-character": 1,
- "hyphenate-lines": 1,
- "hyphenate-resource": 1,
- "hyphens": 1,
- "icon": 1,
- "image-orientation": 1,
- "image-rendering": 1,
- "image-resolution": 1,
- "inline-box-align": 1,
- "left": 1,
- "letter-spacing": 1,
- "line-height": 1,
- "line-stacking": 1,
- "line-stacking-ruby": 1,
- "line-stacking-shift": 1,
- "line-stacking-strategy": 1,
- "list-style": 1,
- "list-style-image": 1,
- "list-style-position": 1,
- "list-style-type": 1,
- "margin": 1,
- "margin-bottom": 1,
- "margin-left": 1,
- "margin-right": 1,
- "margin-top": 1,
- "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": 1,
- "max-width": 1,
- "min-height": 1,
- "min-width": 1,
- "move-to": 1,
- "nav-down": 1,
- "nav-index": 1,
- "nav-left": 1,
- "nav-right": 1,
- "nav-up": 1,
- "opacity": 1,
- "orphans": 1,
- "outline": 1,
- "outline-color": 1,
- "outline-offset": 1,
- "outline-style": 1,
- "outline-width": 1,
- "overflow": 1,
- "overflow-style": 1,
- "overflow-x": 1,
- "overflow-y": 1,
- "padding": 1,
- "padding-bottom": 1,
- "padding-left": 1,
- "padding-right": 1,
- "padding-top": 1,
- "page": 1,
- "page-break-after": 1,
- "page-break-before": 1,
- "page-break-inside": 1,
- "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,
- "position": 1,
- "presentation-level": 1,
- "punctuation-trim": 1,
- "quotes": 1,
- "rendering-intent": 1,
- "resize": 1,
- "rest": 1,
- "rest-after": 1,
- "rest-before": 1,
- "richness": 1,
- "right": 1,
- "rotation": 1,
- "rotation-point": 1,
- "ruby-align": 1,
- "ruby-overhang": 1,
- "ruby-position": 1,
- "ruby-span": 1,
- "size": 1,
- "speak": 1,
- "speak-header": 1,
- "speak-numeral": 1,
- "speak-punctuation": 1,
- "speech-rate": 1,
- "stress": 1,
- "string-set": 1,
- "table-layout": 1,
- "target": 1,
- "target-name": 1,
- "target-new": 1,
- "target-position": 1,
- "text-align": 1,
- "text-align-last": 1,
- "text-decoration": 1,
- "text-emphasis": 1,
- "text-height": 1,
- "text-indent": 1,
- "text-justify": 1,
- "text-outline": 1,
- "text-shadow": 1,
- "text-transform": 1,
- "text-wrap": 1,
- "top": 1,
- "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": 1,
- "vertical-align": 1,
- "visibility": 1,
- "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": 1,
- "white-space-collapse": 1,
- "widows": 1,
- "width": 1,
- "word-break": 1,
- "word-spacing": 1,
- "word-wrap": 1,
- "z-index": 1,
-
- //IE
- "filter": 1,
- "zoom": 1
- };
+ var rule = this;
parser.addListener("property", function(event){
var name = event.property.text.toLowerCase();
-
- if (!properties[name] && name.charAt(0) != "-"){
- reporter.error("Unknown property '" + event.property + "'.", event.line, event.col, rule);
+ if (event.invalid) {
+ reporter.report(event.invalid.message, event.line, event.col, rule);
}
});
}
});
-/*
- * Rule: Don't use classes or IDs with elements (a.foo or a#foo).
- */
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);
+
+ }
- //rule information
+});
+CSSLint.addRule({
id: "overqualified-elements",
- name: "Overqualified Elements",
+ name: "Disallow overqualified elements",
desc: "Don't use classes or IDs with elements (a.foo or a#foo).",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this,
classes = {};
@@ -9109,11 +7215,11 @@ CSSLint.addRule({
for (j=0; j < selector.parts.length; j++){
part = selector.parts[j];
- if (part instanceof parserlib.css.SelectorPart){
+ 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.warn("Element (" + part + ") is overqualified, just use " + modifier + " without element name.", part.line, part.col, rule);
+ 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]){
@@ -9132,10 +7238,8 @@ CSSLint.addRule({
var prop;
for (prop in classes){
if (classes.hasOwnProperty(prop)){
-
- //one use means that this is overqualified
if (classes[prop].length == 1 && classes[prop][0].part.elementName){
- reporter.warn("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);
+ 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);
}
}
}
@@ -9143,18 +7247,11 @@ CSSLint.addRule({
}
});
-/*
- * Rule: Headings (h1-h6) should not be qualified (namespaced).
- */
CSSLint.addRule({
-
- //rule information
id: "qualified-headings",
- name: "Qualified Headings",
+ name: "Disallow qualified headings",
desc: "Headings should not be qualified (namespaced).",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this;
@@ -9169,9 +7266,9 @@ CSSLint.addRule({
for (j=0; j < selector.parts.length; j++){
part = selector.parts[j];
- if (part instanceof parserlib.css.SelectorPart){
+ if (part.type == parser.SELECTOR_PART_TYPE){
if (part.elementName && /h[1-6]/.test(part.elementName.toString()) && j > 0){
- reporter.warn("Heading (" + part.elementName + ") should not be qualified.", part.line, part.col, rule);
+ reporter.report("Heading (" + part.elementName + ") should not be qualified.", part.line, part.col, rule);
}
}
}
@@ -9180,18 +7277,11 @@ CSSLint.addRule({
}
});
-/*
- * Rule: Selectors that look like regular expressions are slow and should be avoided.
- */
CSSLint.addRule({
-
- //rule information
id: "regex-selectors",
- name: "Regex Selectors",
+ name: "Disallow selectors that look like regexs",
desc: "Selectors that look like regular expressions are slow and should be avoided.",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this;
@@ -9206,12 +7296,12 @@ CSSLint.addRule({
selector = selectors[i];
for (j=0; j < selector.parts.length; j++){
part = selector.parts[j];
- if (part instanceof parserlib.css.SelectorPart){
+ 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.warn("Attribute selectors with " + RegExp.$1 + " are slow!", modifier.line, modifier.col, rule);
+ reporter.report("Attribute selectors with " + RegExp.$1 + " are slow!", modifier.line, modifier.col, rule);
}
}
@@ -9223,23 +7313,14 @@ CSSLint.addRule({
}
});
-/*
- * Rule: Total number of rules should not exceed x.
- */
CSSLint.addRule({
-
- //rule information
id: "rules-count",
name: "Rules Count",
desc: "Track how many rules there are.",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this,
count = 0;
-
- //count each rule
parser.addListener("startrule", function(){
count++;
});
@@ -9250,50 +7331,153 @@ CSSLint.addRule({
}
});
-/*
- * Rule: Don't use text-indent for image replacement if you need to support rtl.
- *
- */
-/*
- * Should we be checking for rtl/ltr?
- */
-//Commented out due to lack of tests
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;
- //rule information
+ if (property.hack == "*") {
+ reporter.report("Property with star prefix found.", event.property.line, event.property.col, rule);
+ }
+ });
+ }
+});
+CSSLint.addRule({
id: "text-indent",
- name: "Text Indent",
+ name: "Disallow negative text-indent",
desc: "Checks for text indent less than -99px",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
- var rule = this;
-
- //check for use of "font-size"
+ 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,
- value = event.value.parts[0].value;
+ var name = event.property.toString().toLowerCase(),
+ value = event.value;
- if (name == "text-indent" && value < -99){
- reporter.warn("Negative text-indent doesn't work well with RTL. If you use text-indent for image replacement explicitly set text-direction for that item to ltr.", name.line, name.col, rule);
+ 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);
+
}
});
-/*
- * Rule: Headings (h1-h6) should be defined only once.
- */
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;
- //rule information
+ if (property.hack == "_") {
+ reporter.report("Property with underscore prefix found.", event.property.line, event.property.col, rule);
+ }
+ });
+ }
+});
+CSSLint.addRule({
id: "unique-headings",
- name: "Unique Headings",
+ name: "Headings should only be defined once",
desc: "Headings should be defined only once.",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this;
@@ -9310,35 +7494,56 @@ CSSLint.addRule({
var selectors = event.selectors,
selector,
part,
- i;
+ 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])/.test(part.elementName.toString())){
- headings[RegExp.$1]++;
- if (headings[RegExp.$1] > 1) {
- reporter.warn("Heading (" + part.elementName + ") has already been defined.", part.line, part.col, rule);
+ 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);
+ }
+ });
}
});
-/*
- * Rule: Don't use universal selector because it's slow.
- */
CSSLint.addRule({
-
- //rule information
id: "universal-selector",
- name: "Universal Selector",
+ name: "Disallow universal selector",
desc: "The universal selector (*) is known to be slow.",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this;
@@ -9354,61 +7559,123 @@ CSSLint.addRule({
part = selector.parts[selector.parts.length-1];
if (part.elementName == "*"){
- reporter.warn(rule.desc, part.line, part.col, rule);
+ reporter.report(rule.desc, part.line, part.col, rule);
}
}
});
}
});
-/*
- * Rule: When using a vendor-prefixed property, make sure to
- * include the standard one.
- */
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;
- //rule information
+ 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: "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",
-
- //initialization
init: function(parser, reporter){
var rule = this,
properties,
num,
propertiesToCheck = {
- "-moz-border-radius": "border-radius",
"-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-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"
};
-
- //event handler for beginning of rules
function startRule(){
properties = {};
num=1;
}
-
- //event handler for end of rules
function endRule(event){
var prop,
i, len,
@@ -9428,11 +7695,10 @@ CSSLint.addRule({
actual = needsStandard[i].actual;
if (!properties[needed]){
- reporter.warn("Missing standard property '" + needed + "' to go along with '" + actual + "'.", event.line, event.col, rule);
+ reporter.report("Missing standard property '" + needed + "' to go along with '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
} else {
- //make sure standard property is last
if (properties[needed][0].pos < properties[actual][0].pos){
- reporter.warn("Standard property '" + needed + "' should come after vendor-prefixed property '" + actual + "'.", event.line, event.col, rule);
+ reporter.report("Standard property '" + needed + "' should come after vendor-prefixed property '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
}
}
}
@@ -9441,6 +7707,9 @@ CSSLint.addRule({
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();
@@ -9454,76 +7723,27 @@ CSSLint.addRule({
parser.addListener("endrule", endRule);
parser.addListener("endfontface", endRule);
+ parser.addListener("endpage", endRule);
+ parser.addListener("endpagemargin", endRule);
+ parser.addListener("endkeyframerule", endRule);
}
});
-/*
- * Rule: If an element has a width of 100%, be careful when placing within
- * an element that has padding. It may look strange.
- */
-//Commented out pending further review.
-/*CSSLint.addRule({
-
- //rule information
- id: "width-100",
- name: "Width 100%",
- desc: "Be careful when using width: 100% on elements.",
- browsers: "All",
-
- //initialization
- init: function(parser, reporter){
- var rule = this,
- width100,
- boxsizing;
-
- parser.addListener("startrule", function(){
- width100 = null;
- boxsizing = false;
- });
-
- parser.addListener("property", function(event){
- var name = event.property.text.toLowerCase(),
- value = event.value;
-
- if (name == "width" && value == "100%"){
- width100 = event.property;
- } else if (name == "box-sizing" || /\-(?:webkit|ms|moz)\-box-sizing/.test(name)){ //means you know what you're doing
- boxsizing = true;
- }
- });
-
- parser.addListener("endrule", function(){
- if (width100 && !boxsizing){
- reporter.warn("Elements with a width of 100% may not appear as you expect inside of other elements.", width100.line, width100.col, rule);
- }
- });
- }
-
-});*/
-/*
- * Rule: You don't need to specify units when a value is 0.
- */
CSSLint.addRule({
-
- //rule information
id: "zero-units",
- name: "Zero Units",
+ name: "Disallow units for 0 values",
desc: "You don't need to specify units when a value is 0.",
browsers: "All",
-
- //initialization
init: function(parser, reporter){
var rule = this;
-
- //count how many times "float" is used
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){
- reporter.warn("Values of 0 shouldn't have units specified.", parts[i].line, parts[i].col, rule);
+ 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++;
}
@@ -9534,5 +7754,8 @@ CSSLint.addRule({
});
+
exports.CSSLint = CSSLint;
+
+
});