Browse files

New release

  • Loading branch information...
1 parent 974e8a6 commit f2190608eeed0071db990c13064dbd27718791ae @nzakas nzakas committed Jan 31, 2012
Showing with 1,695 additions and 1,204 deletions.
  1. +14 −0 CHANGELOG
  2. +1 −1 build.xml
  3. +2 −2 build/node-parserlib.js
  4. +1 −1 build/parserlib-core.js
  5. +1 −1 build/parserlib-css.js
  6. +2 −2 build/parserlib.js
  7. +514 −389 release/node-parserlib.js
  8. +1 −1 release/parserlib-core.js
  9. +513 −388 release/parserlib-css.js
  10. +132 −30 release/parserlib-tests.js
  11. +514 −389 release/parserlib.js
View
14 CHANGELOG
@@ -1,3 +1,15 @@
+January 31, 2012 - v0.1.4
+
+* Ensure time is checked correctly (Nicholas C. Zakas)
+* Make sure background images can also be gradients (Nicholas C. Zakas)
+* Validation for border-radius (Nicholas C. Zakas)
+* Validation for border-image-slice (Nicholas C. Zakas)
+* Validation for azimuth (Nicholas C. Zakas)
+* Merged in validation changes (Nicholas C. Zakas)
+* Finished refactoring of validation logic (Nicholas C. Zakas)
+* Cleanup (Nicholas C. Zakas)
+
+
January 5, 2012 - v0.1.3
* Fixed some validation issues (Nicholas C. Zakas)
@@ -186,3 +198,5 @@ November 28, 2011 - v0.1.0
+
+
View
2 build.xml
@@ -1,7 +1,7 @@
<project name="parserlib" default="build.all">
<!-- version number -->
- <property name="parserlib.version" value="0.1.3" />
+ <property name="parserlib.version" value="0.1.4" />
<!-- the directories containing the source files -->
<property name="src.dir" value="./src" />
View
4 build/node-parserlib.js
@@ -21,7 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-/* Version v@VERSION@, Build time: 31-January-2012 10:51:02 */
+/* Version v@VERSION@, Build time: 31-January-2012 10:55:24 */
var parserlib = {};
(function(){
@@ -931,7 +931,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-/* Version v@VERSION@, Build time: 31-January-2012 10:51:02 */
+/* Version v@VERSION@, Build time: 31-January-2012 10:55:24 */
(function(){
var EventTarget = parserlib.util.EventTarget,
TokenStreamBase = parserlib.util.TokenStreamBase,
View
2 build/parserlib-core.js
@@ -21,7 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-/* Version v@VERSION@, Build time: 31-January-2012 10:51:02 */
+/* Version v@VERSION@, Build time: 31-January-2012 10:55:24 */
var parserlib = {};
(function(){
View
2 build/parserlib-css.js
@@ -21,7 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-/* Version v@VERSION@, Build time: 31-January-2012 10:51:02 */
+/* Version v@VERSION@, Build time: 31-January-2012 10:55:24 */
(function(){
var EventTarget = parserlib.util.EventTarget,
TokenStreamBase = parserlib.util.TokenStreamBase,
View
4 build/parserlib.js
@@ -21,7 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-/* Version v@VERSION@, Build time: 31-January-2012 10:51:02 */
+/* Version v@VERSION@, Build time: 31-January-2012 10:55:24 */
var parserlib = {};
(function(){
@@ -931,7 +931,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-/* Version v@VERSION@, Build time: 31-January-2012 10:51:02 */
+/* Version v@VERSION@, Build time: 31-January-2012 10:55:24 */
(function(){
var EventTarget = parserlib.util.EventTarget,
TokenStreamBase = parserlib.util.TokenStreamBase,
View
903 release/node-parserlib.js
@@ -21,7 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-/* Version v0.1.3, Build time: 5-January-2012 09:23:31 */
+/* Version v0.1.4, Build time: 31-January-2012 10:55:24 */
var parserlib = {};
(function(){
@@ -931,7 +931,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-/* Version v0.1.3, Build time: 5-January-2012 09:23:31 */
+/* Version v0.1.4, Build time: 31-January-2012 10:55:24 */
(function(){
var EventTarget = parserlib.util.EventTarget,
TokenStreamBase = parserlib.util.TokenStreamBase,
@@ -3411,69 +3411,199 @@ nth
['-'|'+']? INTEGER | {O}{D}{D} | {E}{V}{E}{N} ] S*
;
*/
+/*global Validation, ValidationTypes, ValidationError*/
var Properties = {
//A
"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>", separator: "," },
- "animation-direction" : { multi: "normal | alternate", separator: "," },
- "animation-duration" : { multi: "<time>", separator: "," },
- "animation-iteration-count" : { multi: "<number> | infinite", separator: "," },
- "animation-name" : { multi: "none | <ident>", separator: "," },
- "animation-play-state" : { multi: "running | paused", separator: "," },
+ "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,
+
+ //vendor prefixed
+ "-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" : 1,
+ "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);
+ }
+ }
+ },
//B
"backface-visibility" : "visible | hidden",
"background" : 1,
- "background-attachment" : { multi: "<attachment>", separator: "," },
- "background-clip" : { multi: "<box>", separator: "," },
+ "background-attachment" : { multi: "<attachment>", comma: true },
+ "background-clip" : { multi: "<box>", comma: true },
"background-color" : "<color> | inherit",
- "background-image" : { multi: "<bg-image>", separator: "," },
- "background-origin" : { multi: "<box>", separator: "," },
- "background-position" : { multi: "<bg-position>", separator: ",", complex: true },
- "background-repeat" : { multi: "<repeat-style>", complex: true },
- "background-size" : { multi: "<bg-size>", separator: ",", complex: true },
- "baseline-shift" : "baseline | sub | super | <percentage> <length>",
+ "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>",
"binding" : 1,
"bleed" : "<length>",
"bookmark-label" : "<content> | <attr> | <string>",
"bookmark-level" : "none | <integer>",
"bookmark-state" : "open | closed",
"bookmark-target" : "none | <uri> | <attr>",
- "border" : { group: "<border-width> || <border-style> || <color>" },
- "border-bottom" : { group: "<border-width> || <border-style> || <color>" },
+ "border" : "<border-width> || <border-style> || <color>",
+ "border-bottom" : "<border-width> || <border-style> || <color>",
"border-bottom-color" : "<color>",
- "border-bottom-left-radius" : { single: "<x-one-radius>", complex: true },
- "border-bottom-right-radius" : { single: "<x-one-radius>", complex: true },
+ "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" : 1,
+ "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" : { group: "<border-width> || <border-style> || <color>" },
+ "border-left" : "<border-width> || <border-style> || <color>",
"border-left-color" : "<color> | inherit",
"border-left-style" : "<border-style>",
"border-left-width" : "<border-width>",
- "border-radius" : 1,
- "border-right" : { group: "<border-width> || <border-style> || <color>" },
+ "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-style" : { multi: "<border-style>", max: 4 },
- "border-top" : { group: "<border-width> || <border-style> || <color>" },
+ "border-top" : "<border-width> || <border-style> || <color>",
"border-top-color" : "<color> | inherit",
- "border-top-left-radius" : { single: "<x-one-radius>", complex: true },
- "border-top-right-radius" : { single: "<x-one-radius>", complex: true },
+ "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 },
@@ -3487,7 +3617,19 @@ var Properties = {
"box-ordinal-group" : "<integer>",
"box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit",
"box-pack" : "start | end | center | justify",
- "box-shadow" : { property: true },
+ "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",
@@ -3502,7 +3644,7 @@ var Properties = {
"column-count" : "<integer> | auto", //http://www.w3.org/TR/css3-multicol/
"column-fill" : "auto | balance",
"column-gap" : "<length> | normal",
- "column-rule" : { group: "<border-width> || <border-style> || <color>" },
+ "column-rule" : "<border-width> || <border-style> || <color>",
"column-rule-color" : "<color>",
"column-rule-style" : "<border-style>",
"column-rule-width" : "<border-width>",
@@ -3842,6 +3984,13 @@ function PropertyValueIterator(value){
*/
this._marks = [];
+ /**
+ * Holds the original property value.
+ * @type parserlib.css.PropertyValue
+ * @property value
+ */
+ this.value = value;
+
}
/**
@@ -3854,6 +4003,15 @@ PropertyValueIterator.prototype.count = function(){
};
/**
+ * Indicates if the iterator is positioned at the first item.
+ * @return {Boolean} True if positioned at first item, false if not.
+ * @method isFirst
+ */
+PropertyValueIterator.prototype.isFirst = function(){
+ return this._i === 0;
+};
+
+/**
* Indicates if there are more parts of the property value.
* @return {Boolean} True if there are more parts, false if not.
* @method hasNext
@@ -3895,6 +4053,17 @@ PropertyValueIterator.prototype.next = function(){
};
/**
+ * Returns the previous part of the property value or null if there is no
+ * previous part.
+ * @return {parserlib.css.PropertyValuePart} The previous part of the
+ * property value or null if there is no next part.
+ * @method previous
+ */
+PropertyValueIterator.prototype.previous = function(){
+ return this._i > 0 ? this._parts[--this._i] : null;
+};
+
+/**
* Restores the last saved bookmark.
* @return {void}
* @method restore
@@ -5541,7 +5710,7 @@ var Tokens = [
//This file will likely change a lot! Very experimental!
-/*global Properties, ValidationError, PropertyValueIterator */
+/*global Properties, ValidationTypes, ValidationError, PropertyValueIterator */
var Validation = {
validate: function(property, value){
@@ -5568,181 +5737,303 @@ var Validation = {
//initialization
if (typeof spec == "string"){
- types = spec.split(/\s\|\s/g);
- max = 1;
- } else if (spec.multi) {
- types = spec.multi.split(/\s\|\s/g);
- max = spec.max;
- } else if (spec.single) {
- types = spec.single.split(/\s\|\s/g);
- } else if (spec.group){
- types = spec.group.split(/\s\|\|\s/g);
- group = { total: 0 };
- }
-
- //Start validation----
- //TODO: Clean up once I figure out the best way to do this
- //Check for complex validations first
- if (spec.complex) {
- if (spec.multi) {
- Validation.types.multiProperty(types[0], value);
+ if (spec.indexOf("||") > -1) {
+ this.groupProperty(spec, expression);
} else {
- Validation.types.singleProperty(types[0], value);
+ this.singleProperty(spec, expression, 1);
}
- } else if (spec.property) {
- Validation.properties[name](value);
- } else {
- //if there's a maximum set, use it (max can't be 0)
- if (max) {
- if (parts.length > max){
- throw new ValidationError("Expected a max of " + max + " property value(s) but found " + parts.length + ".", value.line, value.col);
- }
- }
+ } else if (spec.multi) {
+ this.multiProperty(spec.multi, expression, spec.comma, spec.max || Infinity);
+ } else if (typeof spec == "function") {
+ spec(expression);
+ }
- while (expression.hasNext()){
- part = expression.peek();
- msg = [];
- valid = false;
-
- if (spec.separator && part.type == "operator"){
-
- //two operators in a row - not allowed?
- if ((last && last.type == "operator")){
- msg = msg.concat(types);
- } else if (!expression.peek(1)){
- msg = msg.concat("end of line");
- } else if (part != spec.separator){
- msg.push("'" + spec.separator + "'");
- } else {
- valid = true;
- expression.next();
-
- //if it's a group, reset the tracker
- if (group) {
- group = { total: 0 };
- }
- }
- } else {
+ }
- literals = [];
+ },
+
+ singleProperty: function(types, expression, max, partial) {
- for (j=0, count=types.length; j < count; j++){
-
- //if it's a group and one of the values has been found, skip it
- if (group && group[types[j]]){
- continue;
- }
-
- expression.mark();
- if (typeof Validation.types[types[j]] == "undefined"){
- valid = Validation.types.literal(expression.next(), types[j]);
- literals.push(types[j]);
- } else {
- valid = Validation.types[types[j]](expression.next());
- msg.push(types[j]);
- }
+ 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) {
- if (valid) {
- if (group){
- group[types[j]] = 1;
- group.total++;
- }
- break;
- } else {
- expression.restore();
- }
- }
- }
+ 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;
-
- if (!valid) {
- if (literals.length) {
- msg.push("one of (" + literals.join(" | ") + ")");
- }
- throw new ValidationError("Expected " + (msg.join(" or ") || "end of value") + " but found '" + part + "'.", value.line, value.col);
+ } else if (comma) {
+ if (expression.peek() == ",") {
+ part = expression.next();
+ } else {
+ break;
}
-
-
- last = part;
- }
+ }
+ } else {
+ break;
+
}
-
- //for groups, make sure all items are there
- //if (group && group.total != types.length){
- // throw new ValidationError("Expected all of (" + types.join(", ") + ") but found '" + value + "'.", value.line, value.col);
- //}
}
+
+ 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);
+ }
},
-
- types: {
- any: function(part, spec) {
- var types = spec.split(/\s\|\s/g),
- result = false,
- i, len;
-
- for (i=0, len=types.length; i < len && !result; i++){
- if (this[types[i]]){
- result = this[types[i]](part);
+ 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) {
+
+ //no dupes
+ if (groups[name]) {
+ break;
} else {
- result = this.literal(part, types[i]);
+ 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);
+ }
+ }
+
+
+
+};
+/**
+ * Type to use when a validation error occurs.
+ * @class ValidationError
+ * @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 ValidationError(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
+ValidationError.prototype = new Error();
+//This file will likely change a lot! Very experimental!
+/*global Properties, Validation, ValidationError, PropertyValueIterator, console*/
+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]){
+ found = true;
+ }
+ }
+
+ return found;
+ },
+
+ isSimple: function(type) {
+ return !!this.simple[type];
+ },
+
+ isComplex: function(type) {
+ return !!this.complex[type];
+ },
+
+ /**
+ * Determines if the next part(s) of the given expression
+ * are any of the given types.
+ */
+ 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;
+ },
+
+ /**
+ * Determines if the next part(s) of the given expresion
+ * are one of a group.
+ */
+ 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;
+ },
+
+ /**
+ * Determines if the next part(s) of the given expression
+ * are of a given type.
+ */
+ isType: function (expression, type) {
+ var part = expression.peek(),
+ result = false;
- return result;
- },
+ 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 this.literal(part, "xx-small | x-small | small | medium | large | x-large | xx-large");
+ return ValidationTypes.isLiteral(part, "xx-small | x-small | small | medium | large | x-large | xx-large");
},
"<attachment>": function(part){
- return this.literal(part, "scroll | fixed | local");
+ 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) || part == "none";
+ 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 this.literal(part, "padding-box | border-box | content-box");
+ 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 this.literal(part, "smaller | larger");
+ return ValidationTypes.isLiteral(part, "smaller | larger");
},
//any identifier
"<ident>": function(part){
return part.type == "identifier";
},
- //specific identifiers
- "literal": function(part, options){
- var text = part.text.toString().toLowerCase(),
- args = options.split(" | "),
- i, len, found = false;
-
-
- for (i=0,len=args.length; i < len && !found; i++){
- if (text == args[i]){
- found = true;
- }
- }
-
- return found;
- },
-
"<length>": function(part){
return part.type == "length" || part.type == "number" || part.type == "integer" || part == "0";
},
@@ -5780,15 +6071,15 @@ var Validation = {
},
"<border-width>": function(part){
- return this["<length>"](part) || this.literal(part, "thin | medium | thick");
+ return this["<length>"](part) || ValidationTypes.isLiteral(part, "thin | medium | thick");
},
"<border-style>": function(part){
- return this.literal(part, "none | hidden | dotted | dashed | solid | double | groove | ridge | inset | outset");
+ 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) || this.literal(part, "auto");
+ return this["<length>"](part) || this["<percentage>"](part) || ValidationTypes.isLiteral(part, "auto");
},
"<padding-width>": function(part){
@@ -5798,11 +6089,14 @@ var Validation = {
"<shape>": function(part){
return part.type == "function" && (part.name == "rect" || part.name == "inset-rect");
},
-
- //---------------------------------------------------------------------
- // Complex Types
- //---------------------------------------------------------------------
+
+ "<time>": function(part) {
+ return part.type == "time";
+ }
+ },
+ complex: {
+
"<bg-position>": function(expression){
var types = this,
result = false,
@@ -5812,54 +6106,38 @@ var Validation = {
part,
i, len;
- if (expression.hasNext()){
-
- part = expression.next();
- if (types.literal(part, "top | bottom")) {
- result = true;
- } else {
-
- //must be two-part
- if (types.any(part, numeric)){
- if (expression.hasNext()){
- part = expression.next();
- result = types.any(part, numeric + " | " + yDir);
- }
- } else if (types.any(part, xDir)){
- if (expression.hasNext()){
- part = expression.next();
-
- //two- or three-part
- if (types.any(part, yDir)){
- result = true;
-
- if (expression.hasNext() && types.any(expression.peek(), numeric)) {
- expression.next();
- }
-
- } else if (types.any(part, numeric)){
+ if (ValidationTypes.isAny(expression, "top | bottom")) {
+ result = true;
+ } else {
+
+ //must be two-part
+ if (ValidationTypes.isAny(expression, numeric)){
+ if (expression.hasNext()){
+ result = ValidationTypes.isAny(expression, numeric + " | " + yDir);
+ }
+ } else if (ValidationTypes.isAny(expression, xDir)){
+ if (expression.hasNext()){
+
+ //two- or three-part
+ if (ValidationTypes.isAny(expression, yDir)){
+ result = true;
+
+ ValidationTypes.isAny(expression, numeric);
- //could also be two-part, so check the next part
- if (expression.hasNext() && types.any(expression.peek(), yDir)){
- expression.next(); //skip, already tested
- part = expression.next();
-
- if (expression.hasNext() && types.any(expression.peek(), numeric)){
- expression.next();
- result = true;
- } else {
- result = true;
- }
-
- } else {
- result = true; //it's two-part
- }
+ } else if (ValidationTypes.isAny(expression, numeric)){
+
+ //could also be two-part, so check the next part
+ if (ValidationTypes.isAny(expression, yDir)){
+ ValidationTypes.isAny(expression, numeric);
}
+
+ result = true;
}
- }
- }
- }
+ }
+ }
+ }
+
return result;
},
@@ -5872,19 +6150,12 @@ var Validation = {
part,
i, len;
- if (expression.hasNext()){
- part = expression.next();
-
- if (types.literal(part, "cover | contain")) {
- result = true;
- } else if (types.any(part, numeric)) {
- result = true;
-
- if (expression.hasNext() && types.any(expression.peek(), numeric)) {
- expression.next();
- }
- }
- }
+ if (ValidationTypes.isAny(expression, "cover | contain")) {
+ result = true;
+ } else if (ValidationTypes.isAny(expression, numeric)) {
+ result = true;
+ ValidationTypes.isAny(expression, numeric);
+ }
return result;
},
@@ -5898,12 +6169,12 @@ var Validation = {
if (expression.hasNext()){
part = expression.next();
- if (this.literal(part, "repeat-x | repeat-y")) {
+ if (ValidationTypes.isLiteral(part, "repeat-x | repeat-y")) {
result = true;
- } else if (this.literal(part, values)) {
+ } else if (ValidationTypes.isLiteral(part, values)) {
result = true;
- if (expression.hasNext() && this.literal(expression.peek(), values)) {
+ if (expression.hasNext() && ValidationTypes.isLiteral(expression.peek(), values)) {
expression.next();
}
}
@@ -5916,46 +6187,37 @@ var Validation = {
"<shadow>": function(expression) {
//inset? && [ <length>{2,4} && <color>? ]
var result = false,
+ count = 0,
inset = false,
color = false,
- count = 0,
part;
if (expression.hasNext()) {
- part = expression.peek();
- if (this.literal(part, "inset")){
- expression.next();
- part = expression.peek();
+ if (ValidationTypes.isAny(expression, "inset")){
inset = true;
}
- if (part) {
- if (this["<color>"](part)) {
- expression.next();
- part = expression.peek();
- color = true;
- }
+ if (ValidationTypes.isAny(expression, "<color>")) {
+ color = true;
}
- while (part && this["<length>"](part) && count < 4) {
+ while (ValidationTypes.isAny(expression, "<length>") && count < 4) {
count++;
- expression.next();
- part = expression.peek();
}
- if (part) {
- if (this["<color>"](part) && !color) {
- expression.next();
- part = expression.peek();
+ if (expression.hasNext()) {
+ if (!color) {
+ ValidationTypes.isAny(expression, "<color>");
}
+
+ if (!inset) {
+ ValidationTypes.isAny(expression, "inset");
+ }
+
}
- if (part && this.literal(part, "inset") && !inset){
- expression.next();
- }
-
result = (count >= 2 && count <= 4);
}
@@ -5970,153 +6232,16 @@ var Validation = {
numeric = "<length> | <percentage>",
part;
- if (expression.hasNext()) {
- part = expression.peek();
+ if (ValidationTypes.isAny(expression, numeric)){
+ result = true;
- if (this.any(part, numeric)){
- result = true;
- expression.next();
- part = expression.peek();
-
- if (part && this.any(part, numeric)){
- expression.next();
- }
- }
-
- }
+ ValidationTypes.isAny(expression, numeric);
+ }
return result;
- },
-
- //---------------------------------------------------------------------
- // Properties
- //---------------------------------------------------------------------
-
- singleProperty: function ( type, value, expression, partial ) {
- //so ashamed...
- expression = expression || new PropertyValueIterator(value);
-
- var result = false,
- part;
-
- if (expression.hasNext()) {
- if ( this[type](expression) ) {
- result = true;
- }
- }
-
- if (!result) {
- throw new ValidationError("Expected " + type + " but found '" + value + "'.", value.line, value.col);
- } else if (expression.hasNext() && !partial) {
- part = expression.next();
- throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
- }
- },
-
- multiProperty: function ( type, value, expression, partial ) {
-
- //so ashamed...
- expression = expression || new PropertyValueIterator(value);
-
- var result = false,
- part;
-
- while(expression.hasNext() && !result) {
- if ( this[type]( expression ) ) {
-
- if (!expression.hasNext()) {
- result = true;
- } else if (expression.peek() == ",") {
- expression.next();
- } else {
- result = true;
- break;
- }
- } else {
- break;
- }
- }
-
- if (!result) {
- throw new ValidationError("Expected " + type + " but found '" + value + "'.", value.line, value.col);
- } else if (expression.hasNext() && !partial) {
- part = expression.next();
- throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
- }
}
-
-
- },
-
- properties: {
-
- "box-shadow": function (value) {
- var expression = new PropertyValueIterator(value),
- result = false,
- part;
-
- if (expression.hasNext()){
- part = expression.peek();
-
- if (!Validation.types.literal(part, "none")) {
- Validation.types.multiProperty("<shadow>", value, expression);
- } else {
- expression.next();
- if (expression.hasNext()) {
- part = expression.next();
- throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
- }
- }
- }
-
- },
-
- "border-one-radius": function (value) {
- this.singleProperty("<x-one-radius>", value);
- }
-
-
}
-
-
-
};
-/**
- * Type to use when a validation error occurs.
- * @class ValidationError
- * @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 ValidationError(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
-ValidationError.prototype = new Error();
parserlib.css = {
View
2 release/parserlib-core.js
@@ -21,7 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-/* Version v0.1.3, Build time: 5-January-2012 09:23:31 */
+/* Version v0.1.4, Build time: 31-January-2012 10:55:24 */
var parserlib = {};
(function(){
View
901 release/parserlib-css.js
@@ -21,7 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-/* Version v0.1.3, Build time: 5-January-2012 09:23:31 */
+/* Version v0.1.4, Build time: 31-January-2012 10:55:24 */
(function(){
var EventTarget = parserlib.util.EventTarget,
TokenStreamBase = parserlib.util.TokenStreamBase,
@@ -2501,69 +2501,199 @@ nth
['-'|'+']? INTEGER | {O}{D}{D} | {E}{V}{E}{N} ] S*
;
*/
+/*global Validation, ValidationTypes, ValidationError*/
var Properties = {
//A
"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>", separator: "," },
- "animation-direction" : { multi: "normal | alternate", separator: "," },
- "animation-duration" : { multi: "<time>", separator: "," },
- "animation-iteration-count" : { multi: "<number> | infinite", separator: "," },
- "animation-name" : { multi: "none | <ident>", separator: "," },
- "animation-play-state" : { multi: "running | paused", separator: "," },
+ "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,
+
+ //vendor prefixed
+ "-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" : 1,
+ "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);
+ }
+ }
+ },
//B
"backface-visibility" : "visible | hidden",
"background" : 1,
- "background-attachment" : { multi: "<attachment>", separator: "," },
- "background-clip" : { multi: "<box>", separator: "," },
+ "background-attachment" : { multi: "<attachment>", comma: true },
+ "background-clip" : { multi: "<box>", comma: true },
"background-color" : "<color> | inherit",
- "background-image" : { multi: "<bg-image>", separator: "," },
- "background-origin" : { multi: "<box>", separator: "," },
- "background-position" : { multi: "<bg-position>", separator: ",", complex: true },
- "background-repeat" : { multi: "<repeat-style>", complex: true },
- "background-size" : { multi: "<bg-size>", separator: ",", complex: true },
- "baseline-shift" : "baseline | sub | super | <percentage> <length>",
+ "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>",
"binding" : 1,
"bleed" : "<length>",
"bookmark-label" : "<content> | <attr> | <string>",
"bookmark-level" : "none | <integer>",
"bookmark-state" : "open | closed",
"bookmark-target" : "none | <uri> | <attr>",
- "border" : { group: "<border-width> || <border-style> || <color>" },
- "border-bottom" : { group: "<border-width> || <border-style> || <color>" },
+ "border" : "<border-width> || <border-style> || <color>",
+ "border-bottom" : "<border-width> || <border-style> || <color>",
"border-bottom-color" : "<color>",
- "border-bottom-left-radius" : { single: "<x-one-radius>", complex: true },
- "border-bottom-right-radius" : { single: "<x-one-radius>", complex: true },
+ "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" : 1,
+ "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" : { group: "<border-width> || <border-style> || <color>" },
+ "border-left" : "<border-width> || <border-style> || <color>",
"border-left-color" : "<color> | inherit",
"border-left-style" : "<border-style>",
"border-left-width" : "<border-width>",
- "border-radius" : 1,
- "border-right" : { group: "<border-width> || <border-style> || <color>" },
+ "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-style" : { multi: "<border-style>", max: 4 },
- "border-top" : { group: "<border-width> || <border-style> || <color>" },
+ "border-top" : "<border-width> || <border-style> || <color>",
"border-top-color" : "<color> | inherit",
- "border-top-left-radius" : { single: "<x-one-radius>", complex: true },
- "border-top-right-radius" : { single: "<x-one-radius>", complex: true },
+ "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 },
@@ -2577,7 +2707,19 @@ var Properties = {
"box-ordinal-group" : "<integer>",
"box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit",
"box-pack" : "start | end | center | justify",
- "box-shadow" : { property: true },
+ "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",
@@ -2592,7 +2734,7 @@ var Properties = {
"column-count" : "<integer> | auto", //http://www.w3.org/TR/css3-multicol/
"column-fill" : "auto | balance",
"column-gap" : "<length> | normal",
- "column-rule" : { group: "<border-width> || <border-style> || <color>" },
+ "column-rule" : "<border-width> || <border-style> || <color>",
"column-rule-color" : "<color>",
"column-rule-style" : "<border-style>",
"column-rule-width" : "<border-width>",
@@ -2932,6 +3074,13 @@ function PropertyValueIterator(value){
*/
this._marks = [];
+ /**
+ * Holds the original property value.
+ * @type parserlib.css.PropertyValue
+ * @property value
+ */
+ this.value = value;
+
}
/**
@@ -2944,6 +3093,15 @@ PropertyValueIterator.prototype.count = function(){
};
/**
+ * Indicates if the iterator is positioned at the first item.
+ * @return {Boolean} True if positioned at first item, false if not.
+ * @method isFirst
+ */
+PropertyValueIterator.prototype.isFirst = function(){
+ return this._i === 0;
+};
+
+/**
* Indicates if there are more parts of the property value.
* @return {Boolean} True if there are more parts, false if not.
* @method hasNext
@@ -2985,6 +3143,17 @@ PropertyValueIterator.prototype.next = function(){
};
/**
+ * Returns the previous part of the property value or null if there is no
+ * previous part.
+ * @return {parserlib.css.PropertyValuePart} The previous part of the
+ * property value or null if there is no next part.
+ * @method previous
+ */
+PropertyValueIterator.prototype.previous = function(){
+ return this._i > 0 ? this._parts[--this._i] : null;
+};
+
+/**
* Restores the last saved bookmark.
* @return {void}
* @method restore
@@ -4631,7 +4800,7 @@ var Tokens = [
//This file will likely change a lot! Very experimental!
-/*global Properties, ValidationError, PropertyValueIterator */
+/*global Properties, ValidationTypes, ValidationError, PropertyValueIterator */
var Validation = {
validate: function(property, value){
@@ -4658,181 +4827,303 @@ var Validation = {
//initialization
if (typeof spec == "string"){
- types = spec.split(/\s\|\s/g);
- max = 1;
- } else if (spec.multi) {
- types = spec.multi.split(/\s\|\s/g);
- max = spec.max;
- } else if (spec.single) {
- types = spec.single.split(/\s\|\s/g);
- } else if (spec.group){
- types = spec.group.split(/\s\|\|\s/g);
- group = { total: 0 };
- }
-
- //Start validation----
- //TODO: Clean up once I figure out the best way to do this
- //Check for complex validations first
- if (spec.complex) {
- if (spec.multi) {
- Validation.types.multiProperty(types[0], value);
+ if (spec.indexOf("||") > -1) {
+ this.groupProperty(spec, expression);
} else {
- Validation.types.singleProperty(types[0], value);
+ this.singleProperty(spec, expression, 1);
}
- } else if (spec.property) {
- Validation.properties[name](value);
- } else {
- //if there's a maximum set, use it (max can't be 0)
- if (max) {
- if (parts.length > max){
- throw new ValidationError("Expected a max of " + max + " property value(s) but found " + parts.length + ".", value.line, value.col);
- }
- }
+ } else if (spec.multi) {
+ this.multiProperty(spec.multi, expression, spec.comma, spec.max || Infinity);
+ } else if (typeof spec == "function") {
+ spec(expression);
+ }
- while (expression.hasNext()){
- part = expression.peek();
- msg = [];
- valid = false;
-
- if (spec.separator && part.type == "operator"){
-
- //two operators in a row - not allowed?
- if ((last && last.type == "operator")){
- msg = msg.concat(types);
- } else if (!expression.peek(1)){
- msg = msg.concat("end of line");
- } else if (part != spec.separator){
- msg.push("'" + spec.separator + "'");
- } else {
- valid = true;
- expression.next();
-
- //if it's a group, reset the tracker
- if (group) {
- group = { total: 0 };
- }
- }
- } else {
+ }
- literals = [];
+ },
+
+ 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) {
- for (j=0, count=types.length; j < count; j++){
-
- //if it's a group and one of the values has been found, skip it
- if (group && group[types[j]]){
- continue;
- }
-
- expression.mark();
- if (typeof Validation.types[types[j]] == "undefined"){
- valid = Validation.types.literal(expression.next(), types[j]);
- literals.push(types[j]);
- } else {
- valid = Validation.types[types[j]](expression.next());
- msg.push(types[j]);
- }
+ 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;
- if (valid) {
- if (group){
- group[types[j]] = 1;
- group.total++;
- }
- break;
- } else {
- expression.restore();
- }
- }
+ } else if (comma) {
+ if (expression.peek() == ",") {
+ part = expression.next();
+ } else {
+ break;
}
+ }
+ } else {
+ break;
-
- if (!valid) {
- if (literals.length) {
- msg.push("one of (" + literals.join(" | ") + ")");
- }
- throw new ValidationError("Expected " + (msg.join(" or ") || "end of value") + " but found '" + part + "'.", value.line, value.col);
- }
-
-
- last = part;
- }
}
-
- //for groups, make sure all items are there
- //if (group && group.total != types.length){
- // throw new ValidationError("Expected all of (" + types.join(", ") + ") but found '" + value + "'.", value.line, value.col);
- //}
}
+
+ 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);
+ }
},
-
- types: {
- any: function(part, spec) {
- var types = spec.split(/\s\|\s/g),
- result = false,
- i, len;
-
- for (i=0, len=types.length; i < len && !result; i++){
- if (this[types[i]]){
- result = this[types[i]](part);
+ 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) {
+
+ //no dupes
+ if (groups[name]) {
+ break;
} else {
- result = this.literal(part, types[i]);
+ 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);
+ }
+ }
+
+
+
+};
+/**
+ * Type to use when a validation error occurs.
+ * @class ValidationError
+ * @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 ValidationError(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
+ValidationError.prototype = new Error();
+//This file will likely change a lot! Very experimental!
+/*global Properties, Validation, ValidationError, PropertyValueIterator, console*/
+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]){
+ found = true;
+ }
+ }
+
+ return found;
+ },
+
+ isSimple: function(type) {
+ return !!this.simple[type];
+ },
+
+ isComplex: function(type) {
+ return !!this.complex[type];
+ },
+
+ /**
+ * Determines if the next part(s) of the given expression
+ * are any of the given types.
+ */
+ 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;
+ },
+
+ /**
+ * Determines if the next part(s) of the given expresion
+ * are one of a group.
+ */
+ 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;
+ },
+
+ /**
+ * Determines if the next part(s) of the given expression
+ * are of a given type.
+ */
+ isType: function (expression, type) {
+ var part = expression.peek(),
+ result = false;
- return result;
- },
+ 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 this.literal(part, "xx-small | x-small | small | medium | large | x-large | xx-large");
+ return ValidationTypes.isLiteral(part, "xx-small | x-small | small | medium | large | x-large | xx-large");
},
"<attachment>": function(part){
- return this.literal(part, "scroll | fixed | local");
+ 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) || part == "none";
+ 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 this.literal(part, "padding-box | border-box | content-box");
+ 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 this.literal(part, "smaller | larger");
+ return ValidationTypes.isLiteral(part, "smaller | larger");
},
//any identifier
"<ident>": function(part){
return part.type == "identifier";
},
- //specific identifiers
- "literal": function(part, options){
- var text = part.text.toString().toLowerCase(),
- args = options.split(" | "),
- i, len, found = false;
-
-
- for (i=0,len=args.length; i < len && !found; i++){
- if (text == args[i]){
- found = true;
- }
- }
-
- return found;
- },
-
"<length>": function(part){
return part.type == "length" || part.type == "number" || part.type == "integer" || part == "0";
},
@@ -4870,15 +5161,15 @@ var Validation = {
},
"<border-width>": function(part){
- return this["<length>"](part) || this.literal(part, "thin | medium | thick");
+ return this["<length>"](part) || ValidationTypes.isLiteral(part, "thin | medium | thick");
},
"<border-style>": function(part){
- return this.literal(part, "none | hidden | dotted | dashed | solid | double | groove | ridge | inset | outset");
+ 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) || this.literal(part, "auto");
+ return this["<length>"](part) || this["<percentage>"](part) || ValidationTypes.isLiteral(part, "auto");
},
"<padding-width>": function(part){
@@ -4888,11 +5179,14 @@ var Validation = {
"<shape>": function(part){
return part.type == "function" && (part.name == "rect" || part.name == "inset-rect");
},
-
- //---------------------------------------------------------------------
- // Complex Types
- //---------------------------------------------------------------------
+
+ "<time>": function(part) {
+ return part.type == "time";
+ }
+ },
+ complex: {
+
"<bg-position>": function(expression){
var types = this,
result = false,
@@ -4902,54 +5196,38 @@ var Validation = {
part,
i, len;
- if (expression.hasNext()){
-
- part = expression.next();
- if (types.literal(part, "top | bottom")) {
- result = true;
- } else {
-
- //must be two-part
- if (types.any(part, numeric)){
- if (expression.hasNext()){
- part = expression.next();
- result = types.any(part, numeric + " | " + yDir);
- }
- } else if (types.any(part, xDir)){
- if (expression.hasNext()){
- part = expression.next();
-
- //two- or three-part
- if (types.any(part, yDir)){
- result = true;
-
- if (expression.hasNext() && types.any(expression.peek(), numeric)) {
- expression.next();
- }
-
- } else if (types.any(part, numeric)){
+ if (ValidationTypes.isAny(expression, "top | bottom")) {
+ result = true;
+ } else {
+
+ //must be two-part
+ if (ValidationTypes.isAny(expression, numeric)){
+ if (expression.hasNext()){
+ result = ValidationTypes.isAny(expression, numeric + " | " + yDir);
+ }
+ } else if (ValidationTypes.isAny(expression, xDir)){
+ if (expression.hasNext()){
+
+ //two- or three-part
+ if (ValidationTypes.isAny(expression, yDir)){
+ result = true;
+
+ ValidationTypes.isAny(expression, numeric);
- //could also be two-part, so check the next part
- if (expression.hasNext() && types.any(expression.peek(), yDir)){
- expression.next(); //skip, already tested
- part = expression.next();
-
- if (expression.hasNext() && types.any(expression.peek(), numeric)){
- expression.next();
- result = true;
- } else {
- result = true;
- }
-
- } else {
- result = true; //it's two-part
- }
+ } else if (ValidationTypes.isAny(expression, numeric)){
+
+ //could also be two-part, so check the next part
+ if (ValidationTypes.isAny(expression, yDir)){
+ ValidationTypes.isAny(expression, numeric);
}
+
+ result = true;
}
- }
- }
- }
+ }
+ }
+ }
+
return result;
},
@@ -4962,19 +5240,12 @@ var Validation = {
part,
i, len;
- if (expression.hasNext()){
- part = expression.next();
-
- if (types.literal(part, "cover | contain")) {
- result = true;
- } else if (types.any(part, numeric)) {
- result = true;
-
- if (expression.hasNext() && types.any(expression.peek(), numeric)) {
- expression.next();
- }
- }
- }
+ if (ValidationTypes.isAny(expression, "cover | contain")) {
+ result = true;
+ } else if (ValidationTypes.isAny(expression, numeric)) {
+ result = true;
+ ValidationTypes.isAny(expression, numeric);
+ }
return result;
},
@@ -4988,12 +5259,12 @@ var Validation = {
if (expression.hasNext()){
part = expression.next();
- if (this.literal(part, "repeat-x | repeat-y")) {
+ if (ValidationTypes.isLiteral(part, "repeat-x | repeat-y")) {
result = true;
- } else if (this.literal(part, values)) {
+ } else if (ValidationTypes.isLiteral(part, values)) {
result = true;
- if (expression.hasNext() && this.literal(expression.peek(), values)) {
+ if (expression.hasNext() && ValidationTypes.isLiteral(expression.peek(), values)) {
expression.next();
}
}
@@ -5006,46 +5277,37 @@ var Validation = {
"<shadow>": function(expression) {
//inset? && [ <length>{2,4} && <color>? ]
var result = false,
+ count = 0,
inset = false,
color = false,
- count = 0,
part;
if (expression.hasNext()) {
- part = expression.peek();
- if (this.literal(part, "inset")){
- expression.next();
- part = expression.peek();
+ if (ValidationTypes.isAny(expression, "inset")){
inset = true;
}
- if (part) {
- if (this["<color>"](part)) {
- expression.next();
- part = expression.peek();