_import: function(emit) {
* [STRING|URI] S* media_query_list? ';' S*
var tokenStream = this._tokenStream,
tokenStream.mustMatch(Tokens.IMPORT_SYM);
importToken = tokenStream.token();
tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
uri = tokenStream.token().value.replace(/^(?:url\()?["']?([^"']+?)["']?\)?$/, "$1");
mediaList = this._media_query_list();
//must end with a semicolon
tokenStream.mustMatch(Tokens.SEMICOLON);
line: importToken.startLine,
col: importToken.startCol
_namespace: function(emit) {
* : NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
var tokenStream = this._tokenStream,
tokenStream.mustMatch(Tokens.NAMESPACE_SYM);
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
//it's a namespace prefix - no _namespace_prefix() method because it's just an IDENT
if (tokenStream.match(Tokens.IDENT)) {
prefix = tokenStream.token().value;
tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
/*if (!tokenStream.match(Tokens.STRING)){
tokenStream.mustMatch(Tokens.URI);
uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1");
//must end with a semicolon
tokenStream.mustMatch(Tokens.SEMICOLON);
_supports: function(emit) {
* : SUPPORTS_SYM S* supports_condition S* group_rule_body
var tokenStream = this._tokenStream,
if (tokenStream.match(Tokens.SUPPORTS_SYM)) {
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
this._supports_condition();
tokenStream.mustMatch(Tokens.LBRACE);
tokenStream.mustMatch(Tokens.RBRACE);
_supports_condition: function() {
* : supports_negation | supports_conjunction | supports_disjunction |
* supports_condition_in_parens
var tokenStream = this._tokenStream,
if (tokenStream.match(Tokens.IDENT)) {
ident = tokenStream.token().value.toLowerCase();
tokenStream.mustMatch(Tokens.S);
this._supports_condition_in_parens();
this._supports_condition_in_parens();
while (tokenStream.peek() === Tokens.IDENT) {
ident = tokenStream.LT(1).value.toLowerCase();
if (ident === "and" || ident === "or") {
tokenStream.mustMatch(Tokens.IDENT);
this._supports_condition_in_parens();
_supports_condition_in_parens: function() {
* supports_condition_in_parens
* : ( '(' S* supports_condition S* ')' ) | supports_declaration_condition |
var tokenStream = this._tokenStream,
if (tokenStream.match(Tokens.LPAREN)) {
if (tokenStream.match(Tokens.IDENT)) {
// look ahead for not keyword, if not given, continue with declaration condition.
ident = tokenStream.token().value.toLowerCase();
this._supports_condition();
tokenStream.mustMatch(Tokens.RPAREN);
this._supports_declaration_condition(false);
this._supports_condition();
tokenStream.mustMatch(Tokens.RPAREN);
this._supports_declaration_condition();
_supports_declaration_condition: function(requireStartParen) {
* supports_declaration_condition
* : '(' S* declaration ')'
var tokenStream = this._tokenStream;
if (requireStartParen !== false) {
tokenStream.mustMatch(Tokens.LPAREN);
tokenStream.mustMatch(Tokens.RPAREN);
* : MEDIA_SYM S* media_query_list S* '{' S* ruleset* '}' S*
var tokenStream = this._tokenStream,
tokenStream.mustMatch(Tokens.MEDIA_SYM);
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
mediaList = this._media_query_list();
tokenStream.mustMatch(Tokens.LBRACE);
if (tokenStream.peek() === Tokens.PAGE_SYM) {
} else if (tokenStream.peek() === Tokens.FONT_FACE_SYM) {
} else if (tokenStream.peek() === Tokens.VIEWPORT_SYM) {
} else if (tokenStream.peek() === Tokens.DOCUMENT_SYM) {
} else if (tokenStream.peek() === Tokens.SUPPORTS_SYM) {
} else if (tokenStream.peek() === Tokens.MEDIA_SYM) {
} else if (!this._ruleset()) {
tokenStream.mustMatch(Tokens.RBRACE);
_media_query_list: function() {
* : S* [media_query [ ',' S* media_query ]* ]?
var tokenStream = this._tokenStream,
if (tokenStream.peek() === Tokens.IDENT || tokenStream.peek() === Tokens.LPAREN) {
mediaList.push(this._media_query());
while (tokenStream.match(Tokens.COMMA)) {
mediaList.push(this._media_query());
* Note: "expression" in the grammar maps to the _media_expression
_media_query: function() {
* : [ONLY | NOT]? S* media_type S* [ AND S* expression ]*
* | expression [ AND S* expression ]*
var tokenStream = this._tokenStream,
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") {
token = tokenStream.token();
if (tokenStream.peek() === Tokens.IDENT) {
type = this._media_type();
token = tokenStream.token();
} else if (tokenStream.peek() === Tokens.LPAREN) {
token = tokenStream.LT(1);
expressions.push(this._media_expression());
if (type === null && expressions.length === 0) {
while (tokenStream.match(Tokens.IDENT)) {
if (tokenStream.token().value.toLowerCase() !== "and") {
this._unexpectedToken(tokenStream.token());
expressions.push(this._media_expression());
return new MediaQuery(ident, type, expressions, token.startLine, token.startCol);
_media_type: function() {
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
_media_expression: function() {
* : '(' S* media_feature S* [ ':' S* expr ]? ')' S*
var tokenStream = this._tokenStream,
tokenStream.mustMatch(Tokens.LPAREN);
feature = this._media_feature();
if (tokenStream.match(Tokens.COLON)) {
token = tokenStream.LT(1);
expression = this._expression();
tokenStream.mustMatch(Tokens.RPAREN);
return new MediaFeature(feature, expression ? new SyntaxUnit(expression, token.startLine, token.startCol) : null);
_media_feature: function() {
var tokenStream = this._tokenStream;
tokenStream.mustMatch(Tokens.IDENT);
return SyntaxUnit.fromToken(tokenStream.token());
* PAGE_SYM S* IDENT? pseudo_page? S*
* '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
var tokenStream = this._tokenStream,
tokenStream.mustMatch(Tokens.PAGE_SYM);
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
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();
this._readDeclarations(true, true);
* margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S*
var tokenStream = this._tokenStream,