acc.push(Matcher.cast(v));
required = ms.map(function() {
var result = new Matcher(function(expression) {
var seen = [], max = 0, pass = 0;
var success = function(matchCount) {
max = Math.max(matchCount, max);
return matchCount === ms.length;
return matchCount === max;
var tryMatch = function(matchCount) {
for (var i = 0; i < ms.length; i++) {
if (ms[i].match(expression)) {
// Increase matchCount iff this was a required element
// (or if all the elements are optional)
if (tryMatch(matchCount + ((required === false || required[i]) ? 1 : 0))) {
// Backtrack: try *not* matching using this rule, and
// let's see if it leads to a better overall match.
return success(matchCount);
// Couldn't get a complete match, retrace our steps to make the
// match with the maximum # of required elements.
if (required === false) {
// Use finer-grained specification of which matchers are required.
for (var i = 0; i < ms.length; i++) {
if (required[i] && !seen[i]) {
var p = required === false ? Matcher.prec.OROR : Matcher.prec.ANDAND;
var s = ms.map(function(m, i) {
if (required !== false && !required[i]) {
return m.toString(Matcher.prec.MOD) + "?";
}).join(required === false ? " || " : " && ");
* Create a matcher for two or more options, where all options are
* mandatory but they may appear in any order.
Matcher.andand = function() {
var args = Array.prototype.slice.call(arguments);
return Matcher.many.apply(Matcher, args);
* Create a matcher for two or more options, where options are
* optional and may appear in any order, but at least one must be
Matcher.oror = function() {
var args = Array.prototype.slice.call(arguments);
return Matcher.many.apply(Matcher, args);
/** Instance methods on Matchers. */
// These are expected to be overridden in every instance.
match: function() { throw new Error("unimplemented"); },
toString: function() { throw new Error("unimplemented"); },
// This returns a standalone function to do the matching.
func: function() { return this.match.bind(this); },
then: function(m) { return Matcher.seq(this, m); },
or: function(m) { return Matcher.alt(this, m); },
andand: function(m) { return Matcher.many(true, this, m); },
oror: function(m) { return Matcher.many(false, this, m); },
// Component value multipliers
star: function() { return this.braces(0, Infinity, "*"); },
plus: function() { return this.braces(1, Infinity, "+"); },
question: function() { return this.braces(0, 1, "?"); },
return this.braces(1, Infinity, "#", Matcher.cast(","));
braces: function(min, max, marker, optSep) {
var m1 = this, m2 = optSep ? optSep.then(this) : this;
marker = "{" + min + "," + max + "}";
return new Matcher(function(expression) {
for (i = 0; i < max; i++) {
result = m2.match(expression);
result = m1.match(expression);
return m1.toString(Matcher.prec.MOD) + marker;
},{"../util/StringReader":24,"../util/SyntaxError":25,"./ValidationTypes":21}],4:[function(require,module,exports){
module.exports = MediaFeature;
var SyntaxUnit = require("../util/SyntaxUnit");
var Parser = require("./Parser");
* Represents a media feature, such as max-width:500.
* @namespace parserlib.css
* @extends parserlib.util.SyntaxUnit
* @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, Parser.MEDIA_FEATURE_TYPE);
* The name of the media feature
* The value for the feature or null if there is none.
MediaFeature.prototype = new SyntaxUnit();
MediaFeature.prototype.constructor = MediaFeature;
},{"../util/SyntaxUnit":26,"./Parser":6}],5:[function(require,module,exports){
module.exports = MediaQuery;
var SyntaxUnit = require("../util/SyntaxUnit");
var Parser = require("./Parser");
* Represents an individual media query.
* @namespace parserlib.css
* @extends parserlib.util.SyntaxUnit
* @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 : "") + (mediaType && features.length > 0 ? " and " : "") + features.join(" and "), line, col, Parser.MEDIA_QUERY_TYPE);
* The media modifier ("not" or "only")
this.modifier = modifier;
* The mediaType (i.e., "print")
this.mediaType = mediaType;
* The parts that make up the selector.
this.features = features;
MediaQuery.prototype = new SyntaxUnit();
MediaQuery.prototype.constructor = MediaQuery;
},{"../util/SyntaxUnit":26,"./Parser":6}],6:[function(require,module,exports){
var EventTarget = require("../util/EventTarget");
var SyntaxError = require("../util/SyntaxError");
var SyntaxUnit = require("../util/SyntaxUnit");
var Combinator = require("./Combinator");
var MediaFeature = require("./MediaFeature");
var MediaQuery = require("./MediaQuery");
var PropertyName = require("./PropertyName");
var PropertyValue = require("./PropertyValue");
var PropertyValuePart = require("./PropertyValuePart");
var Selector = require("./Selector");
var SelectorPart = require("./SelectorPart");
var SelectorSubPart = require("./SelectorSubPart");
var TokenStream = require("./TokenStream");
var Tokens = require("./Tokens");
var Validation = require("./Validation");
* @namespace parserlib.css
* @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
function Parser(options) {
//inherit event functionality
this.options = options || {};
this._tokenStream = null;
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
//instance constants - yuck
PROPERTY_VALUE_PART_TYPE : 6,
SELECTOR_SUB_PART_TYPE : 9,
//-----------------------------------------------------------------
//-----------------------------------------------------------------
_stylesheet: function() {
* : [ CHARSET_SYM S* STRING S* ';' ]?
* [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
* [ namespace [S|CDO|CDC]* ]*
* [ [ ruleset | media | page | font_face | keyframes_rule | supports_rule ] [S|CDO|CDC]* ]*
var tokenStream = this._tokenStream,
this.fire("startstylesheet");
//try to read character set
//try to read imports - may be more than one
while (tokenStream.peek() === Tokens.IMPORT_SYM) {
//try to read namespaces - may be more than one
while (tokenStream.peek() === Tokens.NAMESPACE_SYM) {
while (tt > Tokens.EOF) {
case Tokens.FONT_FACE_SYM:
case Tokens.KEYFRAMES_SYM:
case Tokens.VIEWPORT_SYM:
case Tokens.DOCUMENT_SYM:
case Tokens.SUPPORTS_SYM:
case Tokens.UNKNOWN_SYM: //unknown @ rule
if (!this.options.strict) {
message: "Unknown @ rule: " + tokenStream.LT(0).value + ".",
line: tokenStream.LT(0).startLine,
col: tokenStream.LT(0).startCol
while (tokenStream.advance([Tokens.LBRACE, Tokens.RBRACE]) === Tokens.LBRACE) {
count++; //keep track of nesting depth
tokenStream.advance([Tokens.RBRACE]);
//not a syntax error, rethrow it
throw new SyntaxError("Unknown @ rule.", tokenStream.LT(0).startLine, tokenStream.LT(0).startCol);
//error handling for known issues
token = tokenStream.LT(1);
throw new SyntaxError("@charset not allowed here.", token.startLine, token.startCol);
token = tokenStream.LT(1);
throw new SyntaxError("@import not allowed here.", token.startLine, token.startCol);
case Tokens.NAMESPACE_SYM:
token = tokenStream.LT(1);
throw new SyntaxError("@namespace not allowed here.", token.startLine, token.startCol);
tokenStream.get(); //get the last token
this._unexpectedToken(tokenStream.token());
if (ex instanceof SyntaxError && !this.options.strict) {
this._unexpectedToken(tokenStream.token());
this.fire("endstylesheet");
_charset: function(emit) {
var tokenStream = this._tokenStream,
if (tokenStream.match(Tokens.CHARSET_SYM)) {
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
tokenStream.mustMatch(Tokens.STRING);
token = tokenStream.token();
tokenStream.mustMatch(Tokens.SEMICOLON);