From 917de33eb725111544fa23052ba82d583684baee Mon Sep 17 00:00:00 2001 From: Kaliamos Date: Wed, 5 Jul 2017 11:45:58 +0200 Subject: [PATCH] Add csslint --- public/plugins/csslint.js | 10745 ++++++++++++++++++++++++++++++++++++ 1 file changed, 10745 insertions(+) create mode 100644 public/plugins/csslint.js diff --git a/public/plugins/csslint.js b/public/plugins/csslint.js new file mode 100644 index 000000000..a24542c87 --- /dev/null +++ b/public/plugins/csslint.js @@ -0,0 +1,10745 @@ +/*! +CSSLint v1.0.3 +Copyright (c) 2016 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 +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +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. + +*/ + +var CSSLint = (function(){ + var module = module || {}, + exports = exports || {}; + +/*! +Parser-Lib +Copyright (c) 2009-2016 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 +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +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 v1.0.0, Build time: 15-July-2016 12:36:10 */ +var parserlib = (function () { +var require; +require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o). + * @namespace parserlib.css + * @class Combinator + * @extends parserlib.util.SyntaxUnit + * @constructor + * @param {String} text The text representation of the unit. + * @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 Combinator(text, line, col) { + + SyntaxUnit.call(this, text, line, col, Parser.COMBINATOR_TYPE); + + /** + * The type of modifier. + * @type String + * @property type + */ + this.type = "unknown"; + + //pretty simple + if (/^\s+$/.test(text)) { + this.type = "descendant"; + } else if (text === ">") { + this.type = "child"; + } else if (text === "+") { + this.type = "adjacent-sibling"; + } else if (text === "~") { + this.type = "sibling"; + } + +} + +Combinator.prototype = new SyntaxUnit(); +Combinator.prototype.constructor = Combinator; + + +},{"../util/SyntaxUnit":26,"./Parser":6}],3:[function(require,module,exports){ +"use strict"; + +module.exports = Matcher; + +var StringReader = require("../util/StringReader"); +var SyntaxError = require("../util/SyntaxError"); + +/** + * This class implements a combinator library for matcher functions. + * The combinators are described at: + * https://developer.mozilla.org/en-US/docs/Web/CSS/Value_definition_syntax#Component_value_combinators + */ +function Matcher(matchFunc, toString) { + this.match = function(expression) { + // Save/restore marks to ensure that failed matches always restore + // the original location in the expression. + var result; + expression.mark(); + result = matchFunc(expression); + if (result) { + expression.drop(); + } else { + expression.restore(); + } + return result; + }; + this.toString = typeof toString === "function" ? toString : function() { + return toString; + }; +} + +/** Precedence table of combinators. */ +Matcher.prec = { + MOD: 5, + SEQ: 4, + ANDAND: 3, + OROR: 2, + ALT: 1 +}; + +/** Simple recursive-descent grammar to build matchers from strings. */ +Matcher.parse = function(str) { + var reader, eat, expr, oror, andand, seq, mod, term, result; + reader = new StringReader(str); + eat = function(matcher) { + var result = reader.readMatch(matcher); + if (result === null) { + throw new SyntaxError( + "Expected "+matcher, reader.getLine(), reader.getCol()); + } + return result; + }; + expr = function() { + // expr = oror (" | " oror)* + var m = [ oror() ]; + while (reader.readMatch(" | ") !== null) { + m.push(oror()); + } + return m.length === 1 ? m[0] : Matcher.alt.apply(Matcher, m); + }; + oror = function() { + // oror = andand ( " || " andand)* + var m = [ andand() ]; + while (reader.readMatch(" || ") !== null) { + m.push(andand()); + } + return m.length === 1 ? m[0] : Matcher.oror.apply(Matcher, m); + }; + andand = function() { + // andand = seq ( " && " seq)* + var m = [ seq() ]; + while (reader.readMatch(" && ") !== null) { + m.push(seq()); + } + return m.length === 1 ? m[0] : Matcher.andand.apply(Matcher, m); + }; + seq = function() { + // seq = mod ( " " mod)* + var m = [ mod() ]; + while (reader.readMatch(/^ (?![&|\]])/) !== null) { + m.push(mod()); + } + return m.length === 1 ? m[0] : Matcher.seq.apply(Matcher, m); + }; + mod = function() { + // mod = term ( "?" | "*" | "+" | "#" | "{,}" )? + var m = term(); + if (reader.readMatch("?") !== null) { + return m.question(); + } else if (reader.readMatch("*") !== null) { + return m.star(); + } else if (reader.readMatch("+") !== null) { + return m.plus(); + } else if (reader.readMatch("#") !== null) { + return m.hash(); + } else if (reader.readMatch(/^\{\s*/) !== null) { + var min = eat(/^\d+/); + eat(/^\s*,\s*/); + var max = eat(/^\d+/); + eat(/^\s*\}/); + return m.braces(+min, +max); + } + return m; + }; + term = function() { + // term = | literal | "[ " expression " ]" + if (reader.readMatch("[ ") !== null) { + var m = expr(); + eat(" ]"); + return m; + } + return Matcher.fromType(eat(/^[^ ?*+#{]+/)); + }; + result = expr(); + if (!reader.eof()) { + throw new SyntaxError( + "Expected end of string", reader.getLine(), reader.getCol()); + } + return result; +}; + +/** + * Convert a string to a matcher (parsing simple alternations), + * or do nothing if the argument is already a matcher. + */ +Matcher.cast = function(m) { + if (m instanceof Matcher) { + return m; + } + return Matcher.parse(m); +}; + +/** + * Create a matcher for a single type. + */ +Matcher.fromType = function(type) { + // Late require of ValidationTypes to break a dependency cycle. + var ValidationTypes = require("./ValidationTypes"); + return new Matcher(function(expression) { + return expression.hasNext() && ValidationTypes.isType(expression, type); + }, type); +}; + +/** + * Create a matcher for one or more juxtaposed words, which all must + * occur, in the given order. + */ +Matcher.seq = function() { + var ms = Array.prototype.slice.call(arguments).map(Matcher.cast); + if (ms.length === 1) { + return ms[0]; + } + return new Matcher(function(expression) { + var i, result = true; + for (i = 0; result && i < ms.length; i++) { + result = ms[i].match(expression); + } + return result; + }, function(prec) { + var p = Matcher.prec.SEQ; + var s = ms.map(function(m) { + return m.toString(p); + }).join(" "); + if (prec > p) { + s = "[ " + s + " ]"; + } + return s; + }); +}; + +/** + * Create a matcher for one or more alternatives, where exactly one + * must occur. + */ +Matcher.alt = function() { + var ms = Array.prototype.slice.call(arguments).map(Matcher.cast); + if (ms.length === 1) { + return ms[0]; + } + return new Matcher(function(expression) { + var i, result = false; + for (i = 0; !result && i < ms.length; i++) { + result = ms[i].match(expression); + } + return result; + }, function(prec) { + var p = Matcher.prec.ALT; + var s = ms.map(function(m) { + return m.toString(p); + }).join(" | "); + if (prec > p) { + s = "[ " + s + " ]"; + } + return s; + }); +}; + +/** + * Create a matcher for two or more options. This implements the + * double bar (||) and double ampersand (&&) operators, as well as + * variants of && where some of the alternatives are optional. + * This will backtrack through even successful matches to try to + * maximize the number of items matched. + */ +Matcher.many = function(required) { + var ms = Array.prototype.slice.call(arguments, 1).reduce(function(acc, v) { + if (v.expand) { + // Insert all of the options for the given complex rule as + // individual options. + var ValidationTypes = require("./ValidationTypes"); + acc.push.apply(acc, ValidationTypes.complex[v.expand].options); + } else { + acc.push(Matcher.cast(v)); + } + return acc; + }, []); + + if (required === true) { + required = ms.map(function() { + return true; + }); + } + + var result = new Matcher(function(expression) { + var seen = [], max = 0, pass = 0; + var success = function(matchCount) { + if (pass === 0) { + max = Math.max(matchCount, max); + return matchCount === ms.length; + } else { + return matchCount === max; + } + }; + var tryMatch = function(matchCount) { + for (var i = 0; i < ms.length; i++) { + if (seen[i]) { + continue; + } + expression.mark(); + if (ms[i].match(expression)) { + seen[i] = true; + // Increase matchCount iff this was a required element + // (or if all the elements are optional) + if (tryMatch(matchCount + ((required === false || required[i]) ? 1 : 0))) { + expression.drop(); + return true; + } + // Backtrack: try *not* matching using this rule, and + // let's see if it leads to a better overall match. + expression.restore(); + seen[i] = false; + } else { + expression.drop(); + } + } + return success(matchCount); + }; + if (!tryMatch(0)) { + // Couldn't get a complete match, retrace our steps to make the + // match with the maximum # of required elements. + pass++; + tryMatch(0); + } + + if (required === false) { + return max > 0; + } + // Use finer-grained specification of which matchers are required. + for (var i = 0; i < ms.length; i++) { + if (required[i] && !seen[i]) { + return false; + } + } + return true; + }, function(prec) { + 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) + "?"; + } + return m.toString(p); + }).join(required === false ? " || " : " && "); + if (prec > p) { + s = "[ " + s + " ]"; + } + return s; + }); + result.options = ms; + return result; +}; + +/** + * 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); + args.unshift(true); + 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 + * present. + */ +Matcher.oror = function() { + var args = Array.prototype.slice.call(arguments); + args.unshift(false); + return Matcher.many.apply(Matcher, args); +}; + +/** Instance methods on Matchers. */ +Matcher.prototype = { + constructor: Matcher, + // 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); }, + // Basic combinators + 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, "?"); }, + hash: function() { + return this.braces(1, Infinity, "#", Matcher.cast(",")); + }, + braces: function(min, max, marker, optSep) { + var m1 = this, m2 = optSep ? optSep.then(this) : this; + if (!marker) { + marker = "{" + min + "," + max + "}"; + } + return new Matcher(function(expression) { + var result = true, i; + for (i = 0; i < max; i++) { + if (i > 0 && optSep) { + result = m2.match(expression); + } else { + result = m1.match(expression); + } + if (!result) { + break; + } + } + return i >= min; + }, function() { + return m1.toString(Matcher.prec.MOD) + marker; + }); + } +}; + +},{"../util/StringReader":24,"../util/SyntaxError":25,"./ValidationTypes":21}],4:[function(require,module,exports){ +"use strict"; + +module.exports = MediaFeature; + +var SyntaxUnit = require("../util/SyntaxUnit"); + +var Parser = require("./Parser"); + +/** + * Represents a media feature, such as max-width:500. + * @namespace parserlib.css + * @class MediaFeature + * @extends parserlib.util.SyntaxUnit + * @constructor + * @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 + * @type String + * @property name + */ + this.name = name; + + /** + * The value for the feature or null if there is none. + * @type SyntaxUnit + * @property value + */ + this.value = value; +} + +MediaFeature.prototype = new SyntaxUnit(); +MediaFeature.prototype.constructor = MediaFeature; + + +},{"../util/SyntaxUnit":26,"./Parser":6}],5:[function(require,module,exports){ +"use strict"; + +module.exports = MediaQuery; + +var SyntaxUnit = require("../util/SyntaxUnit"); + +var Parser = require("./Parser"); + +/** + * Represents an individual media query. + * @namespace parserlib.css + * @class MediaQuery + * @extends parserlib.util.SyntaxUnit + * @constructor + * @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") + * @type String + * @property modifier + */ + this.modifier = modifier; + + /** + * The mediaType (i.e., "print") + * @type String + * @property mediaType + */ + this.mediaType = mediaType; + + /** + * The parts that make up the selector. + * @type Array + * @property features + */ + this.features = features; + +} + +MediaQuery.prototype = new SyntaxUnit(); +MediaQuery.prototype.constructor = MediaQuery; + + +},{"../util/SyntaxUnit":26,"./Parser":6}],6:[function(require,module,exports){ +"use strict"; + +module.exports = Parser; + +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"); + +/** + * A CSS3 parser. + * @namespace parserlib.css + * @class Parser + * @constructor + * @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 + * syntax errors. + */ +function Parser(options) { + + //inherit event functionality + EventTarget.call(this); + + + this.options = options || {}; + + this._tokenStream = null; +} + +//Static constants +Parser.DEFAULT_TYPE = 0; +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 + prop, + additions = { + __proto__: null, + + //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, + + //----------------------------------------------------------------- + // 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_rule | supports_rule ] [S|CDO|CDC]* ]* + * ; + */ + + var tokenStream = this._tokenStream, + 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._skipCruft(); + break; + case Tokens.FONT_FACE_SYM: + this._font_face(); + this._skipCruft(); + break; + case Tokens.KEYFRAMES_SYM: + this._keyframes(); + this._skipCruft(); + break; + case Tokens.VIEWPORT_SYM: + this._viewport(); + this._skipCruft(); + break; + case Tokens.DOCUMENT_SYM: + this._document(); + this._skipCruft(); + break; + case Tokens.SUPPORTS_SYM: + this._supports(); + 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: + if (!this._ruleset()) { + + //error handling for known issues + switch (tt) { + case Tokens.CHARSET_SYM: + token = tokenStream.LT(1); + this._charset(false); + throw new SyntaxError("@charset not allowed here.", token.startLine, token.startCol); + case Tokens.IMPORT_SYM: + token = tokenStream.LT(1); + this._import(false); + throw new SyntaxError("@import not allowed here.", token.startLine, token.startCol); + case Tokens.NAMESPACE_SYM: + token = tokenStream.LT(1); + this._namespace(false); + 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({ + 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, + 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"); + + 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"); + + 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 + }); + } + + }, + + _supports: function(emit) { + /* + * supports_rule + * : SUPPORTS_SYM S* supports_condition S* group_rule_body + * ; + */ + var tokenStream = this._tokenStream, + line, + col; + + if (tokenStream.match(Tokens.SUPPORTS_SYM)) { + line = tokenStream.token().startLine; + col = tokenStream.token().startCol; + + this._readWhitespace(); + this._supports_condition(); + this._readWhitespace(); + + tokenStream.mustMatch(Tokens.LBRACE); + this._readWhitespace(); + + if (emit !== false) { + this.fire({ + type: "startsupports", + line: line, + col: col + }); + } + + while (true) { + if (!this._ruleset()) { + break; + } + } + + tokenStream.mustMatch(Tokens.RBRACE); + this._readWhitespace(); + + this.fire({ + type: "endsupports", + line: line, + col: col + }); + } + }, + + _supports_condition: function() { + /* + * supports_condition + * : supports_negation | supports_conjunction | supports_disjunction | + * supports_condition_in_parens + * ; + */ + var tokenStream = this._tokenStream, + ident; + + if (tokenStream.match(Tokens.IDENT)) { + ident = tokenStream.token().value.toLowerCase(); + + if (ident === "not") { + tokenStream.mustMatch(Tokens.S); + this._supports_condition_in_parens(); + } else { + tokenStream.unget(); + } + } else { + this._supports_condition_in_parens(); + this._readWhitespace(); + + while (tokenStream.peek() === Tokens.IDENT) { + ident = tokenStream.LT(1).value.toLowerCase(); + if (ident === "and" || ident === "or") { + tokenStream.mustMatch(Tokens.IDENT); + this._readWhitespace(); + this._supports_condition_in_parens(); + this._readWhitespace(); + } + } + } + }, + + _supports_condition_in_parens: function() { + /* + * supports_condition_in_parens + * : ( '(' S* supports_condition S* ')' ) | supports_declaration_condition | + * general_enclosed + * ; + */ + var tokenStream = this._tokenStream, + ident; + + if (tokenStream.match(Tokens.LPAREN)) { + this._readWhitespace(); + if (tokenStream.match(Tokens.IDENT)) { + // look ahead for not keyword, if not given, continue with declaration condition. + ident = tokenStream.token().value.toLowerCase(); + if (ident === "not") { + this._readWhitespace(); + this._supports_condition(); + this._readWhitespace(); + tokenStream.mustMatch(Tokens.RPAREN); + } else { + tokenStream.unget(); + this._supports_declaration_condition(false); + } + } else { + this._supports_condition(); + this._readWhitespace(); + tokenStream.mustMatch(Tokens.RPAREN); + } + } else { + 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); + } + this._readWhitespace(); + this._declaration(); + tokenStream.mustMatch(Tokens.RPAREN); + }, + + _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(); + + 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 (tokenStream.peek() === Tokens.VIEWPORT_SYM) { + this._viewport(); + } else if (tokenStream.peek() === Tokens.DOCUMENT_SYM) { + this._document(); + } else if (tokenStream.peek() === Tokens.SUPPORTS_SYM) { + this._supports(); + } 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 ]* + * | expression [ AND S* expression ]* + * ; + */ + 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 { + this._readWhitespace(); + while (tokenStream.match(Tokens.IDENT)) { + if (tokenStream.token().value.toLowerCase() !== "and") { + this._unexpectedToken(tokenStream.token()); + } + + this._readWhitespace(); + expressions.push(this._media_expression()); + } + } + + return new MediaQuery(ident, type, expressions, token.startLine, token.startCol); + }, + + //CSS3 Media Queries + _media_type: function() { + /* + * media_type + * : IDENT + * ; + */ + 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 + * @private + */ + _media_expression: function() { + /* + * expression + * : '(' S* media_feature S* [ ':' S* expr ]? ')' S* + * ; + */ + 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); + }, + + //CSS3 Media Queries + _media_feature: function() { + /* + * media_feature + * : IDENT + * ; + */ + var tokenStream = this._tokenStream; + + this._readWhitespace(); + + tokenStream.mustMatch(Tokens.IDENT); + + return SyntaxUnit.fromToken(tokenStream.token()); + }, + + //CSS3 Paged Media + _page: function() { + /* + * page: + * 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.fire({ + type: "endpage", + id: identifier, + pseudo: pseudoPage, + line: line, + col: col + }); + + }, + + //CSS3 Paged Media + _margin: function() { + /* + * margin : + * margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S* + * ; + */ + var tokenStream = this._tokenStream, + line, + col, + 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 | + * TOPRIGHTCORNER_SYM | + * BOTTOMLEFTCORNER_SYM | + * BOTTOMLEFT_SYM | + * BOTTOMCENTER_SYM | + * BOTTOMRIGHT_SYM | + * BOTTOMRIGHTCORNER_SYM | + * LEFTTOP_SYM | + * LEFTMIDDLE_SYM | + * LEFTBOTTOM_SYM | + * RIGHTTOP_SYM | + * RIGHTMIDDLE_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.BOTTOMCENTER_SYM, Tokens.BOTTOMRIGHT_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()); + } 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* + * '{' 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 + }); + + }, + + _document: function() { + /* + * document + * : DOCUMENT_SYM S* + * _document_function [ ',' S* _document_function ]* S* + * '{' S* ruleset* '}' + * ; + */ + + var tokenStream = this._tokenStream, + token, + functions = [], + prefix = ""; + + tokenStream.mustMatch(Tokens.DOCUMENT_SYM); + token = tokenStream.token(); + if (/^@\-([^\-]+)\-/.test(token.value)) { + prefix = RegExp.$1; + } + + this._readWhitespace(); + functions.push(this._document_function()); + + while (tokenStream.match(Tokens.COMMA)) { + this._readWhitespace(); + functions.push(this._document_function()); + } + + tokenStream.mustMatch(Tokens.LBRACE); + this._readWhitespace(); + + this.fire({ + type: "startdocument", + functions: functions, + prefix: prefix, + line: token.startLine, + col: token.startCol + }); + + var ok = true; + while (ok) { + switch (tokenStream.peek()) { + case Tokens.PAGE_SYM: + this._page(); + break; + case Tokens.FONT_FACE_SYM: + this._font_face(); + break; + case Tokens.VIEWPORT_SYM: + this._viewport(); + break; + case Tokens.MEDIA_SYM: + this._media(); + break; + case Tokens.KEYFRAMES_SYM: + this._keyframes(); + break; + case Tokens.DOCUMENT_SYM: + this._document(); + break; + default: + ok = Boolean(this._ruleset()); + } + } + + tokenStream.mustMatch(Tokens.RBRACE); + token = tokenStream.token(); + this._readWhitespace(); + + this.fire({ + type: "enddocument", + functions: functions, + prefix: prefix, + line: token.startLine, + col: token.startCol + }); + }, + + _document_function: function() { + /* + * document_function + * : function | URI S* + * ; + */ + + var tokenStream = this._tokenStream, + value; + + if (tokenStream.match(Tokens.URI)) { + value = tokenStream.token().value; + this._readWhitespace(); + } else { + value = this._function(); + } + + return value; + }, + + _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])) { + 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; + + + /* + * Error Recovery: If even a single selector fails to parse, + * then the entire ruleset should be thrown away. + */ + 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) { + + this.fire({ + type: "startrule", + selectors: selectors, + line: selectors[0].line, + col: selectors[0].col + }); + + 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) { + selectors.push(selector); + } else { + this._unexpectedToken(tokenStream.LT(1)); + } + } + } + + 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()) { + + //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 (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 = [ + //HASH + function() { + return tokenStream.match(Tokens.HASH) ? + new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) : + null; + }, + this._class, + this._attrib, + this._pseudo, + this._negation + ], + i = 0, + len = components.length, + component = null, + 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(); + 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) { + /* + * 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. + */ + if (ns) { + tokenStream.unget(); + if (ns.length > 1) { + tokenStream.unget(); + } + } + + return null; + } 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); + token = tokenStream.token(); + 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); + + } 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; + }, + + //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* + * [ [ PREFIXMATCH | + * SUFFIXMATCH | + * 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 += this._readWhitespace(); + + if (tokenStream.match([Tokens.PREFIXMATCH, Tokens.SUFFIXMATCH, Tokens.SUBSTRINGMATCH, + Tokens.EQUALS, Tokens.INCLUDES, Tokens.DASHMATCH])) { + + value += tokenStream.token().value; + value += this._readWhitespace(); + + tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]); + 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); + } else { + var startLine = tokenStream.LT(1).startLine, + startCol = tokenStream.LT(0).startCol; + throw new SyntaxError("Expected a `FUNCTION` or `IDENT` after colon at line " + startLine + ", col " + startCol + ".", startLine, startCol); + } + } + + 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(); + } + + return value.length ? value : null; + + }, + + //CSS3 Selectors + _negation: function() { + /* + * negation + * : NOT S* negation_arg S* ')' + * ; + */ + + var tokenStream = this._tokenStream, + 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 + * : 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; + }, + this._class, + this._attrib, + this._pseudo + ], + arg = null, + i = 0, + len = args.length, + 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; + }, + + _declaration: function() { + + /* + * declaration + * : property ':' S* expr prio? + * | /( empty )/ + * ; + */ + + var tokenStream = this._tokenStream, + property = null, + expr = null, + prio = 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 values = [], + //valueParts = [], + value = null, + operator = null; + + value = this._term(inFunction); + if (value !== null) { + + values.push(value); + + do { + operator = this._operator(inFunction); + + //if there's an operator, keep building up the value parts + if (operator) { + values.push(operator); + } /*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(inFunction); + + 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(inFunction) { + + /* + * 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, + endChar = null, + part = 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 it's a simple block + } else if (inFunction && tokenStream.match([Tokens.LPAREN, Tokens.LBRACE, Tokens.LBRACKET])) { + + token = tokenStream.token(); + endChar = token.endChar; + value = token.value + this._expr(inFunction).text; + if (unary === null) { + line = tokenStream.token().startLine; + col = tokenStream.token().startCol; + } + tokenStream.mustMatch(Tokens.type(endChar)); + value += endChar; + this._readWhitespace(); + + //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; + // Correct potentially-inaccurate IDENT parsing in + // PropertyValuePart constructor. + part = PropertyValuePart.fromToken(tokenStream.token()); + } + 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) { + value = this._ie_function(); + } else { + value = this._function(); + } + } + + /*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 part !== null ? part : 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; + lt = tokenStream.peek(); + } + } while (tokenStream.match([Tokens.COMMA, Tokens.S])); + } + + //END: Horrible Hack + + 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, + 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); + 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 = ""; + + 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); + this._readWhitespace(); + + }, + + _keyframe_name: function() { + + /* + * keyframe_name: + * : IDENT + * | STRING + * ; + */ + var tokenStream = this._tokenStream; + + tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]); + return SyntaxUnit.fromToken(tokenStream.token()); + }, + + _keyframe_rule: function() { + + /* + * keyframe_rule: + * : key_list S* + * '{' S* declaration [ ';' S* declaration ]* '}' S* + * ; + */ + var keyList = this._key_list(); + + this.fire({ + type: "startkeyframerule", + keys: keyList, + line: keyList[0].line, + col: keyList[0].col + }); + + 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, + 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(); + + 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 + * @private + */ + _skipCruft: function() { + while (this._tokenStream.match([Tokens.S, Tokens.CDO, Tokens.CDC])) { + //noop + } + }, + + /** + * Not part of CSS grammar, but this pattern occurs frequently + * in the official CSS grammar. Split out here to eliminate + * duplicate code. + * @param {Boolean} checkStart Indicates if the rule should check + * for the left brace at the beginning. + * @param {Boolean} readMargins Indicates if the rule should check + * for margin patterns. + * @return {void} + * @method _readDeclarations + * @private + */ + _readDeclarations: function(checkStart, readMargins) { + /* + * 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 declaration if there's another declaration + * or margin afterwards. + */ + var tokenStream = this._tokenStream, + tt; + + + this._readWhitespace(); + + if (checkStart) { + 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); + } 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) { + 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; + }, + + /** + * 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; + }, + + /** + * 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 + * separated by semicolons. + * @param {String} input The text to parse as a style attribute + * @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 (Object.prototype.hasOwnProperty.call(additions, prop)) { + proto[prop] = additions[prop]; + } + } + + return proto; +}(); + + +/* +nth + : S* [ ['-'|'+']? INTEGER? {N} [ S* ['-'|'+'] S* INTEGER ]? | + ['-'|'+']? INTEGER | {O}{D}{D} | {E}{V}{E}{N} ] S* + ; +*/ + +},{"../util/EventTarget":23,"../util/SyntaxError":25,"../util/SyntaxUnit":26,"./Combinator":2,"./MediaFeature":4,"./MediaQuery":5,"./PropertyName":8,"./PropertyValue":9,"./PropertyValuePart":11,"./Selector":13,"./SelectorPart":14,"./SelectorSubPart":15,"./TokenStream":17,"./Tokens":18,"./Validation":19}],7:[function(require,module,exports){ +"use strict"; + +/* exported Properties */ + +var Properties = module.exports = { + __proto__: null, + + //A + "align-items" : "flex-start | flex-end | center | baseline | stretch", + "align-content" : "flex-start | flex-end | center | space-between | space-around | stretch", + "align-self" : "auto | flex-start | flex-end | center | baseline | stretch", + "-webkit-align-items" : "flex-start | flex-end | center | baseline | stretch", + "-webkit-align-content" : "flex-start | flex-end | center | space-between | space-around | stretch", + "-webkit-align-self" : "auto | flex-start | flex-end | center | baseline | stretch", + "alignment-adjust" : "auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | | ", + "alignment-baseline" : "auto | baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical", + "animation" : 1, + "animation-delay" : "