} else if (this.simple[type]) {
result = this.simple[type](part);
} else if (this.complex[type] instanceof Matcher) {
result = this.complex[type].match(expression);
result = this.complex[type](expression);
"xx-small | x-small | small | medium | large | x-large | xx-large",
"scroll-position | contents | <animateable-feature-name>",
"<animateable-feature-name>": function(part) {
return this["<ident>"](part) &&
!/^(unset|initial|inherit|will-change|auto|scroll-position|contents)$/i.test(part);
"<angle>": function(part) {
return part.type === "angle";
"<attachment>": "scroll | fixed | local",
// inset() = inset( <shape-arg>{1,4} [round <border-radius>]? )
// circle() = circle( [<shape-radius>]? [at <position>]? )
// ellipse() = ellipse( [<shape-radius>{2}]? [at <position>]? )
// polygon() = polygon( [<fill-rule>,]? [<shape-arg> <shape-arg>]# )
"<basic-shape>": "inset() | circle() | ellipse() | polygon()",
"<bg-image>": "<image> | <gradient> | none",
"none | hidden | dotted | dashed | solid | double | groove | " +
"ridge | inset | outset",
"<border-width>": "<length> | thin | medium | thick",
"<box>": "padding-box | border-box | content-box",
"<clip-source>": "<uri>",
"<color>": function(part) {
return part.type === "color" || String(part) === "transparent" || String(part) === "currentColor";
// The SVG <color> spec doesn't include "currentColor" or "transparent" as a color.
"<color-svg>": function(part) {
return part.type === "color";
"<content>": "content()",
// https://www.w3.org/TR/css3-sizing/#width-height-keywords
"fill-available | -moz-available | -webkit-fill-available | " +
"max-content | -moz-max-content | -webkit-max-content | " +
"min-content | -moz-min-content | -webkit-min-content | " +
"fit-content | -moz-fit-content | -webkit-fit-content",
"<feature-tag-value>": function(part) {
return part.type === "function" && /^[A-Z0-9]{4}$/i.test(part);
// custom() isn't actually in the spec
"blur() | brightness() | contrast() | custom() | " +
"drop-shadow() | grayscale() | hue-rotate() | invert() | " +
"opacity() | saturate() | sepia()",
"<flex-basis>": "<width>",
"<flex-direction>": "row | row-reverse | column | column-reverse",
"<flex-grow>": "<number>",
"<flex-shrink>": "<number>",
"<flex-wrap>": "nowrap | wrap | wrap-reverse",
"<absolute-size> | <relative-size> | <length> | <percentage>",
"normal | ultra-condensed | extra-condensed | condensed | " +
"semi-condensed | semi-expanded | expanded | extra-expanded | " +
"<font-style>": "normal | italic | oblique",
"small-caps | all-small-caps | petite-caps | all-petite-caps | " +
"unicase | titling-caps",
"<font-variant-css21>": "normal | small-caps",
"normal | bold | bolder | lighter | " +
"100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900",
"serif | sans-serif | cursive | fantasy | monospace",
"<geometry-box>": "<shape-box> | fill-box | stroke-box | view-box",
"<glyph-angle>": function(part) {
return part.type === "angle" && part.units === "deg";
"<gradient>": function(part) {
return part.type === "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?(?:repeating\-)?(?:radial\-|linear\-)?gradient/i.test(part);
"cielab() | cielch() | cielchab() | " +
"icc-color() | icc-named-color()",
"<ident>": function(part) {
return part.type === "identifier" || part.wasIdent;
"<ident-not-generic-family>": function(part) {
return this["<ident>"](part) && !this["<generic-family>"](part);
"<integer>": function(part) {
return part.type === "integer";
"<length>": function(part) {
if (part.type === "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?calc/i.test(part)) {
return part.type === "length" || part.type === "number" || part.type === "integer" || String(part) === "0";
"<line>": function(part) {
return part.type === "integer";
"<line-height>": "<number> | <length> | <percentage> | normal",
"<margin-width>": "<length> | <percentage> | auto",
"<miterlimit>": function(part) {
return this["<number>"](part) && part.value >= 1;
"<nonnegative-length-or-percentage>": function(part) {
return (this["<length>"](part) || this["<percentage>"](part)) &&
(String(part) === "0" || part.type === "function" || (part.value) >= 0);
"<nonnegative-number-or-percentage>": function(part) {
return (this["<number>"](part) || this["<percentage>"](part)) &&
(String(part) === "0" || part.type === "function" || (part.value) >= 0);
"<number>": function(part) {
return part.type === "number" || this["<integer>"](part);
"<opacity-value>": function(part) {
return this["<number>"](part) && part.value >= 0 && part.value <= 1;
"<padding-width>": "<nonnegative-length-or-percentage>",
"<percentage>": function(part) {
return part.type === "percentage" || String(part) === "0";
"<relative-size>": "smaller | larger",
"<shape>": "rect() | inset-rect()",
"<shape-box>": "<box> | margin-box",
"<single-animation-direction>":
"normal | reverse | alternate | alternate-reverse",
"<single-animation-name>": function(part) {
return this["<ident>"](part) &&
/^-?[a-z_][-a-z0-9_]+$/i.test(part) &&
!/^(none|unset|initial|inherit)$/i.test(part);
"<string>": function(part) {
return part.type === "string";
"<time>": function(part) {
return part.type === "time";
"<uri>": function(part) {
return part.type === "uri";
"<width>": "<margin-width>"
"[ [ left-side | far-left | left | center-left | center | " +
"center-right | right | far-right | right-side ] || behind ]" +
"leftwards | rightwards",
"<bg-position>": "<position>#",
"[ <length> | <percentage> | auto ]{1,2} | cover | contain",
// [<number> | <percentage>]{1,4} && fill?
// *but* fill can appear between any of the numbers
Matcher.many([true /* first element is required */],
Matcher.cast("<nonnegative-number-or-percentage>"),
Matcher.cast("<nonnegative-number-or-percentage>"),
Matcher.cast("<nonnegative-number-or-percentage>"),
Matcher.cast("<nonnegative-number-or-percentage>"),
"<nonnegative-length-or-percentage>{1,4} " +
"[ / <nonnegative-length-or-percentage>{1,4} ]?",
"<box-shadow>": "none | <shadow>#",
"<clip-path>": "<basic-shape> || <geometry-box>",
// "list of comma and/or white space separated <length>s and
// <percentage>s". There is a non-negative constraint.
Matcher.cast("<nonnegative-length-or-percentage>")
.braces(1, Infinity, "#", Matcher.cast(",").question()),
"<string> | <ident-not-generic-family> <ident>*",
"<filter-function-list>": "[ <filter-function> | <uri> ]+",
// https://www.w3.org/TR/2014/WD-css-flexbox-1-20140325/#flex-property
"none | [ <flex-grow> <flex-shrink>? || <flex-basis> ]",
"<font-family>": "[ <generic-family> | <family-name> ]#",
"[ <font-style> || <font-variant-css21> || " +
"<font-weight> || <font-stretch> ]? <font-size> " +
"[ / <line-height> ]? <font-family>",
"<font-variant-alternates>":
// stylistic(<feature-value-name>)
// styleset(<feature-value-name> #)
// character-variant(<feature-value-name> #)
"character-variant() || " +
// swash(<feature-value-name>)
// ornaments(<feature-value-name>)
// annotation(<feature-value-name>)
"<font-variant-ligatures>":
"[ common-ligatures | no-common-ligatures ] || " +
// <discretionary-lig-values>
"[ discretionary-ligatures | no-discretionary-ligatures ] || " +
// <historical-lig-values>
"[ historical-ligatures | no-historical-ligatures ] || " +
// <contextual-alt-values>
"[ contextual | no-contextual ]",
"<font-variant-numeric>":
// <numeric-figure-values>
"[ lining-nums | oldstyle-nums ] || " +
// <numeric-spacing-values>
"[ proportional-nums | tabular-nums ] || " +
// <numeric-fraction-values>
"[ diagonal-fractions | stacked-fractions ] || " +
"ordinal || slashed-zero",
"<font-variant-east-asian>":
// <east-asian-variant-values>
"[ jis78 | jis83 | jis90 | jis04 | simplified | traditional ] || " +
// <east-asian-width-values>
"[ full-width | proportional-width ] || " +
// Note that <color> here is "as defined in the SVG spec", which
// is more restrictive that the <color> defined in the CSS spec.
// none | currentColor | <color> [<icccolor>]? |
// <funciri> [ none | currentColor | <color> [<icccolor>]? ]?
"<paint>": "<paint-basic> | <uri> <paint-basic>?",
// Helper definition for <paint> above.
"<paint-basic>": "none | currentColor | <color-svg> <icccolor>?",
// Because our `alt` combinator is ordered, we need to test these
// in order from longest possible match to shortest.
"[ center | [ left | right ] [ <percentage> | <length> ]? ] && " +
"[ center | [ top | bottom ] [ <percentage> | <length> ]? ]" +
"[ left | center | right | <percentage> | <length> ] " +
"[ top | center | bottom | <percentage> | <length> ]" +
"[ left | center | right | top | bottom | <percentage> | <length> ]",
"repeat-x | repeat-y | [ repeat | space | round | no-repeat ]{1,2}",
//inset? && [ <length>{2,4} && <color>? ]
Matcher.many([true /* length is required */],
Matcher.cast("<length>").braces(2, 4), "inset", "<color>"),
"<text-decoration-color>":
"<text-decoration-line>":
"none | [ underline || overline || line-through || blink ]",
"<text-decoration-style>":
"solid | double | dotted | dashed | wavy",
"auto | <animateable-feature>#",
//[ <length> | <percentage> ] [ <length> | <percentage> ]?
"[ <length> | <percentage> ]{1,2}"
Object.keys(ValidationTypes.simple).forEach(function(nt) {
var rule = ValidationTypes.simple[nt];
if (typeof rule === "string") {
ValidationTypes.simple[nt] = function(part) {
return ValidationTypes.isLiteral(part, rule);
Object.keys(ValidationTypes.complex).forEach(function(nt) {
var rule = ValidationTypes.complex[nt];
if (typeof rule === "string") {
ValidationTypes.complex[nt] = Matcher.parse(rule);
// Because this is defined relative to other complex validation types,
// we need to define it *after* the rest of the types are initialized.
ValidationTypes.complex["<font-variant>"] =
Matcher.oror({ expand: "<font-variant-ligatures>" },
{ expand: "<font-variant-alternates>" },
{ expand: "<font-variant-numeric>" },
{ expand: "<font-variant-east-asian>" });
},{"./Matcher":3}],22:[function(require,module,exports){
Colors : require("./Colors"),
Combinator : require("./Combinator"),
Parser : require("./Parser"),
PropertyName : require("./PropertyName"),
PropertyValue : require("./PropertyValue"),
PropertyValuePart : require("./PropertyValuePart"),
Matcher : require("./Matcher"),
MediaFeature : require("./MediaFeature"),
MediaQuery : require("./MediaQuery"),
Selector : require("./Selector"),
SelectorPart : require("./SelectorPart"),
SelectorSubPart : require("./SelectorSubPart"),
Specificity : require("./Specificity"),
TokenStream : require("./TokenStream"),
Tokens : require("./Tokens"),
ValidationError : require("./ValidationError")
},{"./Colors":1,"./Combinator":2,"./Matcher":3,"./MediaFeature":4,"./MediaQuery":5,"./Parser":6,"./PropertyName":8,"./PropertyValue":9,"./PropertyValuePart":11,"./Selector":13,"./SelectorPart":14,"./SelectorSubPart":15,"./Specificity":16,"./TokenStream":17,"./Tokens":18,"./ValidationError":20}],23:[function(require,module,exports){
module.exports = EventTarget;
* A generic base to inherit from for any object
* that needs event handling.
* The array of listeners for various events.
this._listeners = Object.create(null);
EventTarget.prototype = {
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.
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.
if (typeof event === "string") {
if (typeof event.target !== "undefined") {
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.
removeListener: function(type, listener) {
if (this._listeners[type]) {
var listeners = this._listeners[type];
for (var i=0, len=listeners.length; i < len; i++) {
if (listeners[i] === listener) {