lib/vendor/csslint-rhino.js in css_lint-0.9.10.1 vs lib/vendor/csslint-rhino.js in css_lint-0.10.0.0
- old
+ new
@@ -1,8 +1,8 @@
/*!
CSSLint
-Copyright (c) 2011 Nicole Sullivan and Nicholas C. Zakas. All rights reserved.
+Copyright (c) 2013 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
@@ -19,11 +19,12 @@
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: 17-January-2013 10:55:01 */
+/* Build: v0.10.0 15-August-2013 01:07:22 */
+var exports = exports || {};
var CSSLint = (function(){
/*!
Parser-Lib
Copyright (c) 2009-2011 Nicholas C. Zakas. All rights reserved.
@@ -44,11 +45,11 @@
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.
*/
-/* Version v0.2.2, Build time: 17-January-2013 10:26:34 */
+/* Version v0.2.3, Build time: 19-June-2013 11:16:15 */
var parserlib = {};
(function(){
/**
@@ -954,11 +955,11 @@
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.
*/
-/* Version v0.2.2, Build time: 17-January-2013 10:26:34 */
+/* Version v0.2.3, Build time: 19-June-2013 11:16:15 */
(function(){
var EventTarget = parserlib.util.EventTarget,
TokenStreamBase = parserlib.util.TokenStreamBase,
StringReader = parserlib.util.StringReader,
SyntaxError = parserlib.util.SyntaxError,
@@ -1294,126 +1295,130 @@
Parser.prototype = function(){
var proto = new EventTarget(), //new prototype
prop,
additions = {
-
+
//restore constructor
constructor: Parser,
-
+
//instance constants - yuck
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,
-
+ 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]* ]*
* ;
- */
-
+ */
+
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 {
-
+
switch(tt){
case Tokens.MEDIA_SYM:
this._media();
this._skipCruft();
break;
case Tokens.PAGE_SYM:
- this._page();
+ this._page();
this._skipCruft();
- break;
+ break;
case Tokens.FONT_FACE_SYM:
- this._font_face();
+ this._font_face();
this._skipCruft();
- break;
+ break;
case Tokens.KEYFRAMES_SYM:
- this._keyframes();
+ this._keyframes();
this._skipCruft();
- break;
+ break;
+ case Tokens.VIEWPORT_SYM:
+ this._viewport();
+ this._skipCruft();
+ break;
case Tokens.UNKNOWN_SYM: //unknown @ rule
tokenStream.get();
if (!this.options.strict){
-
+
//fire error event
this.fire({
type: "error",
error: null,
message: "Unknown @ rule: " + tokenStream.LT(0).value + ".",
line: tokenStream.LT(0).startLine,
col: tokenStream.LT(0).startCol
- });
-
+ });
+
//skip braces
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 {
//not a syntax error, rethrow it
throw new SyntaxError("Unknown @ rule.", tokenStream.LT(0).startLine, tokenStream.LT(0).startCol);
- }
+ }
break;
case Tokens.S:
this._readWhitespace();
break;
- default:
+ default:
if(!this._ruleset()){
-
+
//error handling for known issues
switch(tt){
case Tokens.CHARSET_SYM:
token = tokenStream.LT(1);
this._charset(false);
@@ -1428,240 +1433,242 @@
throw new SyntaxError("@namespace not allowed here.", token.startLine, token.startCol);
default:
tokenStream.get(); //get the last token
this._unexpectedToken(tokenStream.token());
}
-
+
}
}
} catch(ex) {
if (ex instanceof SyntaxError && !this.options.strict){
this.fire({
type: "error",
error: ex,
message: ex.message,
line: ex.line,
col: ex.col
- });
+ });
} else {
throw ex;
}
}
-
+
tt = tokenStream.peek();
}
-
+
if (tt != Tokens.EOF){
this._unexpectedToken(tokenStream.token());
}
-
+
this.fire("endstylesheet");
},
-
+
_charset: function(emit){
var tokenStream = this._tokenStream,
charset,
token,
line,
col;
-
+
if (tokenStream.match(Tokens.CHARSET_SYM)){
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
-
+
this._readWhitespace();
tokenStream.mustMatch(Tokens.STRING);
-
+
token = tokenStream.token();
charset = token.value;
-
+
this._readWhitespace();
tokenStream.mustMatch(Tokens.SEMICOLON);
-
+
if (emit !== false){
- this.fire({
+ this.fire({
type: "charset",
charset:charset,
line: line,
col: col
});
}
- }
+ }
},
-
+
_import: function(emit){
/*
* import
* : IMPORT_SYM S*
* [STRING|URI] S* media_query_list? ';' S*
- */
-
+ */
+
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");
+ 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();
-
+
if (emit !== false){
this.fire({
type: "import",
uri: uri,
media: mediaList,
line: importToken.startLine,
col: importToken.startCol
});
}
-
+
},
-
+
_namespace: function(emit){
/*
* namespace
* : NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
- */
-
+ */
+
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");
+ uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1");
this._readWhitespace();
//must end with a semicolon
tokenStream.mustMatch(Tokens.SEMICOLON);
this._readWhitespace();
-
+
if (emit !== false){
this.fire({
type: "namespace",
prefix: prefix,
uri: uri,
line: line,
col: col
});
}
-
- },
-
+
+ },
+
_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;
-
- this._readWhitespace();
+ this._readWhitespace();
+
mediaList = this._media_query_list();
tokenStream.mustMatch(Tokens.LBRACE);
this._readWhitespace();
-
+
this.fire({
type: "startmedia",
media: mediaList,
line: line,
col: col
});
-
+
while(true) {
if (tokenStream.peek() == Tokens.PAGE_SYM){
this._page();
+ } else if (tokenStream.peek() == Tokens.FONT_FACE_SYM){
+ this._font_face();
} else if (!this._ruleset()){
break;
- }
+ }
}
-
+
tokenStream.mustMatch(Tokens.RBRACE);
this._readWhitespace();
-
+
this.fire({
type: "endmedia",
media: mediaList,
line: line,
col: col
});
- },
-
+ },
+
//CSS3 Media Queries
_media_query_list: function(){
/*
* media_query_list
* : S* [media_query [ ',' S* media_query ]* ]?
* ;
*/
var tokenStream = this._tokenStream,
mediaList = [];
-
-
+
+
this._readWhitespace();
-
+
if (tokenStream.peek() == Tokens.IDENT || tokenStream.peek() == Tokens.LPAREN){
mediaList.push(this._media_query());
}
-
+
while(tokenStream.match(Tokens.COMMA)){
this._readWhitespace();
mediaList.push(this._media_query());
}
-
+
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 ]*
@@ -1671,46 +1678,46 @@
var tokenStream = this._tokenStream,
type = null,
ident = null,
token = null,
expressions = [];
-
+
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;
} else {
token = tokenStream.token();
}
}
-
+
this._readWhitespace();
-
+
if (tokenStream.peek() == Tokens.IDENT){
type = this._media_type();
if (token === null){
token = tokenStream.token();
}
} else if (tokenStream.peek() == Tokens.LPAREN){
if (token === null){
token = tokenStream.LT(1);
}
expressions.push(this._media_expression());
- }
-
+ }
+
if (type === null && expressions.length === 0){
return null;
- } else {
+ } else {
this._readWhitespace();
while (tokenStream.match(Tokens.IDENT)){
if (tokenStream.token().value.toLowerCase() != "and"){
this._unexpectedToken(tokenStream.token());
}
-
+
this._readWhitespace();
expressions.push(this._media_expression());
}
}
@@ -1722,11 +1729,11 @@
/*
* media_type
* : IDENT
* ;
*/
- return this._media_feature();
+ return this._media_feature();
},
/**
* Note: in CSS3 Media Queries, this is called "expression".
* Renamed here to avoid conflict with CSS3 Selectors
@@ -1743,99 +1750,99 @@
*/
var tokenStream = this._tokenStream,
feature = null,
token,
expression = null;
-
+
tokenStream.mustMatch(Tokens.LPAREN);
-
+
feature = this._media_feature();
this._readWhitespace();
-
+
if (tokenStream.match(Tokens.COLON)){
this._readWhitespace();
token = tokenStream.LT(1);
expression = this._expression();
}
-
+
tokenStream.mustMatch(Tokens.RPAREN);
this._readWhitespace();
- return new MediaFeature(feature, (expression ? new SyntaxUnit(expression, token.startLine, token.startCol) : null));
+ 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());
+
+ return SyntaxUnit.fromToken(tokenStream.token());
},
-
+
//CSS3 Paged Media
_page: function(){
/*
* page:
- * PAGE_SYM S* IDENT? pseudo_page? S*
+ * PAGE_SYM S* IDENT? pseudo_page? S*
* '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
* ;
- */
+ */
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;
-
+
this._readWhitespace();
-
+
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._readWhitespace();
-
+
this.fire({
type: "startpage",
id: identifier,
pseudo: pseudoPage,
line: line,
col: col
- });
+ });
- this._readDeclarations(true, true);
-
+ this._readDeclarations(true, true);
+
this.fire({
type: "endpage",
id: identifier,
pseudo: pseudoPage,
line: line,
col: col
- });
-
+ });
+
},
-
+
//CSS3 Paged Media
_margin: function(){
/*
* margin :
* margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S*
@@ -1847,234 +1854,267 @@
marginSym = this._margin_sym();
if (marginSym){
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
-
+
this.fire({
type: "startpagemargin",
margin: marginSym,
line: line,
col: col
- });
-
+ });
+
this._readDeclarations(true);
this.fire({
type: "endpagemargin",
margin: marginSym,
line: line,
col: col
- });
+ });
return true;
} else {
return false;
}
},
//CSS3 Paged Media
_margin_sym: function(){
-
+
/*
* margin_sym :
- * TOPLEFTCORNER_SYM |
- * TOPLEFT_SYM |
- * TOPCENTER_SYM |
- * TOPRIGHT_SYM |
+ * TOPLEFTCORNER_SYM |
+ * TOPLEFT_SYM |
+ * TOPCENTER_SYM |
+ * TOPRIGHT_SYM |
* TOPRIGHTCORNER_SYM |
- * BOTTOMLEFTCORNER_SYM |
- * BOTTOMLEFT_SYM |
- * BOTTOMCENTER_SYM |
+ * BOTTOMLEFTCORNER_SYM |
+ * BOTTOMLEFT_SYM |
+ * BOTTOMCENTER_SYM |
* BOTTOMRIGHT_SYM |
* BOTTOMRIGHTCORNER_SYM |
* LEFTTOP_SYM |
* LEFTMIDDLE_SYM |
* LEFTBOTTOM_SYM |
* RIGHTTOP_SYM |
* RIGHTMIDDLE_SYM |
- * RIGHTBOTTOM_SYM
+ * RIGHTBOTTOM_SYM
* ;
*/
-
+
var tokenStream = this._tokenStream;
-
+
if(tokenStream.match([Tokens.TOPLEFTCORNER_SYM, Tokens.TOPLEFT_SYM,
Tokens.TOPCENTER_SYM, Tokens.TOPRIGHT_SYM, Tokens.TOPRIGHTCORNER_SYM,
- Tokens.BOTTOMLEFTCORNER_SYM, Tokens.BOTTOMLEFT_SYM,
+ Tokens.BOTTOMLEFTCORNER_SYM, Tokens.BOTTOMLEFT_SYM,
Tokens.BOTTOMCENTER_SYM, Tokens.BOTTOMRIGHT_SYM,
- Tokens.BOTTOMRIGHTCORNER_SYM, Tokens.LEFTTOP_SYM,
+ Tokens.BOTTOMRIGHTCORNER_SYM, Tokens.LEFTTOP_SYM,
Tokens.LEFTMIDDLE_SYM, Tokens.LEFTBOTTOM_SYM, Tokens.RIGHTTOP_SYM,
Tokens.RIGHTMIDDLE_SYM, Tokens.RIGHTBOTTOM_SYM]))
{
- return SyntaxUnit.fromToken(tokenStream.token());
+ return SyntaxUnit.fromToken(tokenStream.token());
} else {
return null;
}
-
+
},
-
+
_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*
+ * : FONT_FACE_SYM S*
* '{' S* declaration [ ';' S* declaration ]* '}' S*
* ;
- */
+ */
var tokenStream = this._tokenStream,
line,
col;
-
+
//look for @page
tokenStream.mustMatch(Tokens.FONT_FACE_SYM);
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
-
+
this._readWhitespace();
this.fire({
type: "startfontface",
line: line,
col: col
- });
-
+ });
+
this._readDeclarations(true);
-
+
this.fire({
type: "endfontface",
line: line,
col: col
- });
+ });
},
+ _viewport: function(){
+ /*
+ * viewport
+ * : VIEWPORT_SYM S*
+ * '{' S* declaration? [ ';' S* declaration? ]* '}' S*
+ * ;
+ */
+ var tokenStream = this._tokenStream,
+ line,
+ col;
+
+ tokenStream.mustMatch(Tokens.VIEWPORT_SYM);
+ line = tokenStream.token().startLine;
+ col = tokenStream.token().startCol;
+
+ this._readWhitespace();
+
+ this.fire({
+ type: "startviewport",
+ line: line,
+ col: col
+ });
+
+ this._readDeclarations(true);
+
+ this.fire({
+ type: "endviewport",
+ line: line,
+ col: col
+ });
+
+ },
+
_operator: function(inFunction){
-
+
/*
* operator (outside function)
* : '/' S* | ',' S* | /( empty )/
* operator (inside function)
* : '/' S* | '+' S* | '*' S* | '-' S* /( empty )/
* ;
- */
-
+ */
+
var tokenStream = this._tokenStream,
token = null;
-
+
if (tokenStream.match([Tokens.SLASH, Tokens.COMMA]) ||
(inFunction && tokenStream.match([Tokens.PLUS, Tokens.STAR, Tokens.MINUS]))){
token = tokenStream.token();
this._readWhitespace();
- }
+ }
return token ? PropertyValuePart.fromToken(token) : null;
-
+
},
-
+
_combinator: function(){
-
+
/*
* combinator
* : PLUS S* | GREATER S* | TILDE S* | S+
* ;
- */
-
+ */
+
var tokenStream = this._tokenStream,
value = null,
token;
-
- if(tokenStream.match([Tokens.PLUS, Tokens.GREATER, Tokens.TILDE])){
+
+ if(tokenStream.match([Tokens.PLUS, Tokens.GREATER, Tokens.TILDE])){
token = tokenStream.token();
value = new Combinator(token.value, token.startLine, token.startCol);
this._readWhitespace();
}
-
+
return value;
},
-
+
_unary_operator: function(){
-
+
/*
* unary_operator
* : '-' | '+'
* ;
*/
-
+
var tokenStream = this._tokenStream;
-
+
if (tokenStream.match([Tokens.MINUS, Tokens.PLUS])){
return tokenStream.token().value;
} else {
return null;
- }
+ }
},
-
+
_property: function(){
-
+
/*
* property
* : IDENT S*
- * ;
+ * ;
*/
-
+
var tokenStream = this._tokenStream,
value = null,
hack = null,
tokenValue,
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();
hack = token.value;
line = token.startLine;
col = token.startCol;
}
-
+
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);
}
-
+
value = new PropertyName(tokenValue, hack, (line||token.startLine), (col||token.startCol));
this._readWhitespace();
}
-
+
return value;
},
-
+
//Augmented with CSS3 Selectors
_ruleset: function(){
/*
* ruleset
* : selectors_group
* '{' S* declaration? [ ';' S* declaration? ]* '}' S*
- * ;
- */
-
+ * ;
+ */
+
var tokenStream = this._tokenStream,
tt,
selectors;
@@ -2084,78 +2124,78 @@
*/
try {
selectors = this._selectors_group();
} catch (ex){
if (ex instanceof SyntaxError && !this.options.strict){
-
+
//fire error event
this.fire({
type: "error",
error: ex,
message: ex.message,
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){
-
+ if (selectors){
+
this.fire({
type: "startrule",
selectors: selectors,
line: selectors[0].line,
col: selectors[0].col
- });
-
- this._readDeclarations(true);
-
+ });
+
+ this._readDeclarations(true);
+
this.fire({
type: "endrule",
selectors: selectors,
line: selectors[0].line,
col: selectors[0].col
- });
-
+ });
+
}
-
+
return selectors;
-
+
},
//CSS3 Selectors
_selectors_group: function(){
-
- /*
+
+ /*
* selectors_group
* : selector [ COMMA S* selector ]*
* ;
- */
+ */
var tokenStream = this._tokenStream,
selectors = [],
selector;
-
+
selector = this._selector();
if (selector !== null){
-
+
selectors.push(selector);
while(tokenStream.match(Tokens.COMMA)){
this._readWhitespace();
selector = this._selector();
if (selector !== null){
@@ -2166,103 +2206,103 @@
}
}
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;
}
-
+
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(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()){
-
+ 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 (nextSelector === null){
if (combinator !== null){
this._unexpectedToken(tokenStream.LT(1));
}
} else {
-
+
if (combinator !== null){
selector.push(combinator);
} else {
selector.push(ws);
}
-
+
selector.push(nextSelector);
- }
+ }
} else {
break;
- }
-
+ }
+
}
} while(true);
-
+
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 = [
@@ -2281,72 +2321,72 @@
len = components.length,
component = null,
found = false,
line,
col;
-
-
+
+
//get starting line and column for the selector
line = tokenStream.LT(1).startLine;
col = tokenStream.LT(1).startCol;
-
+
elementName = this._type_selector();
if (!elementName){
elementName = this._universal();
}
-
+
if (elementName !== null){
selectorText += elementName;
- }
-
+ }
+
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 {
break;
}
} else {
i = 0;
modifiers.push(component);
- selectorText += component.toString();
+ selectorText += component.toString();
component = null;
}
}
-
+
return selectorText !== "" ?
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){
+
+ 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.
@@ -2355,111 +2395,111 @@
tokenStream.unget();
if (ns.length > 1){
tokenStream.unget();
}
}
-
+
return null;
- } else {
+ } else {
if (ns){
elementName.text = ns + elementName.text;
elementName.col -= ns.length;
}
return elementName;
}
},
-
+
//CSS3 Selectors
_class: function(){
/*
* class
* : '.' IDENT
* ;
- */
-
+ */
+
var tokenStream = this._tokenStream,
token;
-
+
if (tokenStream.match(Tokens.DOT)){
- tokenStream.mustMatch(Tokens.IDENT);
+ tokenStream.mustMatch(Tokens.IDENT);
token = tokenStream.token();
- return new SelectorSubPart("." + token.value, "class", token.startLine, token.startCol - 1);
+ return new SelectorSubPart("." + token.value, "class", token.startLine, token.startCol - 1);
} else {
return null;
}
-
+
},
-
+
//CSS3 Selectors
_element_name: function(){
/*
* element_name
* : IDENT
* ;
- */
-
+ */
+
var tokenStream = this._tokenStream,
token;
-
+
if (tokenStream.match(Tokens.IDENT)){
token = tokenStream.token();
- return new SelectorSubPart(token.value, "elementName", token.startLine, token.startCol);
-
+ return new SelectorSubPart(token.value, "elementName", token.startLine, token.startCol);
+
} else {
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])){
value += tokenStream.token().value;
}
-
+
tokenStream.mustMatch(Tokens.PIPE);
value += "|";
-
+
}
-
- return value.length ? value : null;
+
+ return value.length ? value : null;
},
-
+
//CSS3 Selectors
_universal: function(){
/*
* universal
* : [ namespace_prefix ]? '*'
- * ;
+ * ;
*/
var tokenStream = this._tokenStream,
value = "",
ns;
-
+
ns = this._namespace_prefix();
if(ns){
value += ns;
}
-
+
if(tokenStream.match(Tokens.STAR)){
value += "*";
}
-
+
return value.length ? value : null;
-
+
},
-
+
//CSS3 Selectors
_attrib: function(){
/*
* attrib
* : '[' S* [ namespace_prefix ]? IDENT S*
@@ -2468,140 +2508,140 @@
* SUBSTRINGMATCH |
* '=' |
* INCLUDES |
* DASHMATCH ] S* [ IDENT | STRING ] S*
* ]? ']'
- * ;
+ * ;
*/
-
+
var tokenStream = this._tokenStream,
value = null,
ns,
token;
-
+
if (tokenStream.match(Tokens.LBRACKET)){
token = tokenStream.token();
value = token.value;
value += this._readWhitespace();
-
+
ns = this._namespace_prefix();
-
+
if (ns){
value += ns;
}
-
+
tokenStream.mustMatch(Tokens.IDENT);
- value += tokenStream.token().value;
+ value += tokenStream.token().value;
value += this._readWhitespace();
-
+
if(tokenStream.match([Tokens.PREFIXMATCH, Tokens.SUFFIXMATCH, Tokens.SUBSTRINGMATCH,
Tokens.EQUALS, Tokens.INCLUDES, Tokens.DASHMATCH])){
-
- value += tokenStream.token().value;
+
+ value += tokenStream.token().value;
value += this._readWhitespace();
-
+
tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
- value += tokenStream.token().value;
+ value += tokenStream.token().value;
value += this._readWhitespace();
}
-
+
tokenStream.mustMatch(Tokens.RBRACKET);
-
+
return new SelectorSubPart(value + "]", "attribute", token.startLine, token.startCol);
} else {
return null;
}
},
-
+
//CSS3 Selectors
_pseudo: function(){
-
+
/*
* pseudo
* : ':' ':'? [ IDENT | functional_pseudo ]
- * ;
- */
-
+ * ;
+ */
+
var tokenStream = this._tokenStream,
pseudo = null,
colons = ":",
line,
col;
-
+
if (tokenStream.match(Tokens.COLON)){
-
+
if (tokenStream.match(Tokens.COLON)){
colons += ":";
}
-
+
if (tokenStream.match(Tokens.IDENT)){
pseudo = tokenStream.token().value;
line = tokenStream.token().startLine;
col = tokenStream.token().startCol - colons.length;
} else if (tokenStream.peek() == Tokens.FUNCTION){
line = tokenStream.LT(1).startLine;
col = tokenStream.LT(1).startCol - colons.length;
pseudo = this._functional_pseudo();
}
-
+
if (pseudo){
pseudo = new SelectorSubPart(colons + pseudo, "pseudo", line, col);
}
}
-
+
return pseudo;
},
-
+
//CSS3 Selectors
_functional_pseudo: function(){
/*
* functional_pseudo
* : FUNCTION S* expression ')'
* ;
- */
-
+ */
+
var tokenStream = this._tokenStream,
value = null;
-
+
if(tokenStream.match(Tokens.FUNCTION)){
value = tokenStream.token().value;
value += this._readWhitespace();
value += this._expression();
tokenStream.mustMatch(Tokens.RPAREN);
value += ")";
}
-
+
return value;
},
-
+
//CSS3 Selectors
_expression: function(){
/*
* expression
* : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
* ;
*/
-
+
var tokenStream = this._tokenStream,
value = "";
-
+
while(tokenStream.match([Tokens.PLUS, Tokens.MINUS, Tokens.DIMENSION,
Tokens.NUMBER, Tokens.STRING, Tokens.IDENT, Tokens.LENGTH,
Tokens.FREQ, Tokens.ANGLE, Tokens.TIME,
Tokens.RESOLUTION, Tokens.SLASH])){
-
+
value += tokenStream.token().value;
- value += this._readWhitespace();
+ value += this._readWhitespace();
}
-
+
return value.length ? value : null;
-
+
},
//CSS3 Selectors
_negation: function(){
- /*
+ /*
* negation
* : NOT S* negation_arg S* ')'
* ;
*/
@@ -2609,180 +2649,180 @@
line,
col,
value = "",
arg,
subpart = null;
-
+
if (tokenStream.match(Tokens.NOT)){
value = tokenStream.token().value;
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
value += this._readWhitespace();
arg = this._negation_arg();
value += arg;
value += this._readWhitespace();
tokenStream.match(Tokens.RPAREN);
value += tokenStream.token().value;
-
+
subpart = new SelectorSubPart(value, "not", line, col);
subpart.args.push(arg);
}
-
+
return subpart;
},
-
+
//CSS3 Selectors
- _negation_arg: function(){
+ _negation_arg: function(){
/*
* negation_arg
* : type_selector | universal | HASH | class | attrib | pseudo
- * ;
- */
-
+ * ;
+ */
+
var tokenStream = this._tokenStream,
args = [
this._type_selector,
this._universal,
function(){
return tokenStream.match(Tokens.HASH) ?
new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
- null;
+ null;
},
this._class,
this._attrib,
- this._pseudo
+ this._pseudo
],
arg = null,
i = 0,
len = args.length,
elementName,
line,
col,
part;
-
+
line = tokenStream.LT(1).startLine;
col = tokenStream.LT(1).startCol;
-
+
while(i < len && arg === null){
-
+
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 {
part = new SelectorPart(null, [arg], arg.toString(), line, col);
}
-
- return part;
+
+ return part;
},
-
+
_declaration: function(){
-
+
/*
* declaration
* : property ':' S* expr prio?
* | /( empty )/
- * ;
- */
-
+ * ;
+ */
+
var tokenStream = this._tokenStream,
property = null,
expr = null,
prio = null,
error = null,
invalid = null,
propertyName= "";
-
+
property = this._property();
if (property !== null){
tokenStream.mustMatch(Tokens.COLON);
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();
-
+
/*
* If hacks should be allowed, then only check the root
* property. If hacks should not be allowed, treat
* _property or *property as invalid properties.
*/
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",
property: property,
value: expr,
important: prio,
line: property.line,
col: property.col,
invalid: invalid
- });
-
+ });
+
return true;
} else {
return false;
}
},
-
+
_prio: function(){
/*
* prio
* : IMPORTANT_SYM S*
- * ;
+ * ;
*/
-
+
var tokenStream = this._tokenStream,
result = tokenStream.match(Tokens.IMPORTANT_SYM);
-
+
this._readWhitespace();
return result;
},
-
+
_expr: function(inFunction){
/*
* expr
* : term [ operator term ]*
* ;
*/
-
+
var tokenStream = this._tokenStream,
values = [],
//valueParts = [],
value = null,
operator = null;
-
+
value = this._term();
if (value !== null){
-
+
values.push(value);
-
+
do {
operator = this._operator(inFunction);
//if there's an operator, keep building up the value parts
if (operator){
@@ -2790,89 +2830,89 @@
} /*else {
//if there's not an operator, you have a full value
values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
valueParts = [];
}*/
-
+
value = this._term();
-
+
if (value === null){
break;
} else {
values.push(value);
}
} 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].line, values[0].col) : null;
},
-
- _term: function(){
-
+
+ _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
* ;
- */
-
+ */
+
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();
if (unary === null){
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])){
-
+
value = tokenStream.token().value;
if (unary === null){
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
}
this._readWhitespace();
} else {
-
+
//see if it's a color
token = this._hexcolor();
if (token === null){
-
+
//if there's no unary, get the start of the next token for line/col info
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){
@@ -2884,65 +2924,65 @@
/*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 = token.startLine;
col = token.startCol;
- }
+ }
}
-
- }
-
+
+ }
+
return value !== null ?
new PropertyValuePart(unary !== null ? unary + value : value, line, col) :
null;
-
+
},
-
+
_function: function(){
-
+
/*
* function
* : FUNCTION S* expr ')' S*
* ;
*/
-
+
var tokenStream = this._tokenStream,
functionText = null,
expr = null,
lt;
-
+
if (tokenStream.match(Tokens.FUNCTION)){
functionText = tokenStream.token().value;
this._readWhitespace();
expr = this._expr(true);
functionText += expr;
-
+
//START: Horrible hack in case it's an IE filter
if (this.options.ieFilters && tokenStream.peek() == Tokens.EQUALS){
do {
-
+
if (this._readWhitespace()){
functionText += tokenStream.token().value;
}
-
+
//might be second time in the loop
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;
-
+
//functionText += this._term();
lt = tokenStream.peek();
while(lt != Tokens.COMMA && lt != Tokens.S && lt != Tokens.RPAREN){
tokenStream.get();
functionText += tokenStream.token().value;
@@ -2950,264 +2990,264 @@
}
} while(tokenStream.match([Tokens.COMMA, Tokens.S]));
}
//END: Horrible Hack
-
- tokenStream.match(Tokens.RPAREN);
+
+ tokenStream.match(Tokens.RPAREN);
functionText += ")";
this._readWhitespace();
- }
-
+ }
+
return functionText;
- },
-
+ },
+
_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;
-
+
do {
-
+
if (this._readWhitespace()){
functionText += tokenStream.token().value;
}
-
+
//might be second time in the loop
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;
-
+
//functionText += this._term();
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);
+ } while(tokenStream.match([Tokens.COMMA, Tokens.S]));
+
+ tokenStream.match(Tokens.RPAREN);
functionText += ")";
this._readWhitespace();
- }
-
+ }
+
return functionText;
- },
-
+ },
+
_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 = null,
color;
-
+
if(tokenStream.match(Tokens.HASH)){
-
+
//need to do some validation here
-
+
token = tokenStream.token();
color = token.value;
if (!/#[a-f0-9]{3,6}/i.test(color)){
throw new SyntaxError("Expected a hex color but found '" + color + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
}
this._readWhitespace();
}
-
+
return token;
},
-
+
//-----------------------------------------------------------------
// Animations methods
//-----------------------------------------------------------------
-
+
_keyframes: function(){
-
+
/*
* keyframes:
* : KEYFRAMES_SYM S* keyframe_name S* '{' S* keyframe_rule* '}' {
* ;
*/
var tokenStream = this._tokenStream,
token,
tt,
name,
- prefix = "";
-
+ prefix = "";
+
tokenStream.mustMatch(Tokens.KEYFRAMES_SYM);
token = tokenStream.token();
if (/^@\-([^\-]+)\-/.test(token.value)) {
prefix = RegExp.$1;
}
-
+
this._readWhitespace();
name = this._keyframe_name();
-
+
this._readWhitespace();
tokenStream.mustMatch(Tokens.LBRACE);
-
+
this.fire({
type: "startkeyframes",
name: name,
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();
tt = tokenStream.peek();
- }
-
+ }
+
this.fire({
type: "endkeyframes",
name: name,
prefix: prefix,
line: token.startLine,
col: token.startCol
- });
-
+ });
+
this._readWhitespace();
- tokenStream.mustMatch(Tokens.RBRACE);
-
+ tokenStream.mustMatch(Tokens.RBRACE);
+
},
-
+
_keyframe_name: function(){
-
+
/*
* keyframe_name:
* : IDENT
* | STRING
* ;
*/
var tokenStream = this._tokenStream,
token;
tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
- return SyntaxUnit.fromToken(tokenStream.token());
+ return SyntaxUnit.fromToken(tokenStream.token());
},
-
+
_keyframe_rule: function(){
-
+
/*
* keyframe_rule:
- * : key_list S*
+ * : key_list S*
* '{' S* declaration [ ';' S* declaration ]* '}' S*
* ;
*/
var tokenStream = this._tokenStream,
token,
keyList = this._key_list();
-
+
this.fire({
type: "startkeyframerule",
keys: keyList,
line: keyList[0].line,
col: keyList[0].col
- });
-
- this._readDeclarations(true);
-
+ });
+
+ this._readDeclarations(true);
+
this.fire({
type: "endkeyframerule",
keys: keyList,
line: keyList[0].line,
col: keyList[0].col
- });
-
+ });
+
},
-
+
_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();
-
+
while(tokenStream.match(Tokens.COMMA)){
this._readWhitespace();
keyList.push(this._key());
this._readWhitespace();
}
return keyList;
},
-
+
_key: function(){
/*
* There is a restriction that IDENT can be only "from" or "to".
*
* key
* : PERCENTAGE
* | IDENT
* ;
*/
-
+
var tokenStream = this._tokenStream,
token;
-
+
if (tokenStream.match(Tokens.PERCENTAGE)){
return SyntaxUnit.fromToken(tokenStream.token());
} else if (tokenStream.match(Tokens.IDENT)){
- token = tokenStream.token();
-
+ token = tokenStream.token();
+
if (/from|to/i.test(token.value)){
return SyntaxUnit.fromToken(token);
}
-
+
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
@@ -3236,261 +3276,262 @@
* 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.
+ * A semicolon is only necessary following a declaration is there's another declaration
+ * or margin afterwards.
*/
var tokenStream = this._tokenStream,
tt;
-
+
this._readWhitespace();
-
+
if (checkStart){
- tokenStream.mustMatch(Tokens.LBRACE);
+ tokenStream.mustMatch(Tokens.LBRACE);
}
-
+
this._readWhitespace();
try {
-
+
while(true){
-
+
if (tokenStream.match(Tokens.SEMICOLON) || (readMargins && this._margin())){
//noop
} else if (this._declaration()){
if (!tokenStream.match(Tokens.SEMICOLON)){
break;
}
} else {
break;
}
-
+
//if ((!this._margin() && !this._declaration()) || !tokenStream.match(Tokens.SEMICOLON)){
// break;
//}
this._readWhitespace();
}
-
+
tokenStream.mustMatch(Tokens.RBRACE);
this._readWhitespace();
-
+
} catch (ex) {
if (ex instanceof SyntaxError && !this.options.strict){
-
+
//fire error event
this.fire({
type: "error",
error: ex,
message: ex.message,
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);
+ this._readDeclarations(false, readMargins);
} else if (tt != Tokens.RBRACE){
//if there's a right brace, the rule is finished so don't do anything
//otherwise, rethrow the error because it wasn't handled properly
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,
ws = "";
-
+
while(tokenStream.match(Tokens.S)){
ws += tokenStream.token().value;
}
-
+
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));
- }
+ }
},
-
+
//-----------------------------------------------------------------
// Validation methods
//-----------------------------------------------------------------
_validateProperty: function(property, value){
Validation.validate(property, value);
},
-
+
//-----------------------------------------------------------------
// Parsing methods
//-----------------------------------------------------------------
-
- parse: function(input){
+
+ parse: function(input){
this._tokenStream = new TokenStream(input, Tokens);
this._stylesheet();
},
-
+
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;
+ 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;
+ 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;
},
/**
- * Parses an HTML style attribute: a set of CSS declarations
+ * Parses an HTML style attribute: a set of CSS declarations
* separated by semicolons.
* @param {String} input The text to parse as a style attribute
- * @return {void}
+ * @return {void}
* @method parseStyleAttribute
*/
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){
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*
;
*/
+
/*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>",
@@ -3584,11 +3625,11 @@
"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-color" : "<color> | inherit",
"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",
@@ -3641,19 +3682,19 @@
"border-left-style" : "<border-style>",
"border-left-width" : "<border-width>",
"border-radius" : function(expression) {
var valid = false,
- numeric = "<length> | <percentage>",
+ simple = "<length> | <percentage> | inherit",
slash = false,
fill = false,
count = 0,
max = 8,
part;
while (expression.hasNext() && count < max) {
- valid = ValidationTypes.isAny(expression, numeric);
+ valid = ValidationTypes.isAny(expression, simple);
if (!valid) {
if (expression.peek() == "/" && count > 0 && !slash) {
slash = true;
max = count + 5;
@@ -3740,11 +3781,11 @@
"cue-before" : 1,
"cursor" : 1,
//D
"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 | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker",
+ "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 | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker | -webkit-box | -webkit-inline-box",
"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",
@@ -3968,10 +4009,11 @@
//Z
"z-index" : "<integer> | auto | inherit",
"zoom" : "<number> | <percentage> | normal"
};
+
/*global SyntaxUnit, Parser*/
/**
* Represents a selector combinator (whitespace, +, >).
* @namespace parserlib.css
* @class PropertyName
@@ -4201,10 +4243,13 @@
case "mm":
case "in":
case "pt":
case "pc":
case "ch":
+ case "vh":
+ case "vw":
+ case "vm":
this.type = "length";
break;
case "deg":
case "rad":
@@ -5587,41 +5632,42 @@
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: "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: "VIEWPORT_SYM", text: "@viewport"},
{ name: "UNKNOWN_SYM" },
//{ name: "ATKEYWORD"},
-
+
//CSS3 animations
{ name: "KEYFRAMES_SYM", text: [ "@keyframes", "@-webkit-keyframes", "@-moz-keyframes", "@-o-keyframes" ] },
//important symbol
{ name: "IMPORTANT_SYM"},
@@ -5632,34 +5678,34 @@
{ name: "TIME"},
{ name: "FREQ"},
{ 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: "INVALID"},
-
+
//combinators
{ name: "PLUS", text: "+" },
{ name: "GREATER", text: ">"},
{ name: "COMMA", text: ","},
{ name: "TILDE", text: "~"},
-
+
//modifier
- { name: "NOT"},
-
+ { name: "NOT"},
+
/*
* Defined in CSS3 Paged Media
*/
{ name: "TOPLEFTCORNER_SYM", text: "@top-left-corner"},
{ name: "TOPLEFT_SYM", text: "@top-left"},
@@ -5687,17 +5733,17 @@
{ 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: "|"
@@ -5716,55 +5762,55 @@
},
{
name: "LBRACE",
text: "{"
- },
+ },
{
name: "RBRACE",
text: "}"
- },
+ },
{
name: "LBRACKET",
text: "["
- },
+ },
{
name: "RBRACKET",
text: "]"
- },
+ },
{
name: "EQUALS",
text: "="
},
{
name: "COLON",
text: ":"
- },
+ },
{
name: "SEMICOLON",
text: ";"
- },
-
+ },
+
{
name: "LPAREN",
text: "("
- },
+ },
{
name: "RPAREN",
text: ")"
- },
+ },
{
name: "DOT",
text: "."
}
];
(function(){
var nameMap = [],
typeMap = {};
-
+
Tokens.UNKNOWN = -1;
Tokens.unshift({name:"EOF"});
for (var i=0, len = Tokens.length; i < len; i++){
nameMap.push(Tokens[i].name);
Tokens[Tokens[i].name] = i;
@@ -5776,15 +5822,15 @@
} else {
typeMap[Tokens[i].text] = i;
}
}
}
-
+
Tokens.name = function(tt){
return nameMap[tt];
};
-
+
Tokens.type = function(c){
return typeMap[c] || -1;
};
})();
@@ -6339,18 +6385,15 @@
},
"<x-one-radius>": function(expression) {
//[ <length> | <percentage> ] [ <length> | <percentage> ]?
var result = false,
- count = 0,
- numeric = "<length> | <percentage>",
- part;
+ simple = "<length> | <percentage> | inherit";
- if (ValidationTypes.isAny(expression, numeric)){
+ if (ValidationTypes.isAny(expression, simple)){
result = true;
-
- ValidationTypes.isAny(expression, numeric);
+ ValidationTypes.isAny(expression, simple);
}
return result;
}
}
@@ -6376,10 +6419,19 @@
ValidationError :ValidationError
};
})();
+
+
+(function(){
+for(var prop in parserlib){
+exports[prop] = parserlib[prop];
+}
+})();
+
+
/**
* Main CSSLint object.
* @class CSSLint
* @static
* @extends parserlib.util.EventTarget
@@ -6390,11 +6442,11 @@
var rules = [],
formatters = [],
embeddedRuleset = /\/\*csslint([^\*]*)\*\//,
api = new parserlib.util.EventTarget();
- api.version = "0.9.10";
+ api.version = "0.10.0";
//-------------------------------------------------------------------------
// Rule Management
//-------------------------------------------------------------------------
@@ -6613,10 +6665,11 @@
//-------------------------------------------------------------------------
return api;
})();
+
/*global CSSLint*/
/**
* An instance of Report is used to report results of the
* verification back to the main API.
* @class Reporter
@@ -7761,10 +7814,11 @@
});
}
});
+
/*
* Rule: Don't use IDs for selectors.
*/
/*global CSSLint*/
CSSLint.addRule({
@@ -8174,10 +8228,11 @@
}
});
}
});
+
/*
* Rule: Warn people past the IE 4095 limit
*/
/*global CSSLint*/
CSSLint.addRule({
@@ -9198,20 +9253,31 @@
});
return output;
}
});
-
return CSSLint;
})();
/*
* Encapsulates all of the CLI functionality. The api argument simply
* provides environment-specific functionality.
*/
/*global CSSLint*/
function cli(api){
+ var globalOptions = {
+ "help" : { "format" : "", "description" : "Displays this information."},
+ "format" : { "format" : "<format>", "description" : "Indicate which format to use for output."},
+ "list-rules" : { "format" : "", "description" : "Outputs all of the rules available."},
+ "quiet" : { "format" : "", "description" : "Only output when errors are present."},
+ "errors" : { "format" : "<rule[,rule]+>", "description" : "Indicate which rules to include as errors."},
+ "warnings" : { "format" : "<rule[,rule]+>", "description" : "Indicate which rules to include as warnings."},
+ "ignore" : { "format" : "<rule[,rule]+>", "description" : "Indicate which rules to ignore completely."},
+ "exclude-list": { "format" : "<file|dir[,file|dir]+>", "description" : "Indicate which files/directories to exclude from being linted."},
+ "version" : { "format" : "", "description" : "Outputs the current version number."}
+ };
+
//-------------------------------------------------------------------------
// Helper functions
//-------------------------------------------------------------------------
/**
@@ -9279,11 +9345,12 @@
* @return {Array} A list of files
*/
function filterFiles(files, options) {
var excludeList = options["exclude-list"],
excludeFiles = [],
- filesToLint = files;
+ filesToLint = files.map(api.getFullPath),
+ fullPath;
if (excludeList) {
// Build up the exclude list, expanding any directory exclusions that were passed in
excludeList.split(",").forEach(function(value){
@@ -9294,12 +9361,13 @@
}
});
// Remove the excluded files from the list of files to lint
excludeFiles.forEach(function(value){
- if (filesToLint.indexOf(value) > -1) {
- filesToLint.splice(filesToLint.indexOf(value),1);
+ fullPath = api.getFullPath(value);
+ if (filesToLint.indexOf(fullPath) > -1) {
+ filesToLint.splice(filesToLint.indexOf(fullPath),1);
}
});
}
return filesToLint;
@@ -9359,24 +9427,39 @@
/**
* Outputs the help screen to the CLI.
* @return {void}
*/
function outputHelp(){
+ var lenToPad = 40,
+ toPrint = '',
+ formatString = '';
+
api.print([
"\nUsage: csslint-rhino.js [options]* [file|dir]*",
" ",
- "Global Options",
- " --help Displays this information.",
- " --format=<format> Indicate which format to use for output.",
- " --list-rules Outputs all of the rules available.",
- " --quiet Only output when errors are present.",
- " --errors=<rule[,rule]+> Indicate which rules to include as errors.",
- " --warnings=<rule[,rule]+> Indicate which rules to include as warnings.",
- " --ignore=<rule[,rule]+> Indicate which rules to ignore completely.",
- " --exclude-list=<file|dir[,file|dir]+> Indicate which files/directories to exclude from being linted.",
- " --version Outputs the current version number."
- ].join("\n") + "\n");
+ "Global Options"
+ ].join("\n"));
+
+ for (var optionName in globalOptions) {
+ if (globalOptions.hasOwnProperty(optionName)) {
+ // Print the option name and the format if present
+ toPrint += " --" + optionName;
+ if (globalOptions[optionName].format !== "") {
+ formatString = '=' + globalOptions[optionName].format;
+ toPrint += formatString;
+ } else {
+ formatString = '';
+ }
+
+ // Pad out with the appropriate number of spaces
+ toPrint += new Array(lenToPad - (optionName.length + formatString.length)).join(' ');
+
+ // Print the description
+ toPrint += globalOptions[optionName].description + "\n";
+ }
+ }
+ api.print(toPrint);
}
/**
* Given an Array of filenames, print wrapping output and process them.
* @param files {Array} filenames list
@@ -9431,11 +9514,10 @@
files = [];
while(arg){
if (arg.indexOf("--") === 0){
argName = arg.substring(2);
- options[argName] = true;
if (argName.indexOf("=") > -1){
parts = argName.split("=");
options[parts[0]] = parts[1];
} else {
@@ -9456,10 +9538,20 @@
options.files = files;
return options;
}
+ function validateOptions(options) {
+ for (var option_key in options) {
+ if (!globalOptions.hasOwnProperty(option_key) && option_key !== 'files') {
+ api.print(option_key + ' is not a valid option. Exiting...');
+ outputHelp();
+ api.quit(0);
+ }
+ }
+ }
+
function readConfigFile(options) {
var data = api.readFile(api.getFullPath(".csslintrc"));
if (data) {
options = processArguments(data.split(/[\s\n\r]+/m), options);
}
@@ -9486,10 +9578,13 @@
if (options.help || argCount === 0){
outputHelp();
api.quit(0);
}
+ // Validate options
+ validateOptions(options);
+
if (options.version){
api.print("v" + CSSLint.version);
api.quit(0);
}
@@ -9498,10 +9593,11 @@
api.quit(0);
}
api.quit(processFiles(options.files,options));
}
+
/*
* CSSLint Rhino Command Line Interface
*/
/*jshint rhino:true*/
/*global cli, File*/
@@ -9550,6 +9646,6 @@
return readFile(filename);
} catch (ex) {
return "";
}
}
-});
+});
\ No newline at end of file