wp/wp-includes/js/codemirror/csslint.js
changeset 7 cf61fcea0001
child 16 a86126ab1dd4
equal deleted inserted replaced
6:490d5cc509ed 7:cf61fcea0001
       
     1 /*!
       
     2 CSSLint v1.0.4
       
     3 Copyright (c) 2016 Nicole Sullivan and Nicholas C. Zakas. All rights reserved.
       
     4 
       
     5 Permission is hereby granted, free of charge, to any person obtaining a copy
       
     6 of this software and associated documentation files (the 'Software'), to deal
       
     7 in the Software without restriction, including without limitation the rights
       
     8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       
     9 copies of the Software, and to permit persons to whom the Software is
       
    10 furnished to do so, subject to the following conditions:
       
    11 
       
    12 The above copyright notice and this permission notice shall be included in
       
    13 all copies or substantial portions of the Software.
       
    14 
       
    15 THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       
    16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       
    17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       
    18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       
    19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       
    20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
       
    21 THE SOFTWARE.
       
    22 
       
    23 */
       
    24 
       
    25 var CSSLint = (function(){
       
    26   var module = module || {},
       
    27       exports = exports || {};
       
    28 
       
    29 /*!
       
    30 Parser-Lib
       
    31 Copyright (c) 2009-2016 Nicholas C. Zakas. All rights reserved.
       
    32 
       
    33 Permission is hereby granted, free of charge, to any person obtaining a copy
       
    34 of this software and associated documentation files (the "Software"), to deal
       
    35 in the Software without restriction, including without limitation the rights
       
    36 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       
    37 copies of the Software, and to permit persons to whom the Software is
       
    38 furnished to do so, subject to the following conditions:
       
    39 
       
    40 The above copyright notice and this permission notice shall be included in
       
    41 all copies or substantial portions of the Software.
       
    42 
       
    43 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       
    44 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       
    45 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
       
    46 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       
    47 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       
    48 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
       
    49 THE SOFTWARE.
       
    50 */
       
    51 /* Version v1.1.0, Build time: 6-December-2016 10:31:29 */
       
    52 var parserlib = (function () {
       
    53 var require;
       
    54 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<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
       
    55 "use strict";
       
    56 
       
    57 /* exported Colors */
       
    58 
       
    59 var Colors = module.exports = {
       
    60     __proto__       :null,
       
    61     aliceblue       :"#f0f8ff",
       
    62     antiquewhite    :"#faebd7",
       
    63     aqua            :"#00ffff",
       
    64     aquamarine      :"#7fffd4",
       
    65     azure           :"#f0ffff",
       
    66     beige           :"#f5f5dc",
       
    67     bisque          :"#ffe4c4",
       
    68     black           :"#000000",
       
    69     blanchedalmond  :"#ffebcd",
       
    70     blue            :"#0000ff",
       
    71     blueviolet      :"#8a2be2",
       
    72     brown           :"#a52a2a",
       
    73     burlywood       :"#deb887",
       
    74     cadetblue       :"#5f9ea0",
       
    75     chartreuse      :"#7fff00",
       
    76     chocolate       :"#d2691e",
       
    77     coral           :"#ff7f50",
       
    78     cornflowerblue  :"#6495ed",
       
    79     cornsilk        :"#fff8dc",
       
    80     crimson         :"#dc143c",
       
    81     cyan            :"#00ffff",
       
    82     darkblue        :"#00008b",
       
    83     darkcyan        :"#008b8b",
       
    84     darkgoldenrod   :"#b8860b",
       
    85     darkgray        :"#a9a9a9",
       
    86     darkgrey        :"#a9a9a9",
       
    87     darkgreen       :"#006400",
       
    88     darkkhaki       :"#bdb76b",
       
    89     darkmagenta     :"#8b008b",
       
    90     darkolivegreen  :"#556b2f",
       
    91     darkorange      :"#ff8c00",
       
    92     darkorchid      :"#9932cc",
       
    93     darkred         :"#8b0000",
       
    94     darksalmon      :"#e9967a",
       
    95     darkseagreen    :"#8fbc8f",
       
    96     darkslateblue   :"#483d8b",
       
    97     darkslategray   :"#2f4f4f",
       
    98     darkslategrey   :"#2f4f4f",
       
    99     darkturquoise   :"#00ced1",
       
   100     darkviolet      :"#9400d3",
       
   101     deeppink        :"#ff1493",
       
   102     deepskyblue     :"#00bfff",
       
   103     dimgray         :"#696969",
       
   104     dimgrey         :"#696969",
       
   105     dodgerblue      :"#1e90ff",
       
   106     firebrick       :"#b22222",
       
   107     floralwhite     :"#fffaf0",
       
   108     forestgreen     :"#228b22",
       
   109     fuchsia         :"#ff00ff",
       
   110     gainsboro       :"#dcdcdc",
       
   111     ghostwhite      :"#f8f8ff",
       
   112     gold            :"#ffd700",
       
   113     goldenrod       :"#daa520",
       
   114     gray            :"#808080",
       
   115     grey            :"#808080",
       
   116     green           :"#008000",
       
   117     greenyellow     :"#adff2f",
       
   118     honeydew        :"#f0fff0",
       
   119     hotpink         :"#ff69b4",
       
   120     indianred       :"#cd5c5c",
       
   121     indigo          :"#4b0082",
       
   122     ivory           :"#fffff0",
       
   123     khaki           :"#f0e68c",
       
   124     lavender        :"#e6e6fa",
       
   125     lavenderblush   :"#fff0f5",
       
   126     lawngreen       :"#7cfc00",
       
   127     lemonchiffon    :"#fffacd",
       
   128     lightblue       :"#add8e6",
       
   129     lightcoral      :"#f08080",
       
   130     lightcyan       :"#e0ffff",
       
   131     lightgoldenrodyellow  :"#fafad2",
       
   132     lightgray       :"#d3d3d3",
       
   133     lightgrey       :"#d3d3d3",
       
   134     lightgreen      :"#90ee90",
       
   135     lightpink       :"#ffb6c1",
       
   136     lightsalmon     :"#ffa07a",
       
   137     lightseagreen   :"#20b2aa",
       
   138     lightskyblue    :"#87cefa",
       
   139     lightslategray  :"#778899",
       
   140     lightslategrey  :"#778899",
       
   141     lightsteelblue  :"#b0c4de",
       
   142     lightyellow     :"#ffffe0",
       
   143     lime            :"#00ff00",
       
   144     limegreen       :"#32cd32",
       
   145     linen           :"#faf0e6",
       
   146     magenta         :"#ff00ff",
       
   147     maroon          :"#800000",
       
   148     mediumaquamarine:"#66cdaa",
       
   149     mediumblue      :"#0000cd",
       
   150     mediumorchid    :"#ba55d3",
       
   151     mediumpurple    :"#9370d8",
       
   152     mediumseagreen  :"#3cb371",
       
   153     mediumslateblue :"#7b68ee",
       
   154     mediumspringgreen   :"#00fa9a",
       
   155     mediumturquoise :"#48d1cc",
       
   156     mediumvioletred :"#c71585",
       
   157     midnightblue    :"#191970",
       
   158     mintcream       :"#f5fffa",
       
   159     mistyrose       :"#ffe4e1",
       
   160     moccasin        :"#ffe4b5",
       
   161     navajowhite     :"#ffdead",
       
   162     navy            :"#000080",
       
   163     oldlace         :"#fdf5e6",
       
   164     olive           :"#808000",
       
   165     olivedrab       :"#6b8e23",
       
   166     orange          :"#ffa500",
       
   167     orangered       :"#ff4500",
       
   168     orchid          :"#da70d6",
       
   169     palegoldenrod   :"#eee8aa",
       
   170     palegreen       :"#98fb98",
       
   171     paleturquoise   :"#afeeee",
       
   172     palevioletred   :"#d87093",
       
   173     papayawhip      :"#ffefd5",
       
   174     peachpuff       :"#ffdab9",
       
   175     peru            :"#cd853f",
       
   176     pink            :"#ffc0cb",
       
   177     plum            :"#dda0dd",
       
   178     powderblue      :"#b0e0e6",
       
   179     purple          :"#800080",
       
   180     red             :"#ff0000",
       
   181     rosybrown       :"#bc8f8f",
       
   182     royalblue       :"#4169e1",
       
   183     saddlebrown     :"#8b4513",
       
   184     salmon          :"#fa8072",
       
   185     sandybrown      :"#f4a460",
       
   186     seagreen        :"#2e8b57",
       
   187     seashell        :"#fff5ee",
       
   188     sienna          :"#a0522d",
       
   189     silver          :"#c0c0c0",
       
   190     skyblue         :"#87ceeb",
       
   191     slateblue       :"#6a5acd",
       
   192     slategray       :"#708090",
       
   193     slategrey       :"#708090",
       
   194     snow            :"#fffafa",
       
   195     springgreen     :"#00ff7f",
       
   196     steelblue       :"#4682b4",
       
   197     tan             :"#d2b48c",
       
   198     teal            :"#008080",
       
   199     thistle         :"#d8bfd8",
       
   200     tomato          :"#ff6347",
       
   201     turquoise       :"#40e0d0",
       
   202     violet          :"#ee82ee",
       
   203     wheat           :"#f5deb3",
       
   204     white           :"#ffffff",
       
   205     whitesmoke      :"#f5f5f5",
       
   206     yellow          :"#ffff00",
       
   207     yellowgreen     :"#9acd32",
       
   208     //'currentColor' color keyword https://www.w3.org/TR/css3-color/#currentcolor
       
   209     currentColor        :"The value of the 'color' property.",
       
   210     //CSS2 system colors https://www.w3.org/TR/css3-color/#css2-system
       
   211     activeBorder        :"Active window border.",
       
   212     activecaption       :"Active window caption.",
       
   213     appworkspace        :"Background color of multiple document interface.",
       
   214     background          :"Desktop background.",
       
   215     buttonface          :"The face background color for 3-D elements that appear 3-D due to one layer of surrounding border.",
       
   216     buttonhighlight     :"The color of the border facing the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",
       
   217     buttonshadow        :"The color of the border away from the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",
       
   218     buttontext          :"Text on push buttons.",
       
   219     captiontext         :"Text in caption, size box, and scrollbar arrow box.",
       
   220     graytext            :"Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.",
       
   221     greytext            :"Greyed (disabled) text. This color is set to #000 if the current display driver does not support a solid grey color.",
       
   222     highlight           :"Item(s) selected in a control.",
       
   223     highlighttext       :"Text of item(s) selected in a control.",
       
   224     inactiveborder      :"Inactive window border.",
       
   225     inactivecaption     :"Inactive window caption.",
       
   226     inactivecaptiontext :"Color of text in an inactive caption.",
       
   227     infobackground      :"Background color for tooltip controls.",
       
   228     infotext            :"Text color for tooltip controls.",
       
   229     menu                :"Menu background.",
       
   230     menutext            :"Text in menus.",
       
   231     scrollbar           :"Scroll bar gray area.",
       
   232     threeddarkshadow    :"The color of the darker (generally outer) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
       
   233     threedface          :"The face background color for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
       
   234     threedhighlight     :"The color of the lighter (generally outer) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
       
   235     threedlightshadow   :"The color of the darker (generally inner) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
       
   236     threedshadow        :"The color of the lighter (generally inner) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
       
   237     window              :"Window background.",
       
   238     windowframe         :"Window frame.",
       
   239     windowtext          :"Text in windows."
       
   240 };
       
   241 
       
   242 },{}],2:[function(require,module,exports){
       
   243 "use strict";
       
   244 
       
   245 module.exports = Combinator;
       
   246 
       
   247 var SyntaxUnit = require("../util/SyntaxUnit");
       
   248 
       
   249 var Parser = require("./Parser");
       
   250 
       
   251 /**
       
   252  * Represents a selector combinator (whitespace, +, >).
       
   253  * @namespace parserlib.css
       
   254  * @class Combinator
       
   255  * @extends parserlib.util.SyntaxUnit
       
   256  * @constructor
       
   257  * @param {String} text The text representation of the unit.
       
   258  * @param {int} line The line of text on which the unit resides.
       
   259  * @param {int} col The column of text on which the unit resides.
       
   260  */
       
   261 function Combinator(text, line, col) {
       
   262 
       
   263     SyntaxUnit.call(this, text, line, col, Parser.COMBINATOR_TYPE);
       
   264 
       
   265     /**
       
   266      * The type of modifier.
       
   267      * @type String
       
   268      * @property type
       
   269      */
       
   270     this.type = "unknown";
       
   271 
       
   272     //pretty simple
       
   273     if (/^\s+$/.test(text)) {
       
   274         this.type = "descendant";
       
   275     } else if (text === ">") {
       
   276         this.type = "child";
       
   277     } else if (text === "+") {
       
   278         this.type = "adjacent-sibling";
       
   279     } else if (text === "~") {
       
   280         this.type = "sibling";
       
   281     }
       
   282 
       
   283 }
       
   284 
       
   285 Combinator.prototype = new SyntaxUnit();
       
   286 Combinator.prototype.constructor = Combinator;
       
   287 
       
   288 
       
   289 },{"../util/SyntaxUnit":26,"./Parser":6}],3:[function(require,module,exports){
       
   290 "use strict";
       
   291 
       
   292 module.exports = Matcher;
       
   293 
       
   294 var StringReader = require("../util/StringReader");
       
   295 var SyntaxError = require("../util/SyntaxError");
       
   296 
       
   297 /**
       
   298  * This class implements a combinator library for matcher functions.
       
   299  * The combinators are described at:
       
   300  * https://developer.mozilla.org/en-US/docs/Web/CSS/Value_definition_syntax#Component_value_combinators
       
   301  */
       
   302 function Matcher(matchFunc, toString) {
       
   303     this.match = function(expression) {
       
   304         // Save/restore marks to ensure that failed matches always restore
       
   305         // the original location in the expression.
       
   306         var result;
       
   307         expression.mark();
       
   308         result = matchFunc(expression);
       
   309         if (result) {
       
   310             expression.drop();
       
   311         } else {
       
   312             expression.restore();
       
   313         }
       
   314         return result;
       
   315     };
       
   316     this.toString = typeof toString === "function" ? toString : function() {
       
   317         return toString;
       
   318     };
       
   319 }
       
   320 
       
   321 /** Precedence table of combinators. */
       
   322 Matcher.prec = {
       
   323     MOD:    5,
       
   324     SEQ:    4,
       
   325     ANDAND: 3,
       
   326     OROR:   2,
       
   327     ALT:    1
       
   328 };
       
   329 
       
   330 /** Simple recursive-descent grammar to build matchers from strings. */
       
   331 Matcher.parse = function(str) {
       
   332     var reader, eat, expr, oror, andand, seq, mod, term, result;
       
   333     reader = new StringReader(str);
       
   334     eat = function(matcher) {
       
   335         var result = reader.readMatch(matcher);
       
   336         if (result === null) {
       
   337             throw new SyntaxError(
       
   338                 "Expected "+matcher, reader.getLine(), reader.getCol());
       
   339         }
       
   340         return result;
       
   341     };
       
   342     expr = function() {
       
   343         // expr = oror (" | " oror)*
       
   344         var m = [ oror() ];
       
   345         while (reader.readMatch(" | ") !== null) {
       
   346             m.push(oror());
       
   347         }
       
   348         return m.length === 1 ? m[0] : Matcher.alt.apply(Matcher, m);
       
   349     };
       
   350     oror = function() {
       
   351         // oror = andand ( " || " andand)*
       
   352         var m = [ andand() ];
       
   353         while (reader.readMatch(" || ") !== null) {
       
   354             m.push(andand());
       
   355         }
       
   356         return m.length === 1 ? m[0] : Matcher.oror.apply(Matcher, m);
       
   357     };
       
   358     andand = function() {
       
   359         // andand = seq ( " && " seq)*
       
   360         var m = [ seq() ];
       
   361         while (reader.readMatch(" && ") !== null) {
       
   362             m.push(seq());
       
   363         }
       
   364         return m.length === 1 ? m[0] : Matcher.andand.apply(Matcher, m);
       
   365     };
       
   366     seq = function() {
       
   367         // seq = mod ( " " mod)*
       
   368         var m = [ mod() ];
       
   369         while (reader.readMatch(/^ (?![&|\]])/) !== null) {
       
   370             m.push(mod());
       
   371         }
       
   372         return m.length === 1 ? m[0] : Matcher.seq.apply(Matcher, m);
       
   373     };
       
   374     mod = function() {
       
   375         // mod = term ( "?" | "*" | "+" | "#" | "{<num>,<num>}" )?
       
   376         var m = term();
       
   377         if (reader.readMatch("?") !== null) {
       
   378             return m.question();
       
   379         } else if (reader.readMatch("*") !== null) {
       
   380             return m.star();
       
   381         } else if (reader.readMatch("+") !== null) {
       
   382             return m.plus();
       
   383         } else if (reader.readMatch("#") !== null) {
       
   384             return m.hash();
       
   385         } else if (reader.readMatch(/^\{\s*/) !== null) {
       
   386             var min = eat(/^\d+/);
       
   387             eat(/^\s*,\s*/);
       
   388             var max = eat(/^\d+/);
       
   389             eat(/^\s*\}/);
       
   390             return m.braces(+min, +max);
       
   391         }
       
   392         return m;
       
   393     };
       
   394     term = function() {
       
   395         // term = <nt> | literal | "[ " expression " ]"
       
   396         if (reader.readMatch("[ ") !== null) {
       
   397             var m = expr();
       
   398             eat(" ]");
       
   399             return m;
       
   400         }
       
   401         return Matcher.fromType(eat(/^[^ ?*+#{]+/));
       
   402     };
       
   403     result = expr();
       
   404     if (!reader.eof()) {
       
   405         throw new SyntaxError(
       
   406             "Expected end of string", reader.getLine(), reader.getCol());
       
   407     }
       
   408     return result;
       
   409 };
       
   410 
       
   411 /**
       
   412  * Convert a string to a matcher (parsing simple alternations),
       
   413  * or do nothing if the argument is already a matcher.
       
   414  */
       
   415 Matcher.cast = function(m) {
       
   416     if (m instanceof Matcher) {
       
   417         return m;
       
   418     }
       
   419     return Matcher.parse(m);
       
   420 };
       
   421 
       
   422 /**
       
   423  * Create a matcher for a single type.
       
   424  */
       
   425 Matcher.fromType = function(type) {
       
   426     // Late require of ValidationTypes to break a dependency cycle.
       
   427     var ValidationTypes = require("./ValidationTypes");
       
   428     return new Matcher(function(expression) {
       
   429         return expression.hasNext() && ValidationTypes.isType(expression, type);
       
   430     }, type);
       
   431 };
       
   432 
       
   433 /**
       
   434  * Create a matcher for one or more juxtaposed words, which all must
       
   435  * occur, in the given order.
       
   436  */
       
   437 Matcher.seq = function() {
       
   438     var ms = Array.prototype.slice.call(arguments).map(Matcher.cast);
       
   439     if (ms.length === 1) {
       
   440         return ms[0];
       
   441     }
       
   442     return new Matcher(function(expression) {
       
   443         var i, result = true;
       
   444         for (i = 0; result && i < ms.length; i++) {
       
   445             result = ms[i].match(expression);
       
   446         }
       
   447         return result;
       
   448     }, function(prec) {
       
   449         var p = Matcher.prec.SEQ;
       
   450         var s = ms.map(function(m) {
       
   451             return m.toString(p);
       
   452         }).join(" ");
       
   453         if (prec > p) {
       
   454             s = "[ " + s + " ]";
       
   455         }
       
   456         return s;
       
   457     });
       
   458 };
       
   459 
       
   460 /**
       
   461  * Create a matcher for one or more alternatives, where exactly one
       
   462  * must occur.
       
   463  */
       
   464 Matcher.alt = function() {
       
   465     var ms = Array.prototype.slice.call(arguments).map(Matcher.cast);
       
   466     if (ms.length === 1) {
       
   467         return ms[0];
       
   468     }
       
   469     return new Matcher(function(expression) {
       
   470         var i, result = false;
       
   471         for (i = 0; !result && i < ms.length; i++) {
       
   472             result = ms[i].match(expression);
       
   473         }
       
   474         return result;
       
   475     }, function(prec) {
       
   476         var p = Matcher.prec.ALT;
       
   477         var s = ms.map(function(m) {
       
   478             return m.toString(p);
       
   479         }).join(" | ");
       
   480         if (prec > p) {
       
   481             s = "[ " + s + " ]";
       
   482         }
       
   483         return s;
       
   484     });
       
   485 };
       
   486 
       
   487 /**
       
   488  * Create a matcher for two or more options.  This implements the
       
   489  * double bar (||) and double ampersand (&&) operators, as well as
       
   490  * variants of && where some of the alternatives are optional.
       
   491  * This will backtrack through even successful matches to try to
       
   492  * maximize the number of items matched.
       
   493  */
       
   494 Matcher.many = function(required) {
       
   495     var ms = Array.prototype.slice.call(arguments, 1).reduce(function(acc, v) {
       
   496         if (v.expand) {
       
   497             // Insert all of the options for the given complex rule as
       
   498             // individual options.
       
   499             var ValidationTypes = require("./ValidationTypes");
       
   500             acc.push.apply(acc, ValidationTypes.complex[v.expand].options);
       
   501         } else {
       
   502             acc.push(Matcher.cast(v));
       
   503         }
       
   504         return acc;
       
   505     }, []);
       
   506 
       
   507     if (required === true) {
       
   508         required = ms.map(function() {
       
   509             return true;
       
   510         });
       
   511     }
       
   512 
       
   513     var result = new Matcher(function(expression) {
       
   514         var seen = [], max = 0, pass = 0;
       
   515         var success = function(matchCount) {
       
   516             if (pass === 0) {
       
   517                 max = Math.max(matchCount, max);
       
   518                 return matchCount === ms.length;
       
   519             } else {
       
   520                 return matchCount === max;
       
   521             }
       
   522         };
       
   523         var tryMatch = function(matchCount) {
       
   524             for (var i = 0; i < ms.length; i++) {
       
   525                 if (seen[i]) {
       
   526                     continue;
       
   527                 }
       
   528                 expression.mark();
       
   529                 if (ms[i].match(expression)) {
       
   530                     seen[i] = true;
       
   531                     // Increase matchCount iff this was a required element
       
   532                     // (or if all the elements are optional)
       
   533                     if (tryMatch(matchCount + ((required === false || required[i]) ? 1 : 0))) {
       
   534                         expression.drop();
       
   535                         return true;
       
   536                     }
       
   537                     // Backtrack: try *not* matching using this rule, and
       
   538                     // let's see if it leads to a better overall match.
       
   539                     expression.restore();
       
   540                     seen[i] = false;
       
   541                 } else {
       
   542                     expression.drop();
       
   543                 }
       
   544             }
       
   545             return success(matchCount);
       
   546         };
       
   547         if (!tryMatch(0)) {
       
   548             // Couldn't get a complete match, retrace our steps to make the
       
   549             // match with the maximum # of required elements.
       
   550             pass++;
       
   551             tryMatch(0);
       
   552         }
       
   553 
       
   554         if (required === false) {
       
   555             return max > 0;
       
   556         }
       
   557         // Use finer-grained specification of which matchers are required.
       
   558         for (var i = 0; i < ms.length; i++) {
       
   559             if (required[i] && !seen[i]) {
       
   560                 return false;
       
   561             }
       
   562         }
       
   563         return true;
       
   564     }, function(prec) {
       
   565         var p = required === false ? Matcher.prec.OROR : Matcher.prec.ANDAND;
       
   566         var s = ms.map(function(m, i) {
       
   567             if (required !== false && !required[i]) {
       
   568                 return m.toString(Matcher.prec.MOD) + "?";
       
   569             }
       
   570             return m.toString(p);
       
   571         }).join(required === false ? " || " : " && ");
       
   572         if (prec > p) {
       
   573             s = "[ " + s + " ]";
       
   574         }
       
   575         return s;
       
   576     });
       
   577     result.options = ms;
       
   578     return result;
       
   579 };
       
   580 
       
   581 /**
       
   582  * Create a matcher for two or more options, where all options are
       
   583  * mandatory but they may appear in any order.
       
   584  */
       
   585 Matcher.andand = function() {
       
   586     var args = Array.prototype.slice.call(arguments);
       
   587     args.unshift(true);
       
   588     return Matcher.many.apply(Matcher, args);
       
   589 };
       
   590 
       
   591 /**
       
   592  * Create a matcher for two or more options, where options are
       
   593  * optional and may appear in any order, but at least one must be
       
   594  * present.
       
   595  */
       
   596 Matcher.oror = function() {
       
   597     var args = Array.prototype.slice.call(arguments);
       
   598     args.unshift(false);
       
   599     return Matcher.many.apply(Matcher, args);
       
   600 };
       
   601 
       
   602 /** Instance methods on Matchers. */
       
   603 Matcher.prototype = {
       
   604     constructor: Matcher,
       
   605     // These are expected to be overridden in every instance.
       
   606     match: function() { throw new Error("unimplemented"); },
       
   607     toString: function() { throw new Error("unimplemented"); },
       
   608     // This returns a standalone function to do the matching.
       
   609     func: function() { return this.match.bind(this); },
       
   610     // Basic combinators
       
   611     then: function(m) { return Matcher.seq(this, m); },
       
   612     or: function(m) { return Matcher.alt(this, m); },
       
   613     andand: function(m) { return Matcher.many(true, this, m); },
       
   614     oror: function(m) { return Matcher.many(false, this, m); },
       
   615     // Component value multipliers
       
   616     star: function() { return this.braces(0, Infinity, "*"); },
       
   617     plus: function() { return this.braces(1, Infinity, "+"); },
       
   618     question: function() { return this.braces(0, 1, "?"); },
       
   619     hash: function() {
       
   620         return this.braces(1, Infinity, "#", Matcher.cast(","));
       
   621     },
       
   622     braces: function(min, max, marker, optSep) {
       
   623         var m1 = this, m2 = optSep ? optSep.then(this) : this;
       
   624         if (!marker) {
       
   625             marker = "{" + min + "," + max + "}";
       
   626         }
       
   627         return new Matcher(function(expression) {
       
   628             var result = true, i;
       
   629             for (i = 0; i < max; i++) {
       
   630                 if (i > 0 && optSep) {
       
   631                     result = m2.match(expression);
       
   632                 } else {
       
   633                     result = m1.match(expression);
       
   634                 }
       
   635                 if (!result) {
       
   636                     break;
       
   637                 }
       
   638             }
       
   639             return i >= min;
       
   640         }, function() {
       
   641             return m1.toString(Matcher.prec.MOD) + marker;
       
   642         });
       
   643     }
       
   644 };
       
   645 
       
   646 },{"../util/StringReader":24,"../util/SyntaxError":25,"./ValidationTypes":21}],4:[function(require,module,exports){
       
   647 "use strict";
       
   648 
       
   649 module.exports = MediaFeature;
       
   650 
       
   651 var SyntaxUnit = require("../util/SyntaxUnit");
       
   652 
       
   653 var Parser = require("./Parser");
       
   654 
       
   655 /**
       
   656  * Represents a media feature, such as max-width:500.
       
   657  * @namespace parserlib.css
       
   658  * @class MediaFeature
       
   659  * @extends parserlib.util.SyntaxUnit
       
   660  * @constructor
       
   661  * @param {SyntaxUnit} name The name of the feature.
       
   662  * @param {SyntaxUnit} value The value of the feature or null if none.
       
   663  */
       
   664 function MediaFeature(name, value) {
       
   665 
       
   666     SyntaxUnit.call(this, "(" + name + (value !== null ? ":" + value : "") + ")", name.startLine, name.startCol, Parser.MEDIA_FEATURE_TYPE);
       
   667 
       
   668     /**
       
   669      * The name of the media feature
       
   670      * @type String
       
   671      * @property name
       
   672      */
       
   673     this.name = name;
       
   674 
       
   675     /**
       
   676      * The value for the feature or null if there is none.
       
   677      * @type SyntaxUnit
       
   678      * @property value
       
   679      */
       
   680     this.value = value;
       
   681 }
       
   682 
       
   683 MediaFeature.prototype = new SyntaxUnit();
       
   684 MediaFeature.prototype.constructor = MediaFeature;
       
   685 
       
   686 
       
   687 },{"../util/SyntaxUnit":26,"./Parser":6}],5:[function(require,module,exports){
       
   688 "use strict";
       
   689 
       
   690 module.exports = MediaQuery;
       
   691 
       
   692 var SyntaxUnit = require("../util/SyntaxUnit");
       
   693 
       
   694 var Parser = require("./Parser");
       
   695 
       
   696 /**
       
   697  * Represents an individual media query.
       
   698  * @namespace parserlib.css
       
   699  * @class MediaQuery
       
   700  * @extends parserlib.util.SyntaxUnit
       
   701  * @constructor
       
   702  * @param {String} modifier The modifier "not" or "only" (or null).
       
   703  * @param {String} mediaType The type of media (i.e., "print").
       
   704  * @param {Array} parts Array of selectors parts making up this selector.
       
   705  * @param {int} line The line of text on which the unit resides.
       
   706  * @param {int} col The column of text on which the unit resides.
       
   707  */
       
   708 function MediaQuery(modifier, mediaType, features, line, col) {
       
   709 
       
   710     SyntaxUnit.call(this, (modifier ? modifier + " ": "") + (mediaType ? mediaType : "") + (mediaType && features.length > 0 ? " and " : "") + features.join(" and "), line, col, Parser.MEDIA_QUERY_TYPE);
       
   711 
       
   712     /**
       
   713      * The media modifier ("not" or "only")
       
   714      * @type String
       
   715      * @property modifier
       
   716      */
       
   717     this.modifier = modifier;
       
   718 
       
   719     /**
       
   720      * The mediaType (i.e., "print")
       
   721      * @type String
       
   722      * @property mediaType
       
   723      */
       
   724     this.mediaType = mediaType;
       
   725 
       
   726     /**
       
   727      * The parts that make up the selector.
       
   728      * @type Array
       
   729      * @property features
       
   730      */
       
   731     this.features = features;
       
   732 
       
   733 }
       
   734 
       
   735 MediaQuery.prototype = new SyntaxUnit();
       
   736 MediaQuery.prototype.constructor = MediaQuery;
       
   737 
       
   738 
       
   739 },{"../util/SyntaxUnit":26,"./Parser":6}],6:[function(require,module,exports){
       
   740 "use strict";
       
   741 
       
   742 module.exports = Parser;
       
   743 
       
   744 var EventTarget = require("../util/EventTarget");
       
   745 var SyntaxError = require("../util/SyntaxError");
       
   746 var SyntaxUnit = require("../util/SyntaxUnit");
       
   747 
       
   748 var Combinator = require("./Combinator");
       
   749 var MediaFeature = require("./MediaFeature");
       
   750 var MediaQuery = require("./MediaQuery");
       
   751 var PropertyName = require("./PropertyName");
       
   752 var PropertyValue = require("./PropertyValue");
       
   753 var PropertyValuePart = require("./PropertyValuePart");
       
   754 var Selector = require("./Selector");
       
   755 var SelectorPart = require("./SelectorPart");
       
   756 var SelectorSubPart = require("./SelectorSubPart");
       
   757 var TokenStream = require("./TokenStream");
       
   758 var Tokens = require("./Tokens");
       
   759 var Validation = require("./Validation");
       
   760 
       
   761 /**
       
   762  * A CSS3 parser.
       
   763  * @namespace parserlib.css
       
   764  * @class Parser
       
   765  * @constructor
       
   766  * @param {Object} options (Optional) Various options for the parser:
       
   767  *      starHack (true|false) to allow IE6 star hack as valid,
       
   768  *      underscoreHack (true|false) to interpret leading underscores
       
   769  *      as IE6-7 targeting for known properties, ieFilters (true|false)
       
   770  *      to indicate that IE < 8 filters should be accepted and not throw
       
   771  *      syntax errors.
       
   772  */
       
   773 function Parser(options) {
       
   774 
       
   775     //inherit event functionality
       
   776     EventTarget.call(this);
       
   777 
       
   778 
       
   779     this.options = options || {};
       
   780 
       
   781     this._tokenStream = null;
       
   782 }
       
   783 
       
   784 //Static constants
       
   785 Parser.DEFAULT_TYPE = 0;
       
   786 Parser.COMBINATOR_TYPE = 1;
       
   787 Parser.MEDIA_FEATURE_TYPE = 2;
       
   788 Parser.MEDIA_QUERY_TYPE = 3;
       
   789 Parser.PROPERTY_NAME_TYPE = 4;
       
   790 Parser.PROPERTY_VALUE_TYPE = 5;
       
   791 Parser.PROPERTY_VALUE_PART_TYPE = 6;
       
   792 Parser.SELECTOR_TYPE = 7;
       
   793 Parser.SELECTOR_PART_TYPE = 8;
       
   794 Parser.SELECTOR_SUB_PART_TYPE = 9;
       
   795 
       
   796 Parser.prototype = function() {
       
   797 
       
   798     var proto = new EventTarget(),  //new prototype
       
   799         prop,
       
   800         additions =  {
       
   801             __proto__: null,
       
   802 
       
   803             //restore constructor
       
   804             constructor: Parser,
       
   805 
       
   806             //instance constants - yuck
       
   807             DEFAULT_TYPE : 0,
       
   808             COMBINATOR_TYPE : 1,
       
   809             MEDIA_FEATURE_TYPE : 2,
       
   810             MEDIA_QUERY_TYPE : 3,
       
   811             PROPERTY_NAME_TYPE : 4,
       
   812             PROPERTY_VALUE_TYPE : 5,
       
   813             PROPERTY_VALUE_PART_TYPE : 6,
       
   814             SELECTOR_TYPE : 7,
       
   815             SELECTOR_PART_TYPE : 8,
       
   816             SELECTOR_SUB_PART_TYPE : 9,
       
   817 
       
   818             //-----------------------------------------------------------------
       
   819             // Grammar
       
   820             //-----------------------------------------------------------------
       
   821 
       
   822             _stylesheet: function() {
       
   823 
       
   824                 /*
       
   825                  * stylesheet
       
   826                  *  : [ CHARSET_SYM S* STRING S* ';' ]?
       
   827                  *    [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
       
   828                  *    [ namespace [S|CDO|CDC]* ]*
       
   829                  *    [ [ ruleset | media | page | font_face | keyframes_rule | supports_rule ] [S|CDO|CDC]* ]*
       
   830                  *  ;
       
   831                  */
       
   832 
       
   833                 var tokenStream = this._tokenStream,
       
   834                     count,
       
   835                     token,
       
   836                     tt;
       
   837 
       
   838                 this.fire("startstylesheet");
       
   839 
       
   840                 //try to read character set
       
   841                 this._charset();
       
   842 
       
   843                 this._skipCruft();
       
   844 
       
   845                 //try to read imports - may be more than one
       
   846                 while (tokenStream.peek() === Tokens.IMPORT_SYM) {
       
   847                     this._import();
       
   848                     this._skipCruft();
       
   849                 }
       
   850 
       
   851                 //try to read namespaces - may be more than one
       
   852                 while (tokenStream.peek() === Tokens.NAMESPACE_SYM) {
       
   853                     this._namespace();
       
   854                     this._skipCruft();
       
   855                 }
       
   856 
       
   857                 //get the next token
       
   858                 tt = tokenStream.peek();
       
   859 
       
   860                 //try to read the rest
       
   861                 while (tt > Tokens.EOF) {
       
   862 
       
   863                     try {
       
   864 
       
   865                         switch (tt) {
       
   866                             case Tokens.MEDIA_SYM:
       
   867                                 this._media();
       
   868                                 this._skipCruft();
       
   869                                 break;
       
   870                             case Tokens.PAGE_SYM:
       
   871                                 this._page();
       
   872                                 this._skipCruft();
       
   873                                 break;
       
   874                             case Tokens.FONT_FACE_SYM:
       
   875                                 this._font_face();
       
   876                                 this._skipCruft();
       
   877                                 break;
       
   878                             case Tokens.KEYFRAMES_SYM:
       
   879                                 this._keyframes();
       
   880                                 this._skipCruft();
       
   881                                 break;
       
   882                             case Tokens.VIEWPORT_SYM:
       
   883                                 this._viewport();
       
   884                                 this._skipCruft();
       
   885                                 break;
       
   886                             case Tokens.DOCUMENT_SYM:
       
   887                                 this._document();
       
   888                                 this._skipCruft();
       
   889                                 break;
       
   890                             case Tokens.SUPPORTS_SYM:
       
   891                                 this._supports();
       
   892                                 this._skipCruft();
       
   893                                 break;
       
   894                             case Tokens.UNKNOWN_SYM:  //unknown @ rule
       
   895                                 tokenStream.get();
       
   896                                 if (!this.options.strict) {
       
   897 
       
   898                                     //fire error event
       
   899                                     this.fire({
       
   900                                         type:       "error",
       
   901                                         error:      null,
       
   902                                         message:    "Unknown @ rule: " + tokenStream.LT(0).value + ".",
       
   903                                         line:       tokenStream.LT(0).startLine,
       
   904                                         col:        tokenStream.LT(0).startCol
       
   905                                     });
       
   906 
       
   907                                     //skip braces
       
   908                                     count=0;
       
   909                                     while (tokenStream.advance([Tokens.LBRACE, Tokens.RBRACE]) === Tokens.LBRACE) {
       
   910                                         count++;    //keep track of nesting depth
       
   911                                     }
       
   912 
       
   913                                     while (count) {
       
   914                                         tokenStream.advance([Tokens.RBRACE]);
       
   915                                         count--;
       
   916                                     }
       
   917 
       
   918                                 } else {
       
   919                                     //not a syntax error, rethrow it
       
   920                                     throw new SyntaxError("Unknown @ rule.", tokenStream.LT(0).startLine, tokenStream.LT(0).startCol);
       
   921                                 }
       
   922                                 break;
       
   923                             case Tokens.S:
       
   924                                 this._readWhitespace();
       
   925                                 break;
       
   926                             default:
       
   927                                 if (!this._ruleset()) {
       
   928 
       
   929                                     //error handling for known issues
       
   930                                     switch (tt) {
       
   931                                         case Tokens.CHARSET_SYM:
       
   932                                             token = tokenStream.LT(1);
       
   933                                             this._charset(false);
       
   934                                             throw new SyntaxError("@charset not allowed here.", token.startLine, token.startCol);
       
   935                                         case Tokens.IMPORT_SYM:
       
   936                                             token = tokenStream.LT(1);
       
   937                                             this._import(false);
       
   938                                             throw new SyntaxError("@import not allowed here.", token.startLine, token.startCol);
       
   939                                         case Tokens.NAMESPACE_SYM:
       
   940                                             token = tokenStream.LT(1);
       
   941                                             this._namespace(false);
       
   942                                             throw new SyntaxError("@namespace not allowed here.", token.startLine, token.startCol);
       
   943                                         default:
       
   944                                             tokenStream.get();  //get the last token
       
   945                                             this._unexpectedToken(tokenStream.token());
       
   946                                     }
       
   947 
       
   948                                 }
       
   949                         }
       
   950                     } catch (ex) {
       
   951                         if (ex instanceof SyntaxError && !this.options.strict) {
       
   952                             this.fire({
       
   953                                 type:       "error",
       
   954                                 error:      ex,
       
   955                                 message:    ex.message,
       
   956                                 line:       ex.line,
       
   957                                 col:        ex.col
       
   958                             });
       
   959                         } else {
       
   960                             throw ex;
       
   961                         }
       
   962                     }
       
   963 
       
   964                     tt = tokenStream.peek();
       
   965                 }
       
   966 
       
   967                 if (tt !== Tokens.EOF) {
       
   968                     this._unexpectedToken(tokenStream.token());
       
   969                 }
       
   970 
       
   971                 this.fire("endstylesheet");
       
   972             },
       
   973 
       
   974             _charset: function(emit) {
       
   975                 var tokenStream = this._tokenStream,
       
   976                     charset,
       
   977                     token,
       
   978                     line,
       
   979                     col;
       
   980 
       
   981                 if (tokenStream.match(Tokens.CHARSET_SYM)) {
       
   982                     line = tokenStream.token().startLine;
       
   983                     col = tokenStream.token().startCol;
       
   984 
       
   985                     this._readWhitespace();
       
   986                     tokenStream.mustMatch(Tokens.STRING);
       
   987 
       
   988                     token = tokenStream.token();
       
   989                     charset = token.value;
       
   990 
       
   991                     this._readWhitespace();
       
   992                     tokenStream.mustMatch(Tokens.SEMICOLON);
       
   993 
       
   994                     if (emit !== false) {
       
   995                         this.fire({
       
   996                             type:   "charset",
       
   997                             charset:charset,
       
   998                             line:   line,
       
   999                             col:    col
       
  1000                         });
       
  1001                     }
       
  1002                 }
       
  1003             },
       
  1004 
       
  1005             _import: function(emit) {
       
  1006                 /*
       
  1007                  * import
       
  1008                  *   : IMPORT_SYM S*
       
  1009                  *    [STRING|URI] S* media_query_list? ';' S*
       
  1010                  */
       
  1011 
       
  1012                 var tokenStream = this._tokenStream,
       
  1013                     uri,
       
  1014                     importToken,
       
  1015                     mediaList   = [];
       
  1016 
       
  1017                 //read import symbol
       
  1018                 tokenStream.mustMatch(Tokens.IMPORT_SYM);
       
  1019                 importToken = tokenStream.token();
       
  1020                 this._readWhitespace();
       
  1021 
       
  1022                 tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
       
  1023 
       
  1024                 //grab the URI value
       
  1025                 uri = tokenStream.token().value.replace(/^(?:url\()?["']?([^"']+?)["']?\)?$/, "$1");
       
  1026 
       
  1027                 this._readWhitespace();
       
  1028 
       
  1029                 mediaList = this._media_query_list();
       
  1030 
       
  1031                 //must end with a semicolon
       
  1032                 tokenStream.mustMatch(Tokens.SEMICOLON);
       
  1033                 this._readWhitespace();
       
  1034 
       
  1035                 if (emit !== false) {
       
  1036                     this.fire({
       
  1037                         type:   "import",
       
  1038                         uri:    uri,
       
  1039                         media:  mediaList,
       
  1040                         line:   importToken.startLine,
       
  1041                         col:    importToken.startCol
       
  1042                     });
       
  1043                 }
       
  1044 
       
  1045             },
       
  1046 
       
  1047             _namespace: function(emit) {
       
  1048                 /*
       
  1049                  * namespace
       
  1050                  *   : NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
       
  1051                  */
       
  1052 
       
  1053                 var tokenStream = this._tokenStream,
       
  1054                     line,
       
  1055                     col,
       
  1056                     prefix,
       
  1057                     uri;
       
  1058 
       
  1059                 //read import symbol
       
  1060                 tokenStream.mustMatch(Tokens.NAMESPACE_SYM);
       
  1061                 line = tokenStream.token().startLine;
       
  1062                 col = tokenStream.token().startCol;
       
  1063                 this._readWhitespace();
       
  1064 
       
  1065                 //it's a namespace prefix - no _namespace_prefix() method because it's just an IDENT
       
  1066                 if (tokenStream.match(Tokens.IDENT)) {
       
  1067                     prefix = tokenStream.token().value;
       
  1068                     this._readWhitespace();
       
  1069                 }
       
  1070 
       
  1071                 tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
       
  1072                 /*if (!tokenStream.match(Tokens.STRING)){
       
  1073                     tokenStream.mustMatch(Tokens.URI);
       
  1074                 }*/
       
  1075 
       
  1076                 //grab the URI value
       
  1077                 uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1");
       
  1078 
       
  1079                 this._readWhitespace();
       
  1080 
       
  1081                 //must end with a semicolon
       
  1082                 tokenStream.mustMatch(Tokens.SEMICOLON);
       
  1083                 this._readWhitespace();
       
  1084 
       
  1085                 if (emit !== false) {
       
  1086                     this.fire({
       
  1087                         type:   "namespace",
       
  1088                         prefix: prefix,
       
  1089                         uri:    uri,
       
  1090                         line:   line,
       
  1091                         col:    col
       
  1092                     });
       
  1093                 }
       
  1094 
       
  1095             },
       
  1096 
       
  1097             _supports: function(emit) {
       
  1098                 /*
       
  1099                  * supports_rule
       
  1100                  *  : SUPPORTS_SYM S* supports_condition S* group_rule_body
       
  1101                  *  ;
       
  1102                  */
       
  1103                 var tokenStream = this._tokenStream,
       
  1104                     line,
       
  1105                     col;
       
  1106 
       
  1107                 if (tokenStream.match(Tokens.SUPPORTS_SYM)) {
       
  1108                     line = tokenStream.token().startLine;
       
  1109                     col = tokenStream.token().startCol;
       
  1110 
       
  1111                     this._readWhitespace();
       
  1112                     this._supports_condition();
       
  1113                     this._readWhitespace();
       
  1114 
       
  1115                     tokenStream.mustMatch(Tokens.LBRACE);
       
  1116                     this._readWhitespace();
       
  1117 
       
  1118                     if (emit !== false) {
       
  1119                         this.fire({
       
  1120                             type:   "startsupports",
       
  1121                             line:   line,
       
  1122                             col:    col
       
  1123                         });
       
  1124                     }
       
  1125 
       
  1126                     while (true) {
       
  1127                         if (!this._ruleset()) {
       
  1128                             break;
       
  1129                         }
       
  1130                     }
       
  1131 
       
  1132                     tokenStream.mustMatch(Tokens.RBRACE);
       
  1133                     this._readWhitespace();
       
  1134 
       
  1135                     this.fire({
       
  1136                         type:   "endsupports",
       
  1137                         line:   line,
       
  1138                         col:    col
       
  1139                     });
       
  1140                 }
       
  1141             },
       
  1142 
       
  1143             _supports_condition: function() {
       
  1144                 /*
       
  1145                  * supports_condition
       
  1146                  *  : supports_negation | supports_conjunction | supports_disjunction |
       
  1147                  *    supports_condition_in_parens
       
  1148                  *  ;
       
  1149                  */
       
  1150                 var tokenStream = this._tokenStream,
       
  1151                     ident;
       
  1152 
       
  1153                 if (tokenStream.match(Tokens.IDENT)) {
       
  1154                     ident = tokenStream.token().value.toLowerCase();
       
  1155 
       
  1156                     if (ident === "not") {
       
  1157                         tokenStream.mustMatch(Tokens.S);
       
  1158                         this._supports_condition_in_parens();
       
  1159                     } else {
       
  1160                         tokenStream.unget();
       
  1161                     }
       
  1162                 } else {
       
  1163                     this._supports_condition_in_parens();
       
  1164                     this._readWhitespace();
       
  1165 
       
  1166                     while (tokenStream.peek() === Tokens.IDENT) {
       
  1167                         ident = tokenStream.LT(1).value.toLowerCase();
       
  1168                         if (ident === "and" || ident === "or") {
       
  1169                             tokenStream.mustMatch(Tokens.IDENT);
       
  1170                             this._readWhitespace();
       
  1171                             this._supports_condition_in_parens();
       
  1172                             this._readWhitespace();
       
  1173                         }
       
  1174                     }
       
  1175                 }
       
  1176             },
       
  1177 
       
  1178             _supports_condition_in_parens: function() {
       
  1179                 /*
       
  1180                  * supports_condition_in_parens
       
  1181                  *  : ( '(' S* supports_condition S* ')' ) | supports_declaration_condition |
       
  1182                  *    general_enclosed
       
  1183                  *  ;
       
  1184                  */
       
  1185                 var tokenStream = this._tokenStream,
       
  1186                     ident;
       
  1187 
       
  1188                 if (tokenStream.match(Tokens.LPAREN)) {
       
  1189                     this._readWhitespace();
       
  1190                     if (tokenStream.match(Tokens.IDENT)) {
       
  1191                         // look ahead for not keyword, if not given, continue with declaration condition.
       
  1192                         ident = tokenStream.token().value.toLowerCase();
       
  1193                         if (ident === "not") {
       
  1194                             this._readWhitespace();
       
  1195                             this._supports_condition();
       
  1196                             this._readWhitespace();
       
  1197                             tokenStream.mustMatch(Tokens.RPAREN);
       
  1198                         } else {
       
  1199                             tokenStream.unget();
       
  1200                             this._supports_declaration_condition(false);
       
  1201                         }
       
  1202                     } else {
       
  1203                         this._supports_condition();
       
  1204                         this._readWhitespace();
       
  1205                         tokenStream.mustMatch(Tokens.RPAREN);
       
  1206                     }
       
  1207                 } else {
       
  1208                     this._supports_declaration_condition();
       
  1209                 }
       
  1210             },
       
  1211 
       
  1212             _supports_declaration_condition: function(requireStartParen) {
       
  1213                 /*
       
  1214                  * supports_declaration_condition
       
  1215                  *  : '(' S* declaration ')'
       
  1216                  *  ;
       
  1217                  */
       
  1218                 var tokenStream = this._tokenStream;
       
  1219 
       
  1220                 if (requireStartParen !== false) {
       
  1221                     tokenStream.mustMatch(Tokens.LPAREN);
       
  1222                 }
       
  1223                 this._readWhitespace();
       
  1224                 this._declaration();
       
  1225                 tokenStream.mustMatch(Tokens.RPAREN);
       
  1226             },
       
  1227 
       
  1228             _media: function() {
       
  1229                 /*
       
  1230                  * media
       
  1231                  *   : MEDIA_SYM S* media_query_list S* '{' S* ruleset* '}' S*
       
  1232                  *   ;
       
  1233                  */
       
  1234                 var tokenStream     = this._tokenStream,
       
  1235                     line,
       
  1236                     col,
       
  1237                     mediaList;//       = [];
       
  1238 
       
  1239                 //look for @media
       
  1240                 tokenStream.mustMatch(Tokens.MEDIA_SYM);
       
  1241                 line = tokenStream.token().startLine;
       
  1242                 col = tokenStream.token().startCol;
       
  1243 
       
  1244                 this._readWhitespace();
       
  1245 
       
  1246                 mediaList = this._media_query_list();
       
  1247 
       
  1248                 tokenStream.mustMatch(Tokens.LBRACE);
       
  1249                 this._readWhitespace();
       
  1250 
       
  1251                 this.fire({
       
  1252                     type:   "startmedia",
       
  1253                     media:  mediaList,
       
  1254                     line:   line,
       
  1255                     col:    col
       
  1256                 });
       
  1257 
       
  1258                 while (true) {
       
  1259                     if (tokenStream.peek() === Tokens.PAGE_SYM) {
       
  1260                         this._page();
       
  1261                     } else if (tokenStream.peek() === Tokens.FONT_FACE_SYM) {
       
  1262                         this._font_face();
       
  1263                     } else if (tokenStream.peek() === Tokens.VIEWPORT_SYM) {
       
  1264                         this._viewport();
       
  1265                     } else if (tokenStream.peek() === Tokens.DOCUMENT_SYM) {
       
  1266                         this._document();
       
  1267                     } else if (tokenStream.peek() === Tokens.SUPPORTS_SYM) {
       
  1268                         this._supports();
       
  1269                     } else if (tokenStream.peek() === Tokens.MEDIA_SYM) {
       
  1270                         this._media();
       
  1271                     } else if (!this._ruleset()) {
       
  1272                         break;
       
  1273                     }
       
  1274                 }
       
  1275 
       
  1276                 tokenStream.mustMatch(Tokens.RBRACE);
       
  1277                 this._readWhitespace();
       
  1278 
       
  1279                 this.fire({
       
  1280                     type:   "endmedia",
       
  1281                     media:  mediaList,
       
  1282                     line:   line,
       
  1283                     col:    col
       
  1284                 });
       
  1285             },
       
  1286 
       
  1287 
       
  1288             //CSS3 Media Queries
       
  1289             _media_query_list: function() {
       
  1290                 /*
       
  1291                  * media_query_list
       
  1292                  *   : S* [media_query [ ',' S* media_query ]* ]?
       
  1293                  *   ;
       
  1294                  */
       
  1295                 var tokenStream = this._tokenStream,
       
  1296                     mediaList   = [];
       
  1297 
       
  1298 
       
  1299                 this._readWhitespace();
       
  1300 
       
  1301                 if (tokenStream.peek() === Tokens.IDENT || tokenStream.peek() === Tokens.LPAREN) {
       
  1302                     mediaList.push(this._media_query());
       
  1303                 }
       
  1304 
       
  1305                 while (tokenStream.match(Tokens.COMMA)) {
       
  1306                     this._readWhitespace();
       
  1307                     mediaList.push(this._media_query());
       
  1308                 }
       
  1309 
       
  1310                 return mediaList;
       
  1311             },
       
  1312 
       
  1313             /*
       
  1314              * Note: "expression" in the grammar maps to the _media_expression
       
  1315              * method.
       
  1316 
       
  1317              */
       
  1318             _media_query: function() {
       
  1319                 /*
       
  1320                  * media_query
       
  1321                  *   : [ONLY | NOT]? S* media_type S* [ AND S* expression ]*
       
  1322                  *   | expression [ AND S* expression ]*
       
  1323                  *   ;
       
  1324                  */
       
  1325                 var tokenStream = this._tokenStream,
       
  1326                     type        = null,
       
  1327                     ident       = null,
       
  1328                     token       = null,
       
  1329                     expressions = [];
       
  1330 
       
  1331                 if (tokenStream.match(Tokens.IDENT)) {
       
  1332                     ident = tokenStream.token().value.toLowerCase();
       
  1333 
       
  1334                     //since there's no custom tokens for these, need to manually check
       
  1335                     if (ident !== "only" && ident !== "not") {
       
  1336                         tokenStream.unget();
       
  1337                         ident = null;
       
  1338                     } else {
       
  1339                         token = tokenStream.token();
       
  1340                     }
       
  1341                 }
       
  1342 
       
  1343                 this._readWhitespace();
       
  1344 
       
  1345                 if (tokenStream.peek() === Tokens.IDENT) {
       
  1346                     type = this._media_type();
       
  1347                     if (token === null) {
       
  1348                         token = tokenStream.token();
       
  1349                     }
       
  1350                 } else if (tokenStream.peek() === Tokens.LPAREN) {
       
  1351                     if (token === null) {
       
  1352                         token = tokenStream.LT(1);
       
  1353                     }
       
  1354                     expressions.push(this._media_expression());
       
  1355                 }
       
  1356 
       
  1357                 if (type === null && expressions.length === 0) {
       
  1358                     return null;
       
  1359                 } else {
       
  1360                     this._readWhitespace();
       
  1361                     while (tokenStream.match(Tokens.IDENT)) {
       
  1362                         if (tokenStream.token().value.toLowerCase() !== "and") {
       
  1363                             this._unexpectedToken(tokenStream.token());
       
  1364                         }
       
  1365 
       
  1366                         this._readWhitespace();
       
  1367                         expressions.push(this._media_expression());
       
  1368                     }
       
  1369                 }
       
  1370 
       
  1371                 return new MediaQuery(ident, type, expressions, token.startLine, token.startCol);
       
  1372             },
       
  1373 
       
  1374             //CSS3 Media Queries
       
  1375             _media_type: function() {
       
  1376                 /*
       
  1377                  * media_type
       
  1378                  *   : IDENT
       
  1379                  *   ;
       
  1380                  */
       
  1381                 return this._media_feature();
       
  1382             },
       
  1383 
       
  1384             /**
       
  1385              * Note: in CSS3 Media Queries, this is called "expression".
       
  1386              * Renamed here to avoid conflict with CSS3 Selectors
       
  1387              * definition of "expression". Also note that "expr" in the
       
  1388              * grammar now maps to "expression" from CSS3 selectors.
       
  1389              * @method _media_expression
       
  1390              * @private
       
  1391              */
       
  1392             _media_expression: function() {
       
  1393                 /*
       
  1394                  * expression
       
  1395                  *  : '(' S* media_feature S* [ ':' S* expr ]? ')' S*
       
  1396                  *  ;
       
  1397                  */
       
  1398                 var tokenStream = this._tokenStream,
       
  1399                     feature     = null,
       
  1400                     token,
       
  1401                     expression  = null;
       
  1402 
       
  1403                 tokenStream.mustMatch(Tokens.LPAREN);
       
  1404 
       
  1405                 feature = this._media_feature();
       
  1406                 this._readWhitespace();
       
  1407 
       
  1408                 if (tokenStream.match(Tokens.COLON)) {
       
  1409                     this._readWhitespace();
       
  1410                     token = tokenStream.LT(1);
       
  1411                     expression = this._expression();
       
  1412                 }
       
  1413 
       
  1414                 tokenStream.mustMatch(Tokens.RPAREN);
       
  1415                 this._readWhitespace();
       
  1416 
       
  1417                 return new MediaFeature(feature, expression ? new SyntaxUnit(expression, token.startLine, token.startCol) : null);
       
  1418             },
       
  1419 
       
  1420             //CSS3 Media Queries
       
  1421             _media_feature: function() {
       
  1422                 /*
       
  1423                  * media_feature
       
  1424                  *   : IDENT
       
  1425                  *   ;
       
  1426                  */
       
  1427                 var tokenStream = this._tokenStream;
       
  1428 
       
  1429                 this._readWhitespace();
       
  1430 
       
  1431                 tokenStream.mustMatch(Tokens.IDENT);
       
  1432 
       
  1433                 return SyntaxUnit.fromToken(tokenStream.token());
       
  1434             },
       
  1435 
       
  1436             //CSS3 Paged Media
       
  1437             _page: function() {
       
  1438                 /*
       
  1439                  * page:
       
  1440                  *    PAGE_SYM S* IDENT? pseudo_page? S*
       
  1441                  *    '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
       
  1442                  *    ;
       
  1443                  */
       
  1444                 var tokenStream = this._tokenStream,
       
  1445                     line,
       
  1446                     col,
       
  1447                     identifier  = null,
       
  1448                     pseudoPage  = null;
       
  1449 
       
  1450                 //look for @page
       
  1451                 tokenStream.mustMatch(Tokens.PAGE_SYM);
       
  1452                 line = tokenStream.token().startLine;
       
  1453                 col = tokenStream.token().startCol;
       
  1454 
       
  1455                 this._readWhitespace();
       
  1456 
       
  1457                 if (tokenStream.match(Tokens.IDENT)) {
       
  1458                     identifier = tokenStream.token().value;
       
  1459 
       
  1460                     //The value 'auto' may not be used as a page name and MUST be treated as a syntax error.
       
  1461                     if (identifier.toLowerCase() === "auto") {
       
  1462                         this._unexpectedToken(tokenStream.token());
       
  1463                     }
       
  1464                 }
       
  1465 
       
  1466                 //see if there's a colon upcoming
       
  1467                 if (tokenStream.peek() === Tokens.COLON) {
       
  1468                     pseudoPage = this._pseudo_page();
       
  1469                 }
       
  1470 
       
  1471                 this._readWhitespace();
       
  1472 
       
  1473                 this.fire({
       
  1474                     type:   "startpage",
       
  1475                     id:     identifier,
       
  1476                     pseudo: pseudoPage,
       
  1477                     line:   line,
       
  1478                     col:    col
       
  1479                 });
       
  1480 
       
  1481                 this._readDeclarations(true, true);
       
  1482 
       
  1483                 this.fire({
       
  1484                     type:   "endpage",
       
  1485                     id:     identifier,
       
  1486                     pseudo: pseudoPage,
       
  1487                     line:   line,
       
  1488                     col:    col
       
  1489                 });
       
  1490 
       
  1491             },
       
  1492 
       
  1493             //CSS3 Paged Media
       
  1494             _margin: function() {
       
  1495                 /*
       
  1496                  * margin :
       
  1497                  *    margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S*
       
  1498                  *    ;
       
  1499                  */
       
  1500                 var tokenStream = this._tokenStream,
       
  1501                     line,
       
  1502                     col,
       
  1503                     marginSym   = this._margin_sym();
       
  1504 
       
  1505                 if (marginSym) {
       
  1506                     line = tokenStream.token().startLine;
       
  1507                     col = tokenStream.token().startCol;
       
  1508 
       
  1509                     this.fire({
       
  1510                         type: "startpagemargin",
       
  1511                         margin: marginSym,
       
  1512                         line:   line,
       
  1513                         col:    col
       
  1514                     });
       
  1515 
       
  1516                     this._readDeclarations(true);
       
  1517 
       
  1518                     this.fire({
       
  1519                         type: "endpagemargin",
       
  1520                         margin: marginSym,
       
  1521                         line:   line,
       
  1522                         col:    col
       
  1523                     });
       
  1524                     return true;
       
  1525                 } else {
       
  1526                     return false;
       
  1527                 }
       
  1528             },
       
  1529 
       
  1530             //CSS3 Paged Media
       
  1531             _margin_sym: function() {
       
  1532 
       
  1533                 /*
       
  1534                  * margin_sym :
       
  1535                  *    TOPLEFTCORNER_SYM |
       
  1536                  *    TOPLEFT_SYM |
       
  1537                  *    TOPCENTER_SYM |
       
  1538                  *    TOPRIGHT_SYM |
       
  1539                  *    TOPRIGHTCORNER_SYM |
       
  1540                  *    BOTTOMLEFTCORNER_SYM |
       
  1541                  *    BOTTOMLEFT_SYM |
       
  1542                  *    BOTTOMCENTER_SYM |
       
  1543                  *    BOTTOMRIGHT_SYM |
       
  1544                  *    BOTTOMRIGHTCORNER_SYM |
       
  1545                  *    LEFTTOP_SYM |
       
  1546                  *    LEFTMIDDLE_SYM |
       
  1547                  *    LEFTBOTTOM_SYM |
       
  1548                  *    RIGHTTOP_SYM |
       
  1549                  *    RIGHTMIDDLE_SYM |
       
  1550                  *    RIGHTBOTTOM_SYM
       
  1551                  *    ;
       
  1552                  */
       
  1553 
       
  1554                 var tokenStream = this._tokenStream;
       
  1555 
       
  1556                 if (tokenStream.match([Tokens.TOPLEFTCORNER_SYM, Tokens.TOPLEFT_SYM,
       
  1557                         Tokens.TOPCENTER_SYM, Tokens.TOPRIGHT_SYM, Tokens.TOPRIGHTCORNER_SYM,
       
  1558                         Tokens.BOTTOMLEFTCORNER_SYM, Tokens.BOTTOMLEFT_SYM,
       
  1559                         Tokens.BOTTOMCENTER_SYM, Tokens.BOTTOMRIGHT_SYM,
       
  1560                         Tokens.BOTTOMRIGHTCORNER_SYM, Tokens.LEFTTOP_SYM,
       
  1561                         Tokens.LEFTMIDDLE_SYM, Tokens.LEFTBOTTOM_SYM, Tokens.RIGHTTOP_SYM,
       
  1562                         Tokens.RIGHTMIDDLE_SYM, Tokens.RIGHTBOTTOM_SYM])) {
       
  1563                     return SyntaxUnit.fromToken(tokenStream.token());
       
  1564                 } else {
       
  1565                     return null;
       
  1566                 }
       
  1567 
       
  1568             },
       
  1569 
       
  1570             _pseudo_page: function() {
       
  1571                 /*
       
  1572                  * pseudo_page
       
  1573                  *   : ':' IDENT
       
  1574                  *   ;
       
  1575                  */
       
  1576 
       
  1577                 var tokenStream = this._tokenStream;
       
  1578 
       
  1579                 tokenStream.mustMatch(Tokens.COLON);
       
  1580                 tokenStream.mustMatch(Tokens.IDENT);
       
  1581 
       
  1582                 //TODO: CSS3 Paged Media says only "left", "center", and "right" are allowed
       
  1583 
       
  1584                 return tokenStream.token().value;
       
  1585             },
       
  1586 
       
  1587             _font_face: function() {
       
  1588                 /*
       
  1589                  * font_face
       
  1590                  *   : FONT_FACE_SYM S*
       
  1591                  *     '{' S* declaration [ ';' S* declaration ]* '}' S*
       
  1592                  *   ;
       
  1593                  */
       
  1594                 var tokenStream = this._tokenStream,
       
  1595                     line,
       
  1596                     col;
       
  1597 
       
  1598                 //look for @page
       
  1599                 tokenStream.mustMatch(Tokens.FONT_FACE_SYM);
       
  1600                 line = tokenStream.token().startLine;
       
  1601                 col = tokenStream.token().startCol;
       
  1602 
       
  1603                 this._readWhitespace();
       
  1604 
       
  1605                 this.fire({
       
  1606                     type:   "startfontface",
       
  1607                     line:   line,
       
  1608                     col:    col
       
  1609                 });
       
  1610 
       
  1611                 this._readDeclarations(true);
       
  1612 
       
  1613                 this.fire({
       
  1614                     type:   "endfontface",
       
  1615                     line:   line,
       
  1616                     col:    col
       
  1617                 });
       
  1618             },
       
  1619 
       
  1620             _viewport: function() {
       
  1621                 /*
       
  1622                  * viewport
       
  1623                  *   : VIEWPORT_SYM S*
       
  1624                  *     '{' S* declaration? [ ';' S* declaration? ]* '}' S*
       
  1625                  *   ;
       
  1626                  */
       
  1627                 var tokenStream = this._tokenStream,
       
  1628                     line,
       
  1629                     col;
       
  1630 
       
  1631                 tokenStream.mustMatch(Tokens.VIEWPORT_SYM);
       
  1632                 line = tokenStream.token().startLine;
       
  1633                 col = tokenStream.token().startCol;
       
  1634 
       
  1635                 this._readWhitespace();
       
  1636 
       
  1637                 this.fire({
       
  1638                     type:   "startviewport",
       
  1639                     line:   line,
       
  1640                     col:    col
       
  1641                 });
       
  1642 
       
  1643                 this._readDeclarations(true);
       
  1644 
       
  1645                 this.fire({
       
  1646                     type:   "endviewport",
       
  1647                     line:   line,
       
  1648                     col:    col
       
  1649                 });
       
  1650 
       
  1651             },
       
  1652 
       
  1653             _document: function() {
       
  1654                 /*
       
  1655                  * document
       
  1656                  *   : DOCUMENT_SYM S*
       
  1657                  *     _document_function [ ',' S* _document_function ]* S*
       
  1658                  *     '{' S* ruleset* '}'
       
  1659                  *   ;
       
  1660                  */
       
  1661 
       
  1662                 var tokenStream = this._tokenStream,
       
  1663                     token,
       
  1664                     functions = [],
       
  1665                     prefix = "";
       
  1666 
       
  1667                 tokenStream.mustMatch(Tokens.DOCUMENT_SYM);
       
  1668                 token = tokenStream.token();
       
  1669                 if (/^@\-([^\-]+)\-/.test(token.value)) {
       
  1670                     prefix = RegExp.$1;
       
  1671                 }
       
  1672 
       
  1673                 this._readWhitespace();
       
  1674                 functions.push(this._document_function());
       
  1675 
       
  1676                 while (tokenStream.match(Tokens.COMMA)) {
       
  1677                     this._readWhitespace();
       
  1678                     functions.push(this._document_function());
       
  1679                 }
       
  1680 
       
  1681                 tokenStream.mustMatch(Tokens.LBRACE);
       
  1682                 this._readWhitespace();
       
  1683 
       
  1684                 this.fire({
       
  1685                     type:      "startdocument",
       
  1686                     functions: functions,
       
  1687                     prefix:    prefix,
       
  1688                     line:      token.startLine,
       
  1689                     col:       token.startCol
       
  1690                 });
       
  1691 
       
  1692                 var ok = true;
       
  1693                 while (ok) {
       
  1694                     switch (tokenStream.peek()) {
       
  1695                         case Tokens.PAGE_SYM:
       
  1696                             this._page();
       
  1697                             break;
       
  1698                         case Tokens.FONT_FACE_SYM:
       
  1699                             this._font_face();
       
  1700                             break;
       
  1701                         case Tokens.VIEWPORT_SYM:
       
  1702                             this._viewport();
       
  1703                             break;
       
  1704                         case Tokens.MEDIA_SYM:
       
  1705                             this._media();
       
  1706                             break;
       
  1707                         case Tokens.KEYFRAMES_SYM:
       
  1708                             this._keyframes();
       
  1709                             break;
       
  1710                         case Tokens.DOCUMENT_SYM:
       
  1711                             this._document();
       
  1712                             break;
       
  1713                         default:
       
  1714                             ok = Boolean(this._ruleset());
       
  1715                     }
       
  1716                 }
       
  1717 
       
  1718                 tokenStream.mustMatch(Tokens.RBRACE);
       
  1719                 token = tokenStream.token();
       
  1720                 this._readWhitespace();
       
  1721 
       
  1722                 this.fire({
       
  1723                     type:      "enddocument",
       
  1724                     functions: functions,
       
  1725                     prefix:    prefix,
       
  1726                     line:      token.startLine,
       
  1727                     col:       token.startCol
       
  1728                 });
       
  1729             },
       
  1730 
       
  1731             _document_function: function() {
       
  1732                 /*
       
  1733                  * document_function
       
  1734                  *   : function | URI S*
       
  1735                  *   ;
       
  1736                  */
       
  1737 
       
  1738                 var tokenStream = this._tokenStream,
       
  1739                     value;
       
  1740 
       
  1741                 if (tokenStream.match(Tokens.URI)) {
       
  1742                     value = tokenStream.token().value;
       
  1743                     this._readWhitespace();
       
  1744                 } else {
       
  1745                     value = this._function();
       
  1746                 }
       
  1747 
       
  1748                 return value;
       
  1749             },
       
  1750 
       
  1751             _operator: function(inFunction) {
       
  1752 
       
  1753                 /*
       
  1754                  * operator (outside function)
       
  1755                  *  : '/' S* | ',' S* | /( empty )/
       
  1756                  * operator (inside function)
       
  1757                  *  : '/' S* | '+' S* | '*' S* | '-' S* /( empty )/
       
  1758                  *  ;
       
  1759                  */
       
  1760 
       
  1761                 var tokenStream = this._tokenStream,
       
  1762                     token       = null;
       
  1763 
       
  1764                 if (tokenStream.match([Tokens.SLASH, Tokens.COMMA]) ||
       
  1765                     (inFunction && tokenStream.match([Tokens.PLUS, Tokens.STAR, Tokens.MINUS]))) {
       
  1766                     token =  tokenStream.token();
       
  1767                     this._readWhitespace();
       
  1768                 }
       
  1769                 return token ? PropertyValuePart.fromToken(token) : null;
       
  1770 
       
  1771             },
       
  1772 
       
  1773             _combinator: function() {
       
  1774 
       
  1775                 /*
       
  1776                  * combinator
       
  1777                  *  : PLUS S* | GREATER S* | TILDE S* | S+
       
  1778                  *  ;
       
  1779                  */
       
  1780 
       
  1781                 var tokenStream = this._tokenStream,
       
  1782                     value       = null,
       
  1783                     token;
       
  1784 
       
  1785                 if (tokenStream.match([Tokens.PLUS, Tokens.GREATER, Tokens.TILDE])) {
       
  1786                     token = tokenStream.token();
       
  1787                     value = new Combinator(token.value, token.startLine, token.startCol);
       
  1788                     this._readWhitespace();
       
  1789                 }
       
  1790 
       
  1791                 return value;
       
  1792             },
       
  1793 
       
  1794             _unary_operator: function() {
       
  1795 
       
  1796                 /*
       
  1797                  * unary_operator
       
  1798                  *  : '-' | '+'
       
  1799                  *  ;
       
  1800                  */
       
  1801 
       
  1802                 var tokenStream = this._tokenStream;
       
  1803 
       
  1804                 if (tokenStream.match([Tokens.MINUS, Tokens.PLUS])) {
       
  1805                     return tokenStream.token().value;
       
  1806                 } else {
       
  1807                     return null;
       
  1808                 }
       
  1809             },
       
  1810 
       
  1811             _property: function() {
       
  1812 
       
  1813                 /*
       
  1814                  * property
       
  1815                  *   : IDENT S*
       
  1816                  *   ;
       
  1817                  */
       
  1818 
       
  1819                 var tokenStream = this._tokenStream,
       
  1820                     value       = null,
       
  1821                     hack        = null,
       
  1822                     tokenValue,
       
  1823                     token,
       
  1824                     line,
       
  1825                     col;
       
  1826 
       
  1827                 //check for star hack - throws error if not allowed
       
  1828                 if (tokenStream.peek() === Tokens.STAR && this.options.starHack) {
       
  1829                     tokenStream.get();
       
  1830                     token = tokenStream.token();
       
  1831                     hack = token.value;
       
  1832                     line = token.startLine;
       
  1833                     col = token.startCol;
       
  1834                 }
       
  1835 
       
  1836                 if (tokenStream.match(Tokens.IDENT)) {
       
  1837                     token = tokenStream.token();
       
  1838                     tokenValue = token.value;
       
  1839 
       
  1840                     //check for underscore hack - no error if not allowed because it's valid CSS syntax
       
  1841                     if (tokenValue.charAt(0) === "_" && this.options.underscoreHack) {
       
  1842                         hack = "_";
       
  1843                         tokenValue = tokenValue.substring(1);
       
  1844                     }
       
  1845 
       
  1846                     value = new PropertyName(tokenValue, hack, (line||token.startLine), (col||token.startCol));
       
  1847                     this._readWhitespace();
       
  1848                 }
       
  1849 
       
  1850                 return value;
       
  1851             },
       
  1852 
       
  1853             //Augmented with CSS3 Selectors
       
  1854             _ruleset: function() {
       
  1855                 /*
       
  1856                  * ruleset
       
  1857                  *   : selectors_group
       
  1858                  *     '{' S* declaration? [ ';' S* declaration? ]* '}' S*
       
  1859                  *   ;
       
  1860                  */
       
  1861 
       
  1862                 var tokenStream = this._tokenStream,
       
  1863                     tt,
       
  1864                     selectors;
       
  1865 
       
  1866 
       
  1867                 /*
       
  1868                  * Error Recovery: If even a single selector fails to parse,
       
  1869                  * then the entire ruleset should be thrown away.
       
  1870                  */
       
  1871                 try {
       
  1872                     selectors = this._selectors_group();
       
  1873                 } catch (ex) {
       
  1874                     if (ex instanceof SyntaxError && !this.options.strict) {
       
  1875 
       
  1876                         //fire error event
       
  1877                         this.fire({
       
  1878                             type:       "error",
       
  1879                             error:      ex,
       
  1880                             message:    ex.message,
       
  1881                             line:       ex.line,
       
  1882                             col:        ex.col
       
  1883                         });
       
  1884 
       
  1885                         //skip over everything until closing brace
       
  1886                         tt = tokenStream.advance([Tokens.RBRACE]);
       
  1887                         if (tt === Tokens.RBRACE) {
       
  1888                             //if there's a right brace, the rule is finished so don't do anything
       
  1889                         } else {
       
  1890                             //otherwise, rethrow the error because it wasn't handled properly
       
  1891                             throw ex;
       
  1892                         }
       
  1893 
       
  1894                     } else {
       
  1895                         //not a syntax error, rethrow it
       
  1896                         throw ex;
       
  1897                     }
       
  1898 
       
  1899                     //trigger parser to continue
       
  1900                     return true;
       
  1901                 }
       
  1902 
       
  1903                 //if it got here, all selectors parsed
       
  1904                 if (selectors) {
       
  1905 
       
  1906                     this.fire({
       
  1907                         type:       "startrule",
       
  1908                         selectors:  selectors,
       
  1909                         line:       selectors[0].line,
       
  1910                         col:        selectors[0].col
       
  1911                     });
       
  1912 
       
  1913                     this._readDeclarations(true);
       
  1914 
       
  1915                     this.fire({
       
  1916                         type:       "endrule",
       
  1917                         selectors:  selectors,
       
  1918                         line:       selectors[0].line,
       
  1919                         col:        selectors[0].col
       
  1920                     });
       
  1921 
       
  1922                 }
       
  1923 
       
  1924                 return selectors;
       
  1925 
       
  1926             },
       
  1927 
       
  1928             //CSS3 Selectors
       
  1929             _selectors_group: function() {
       
  1930 
       
  1931                 /*
       
  1932                  * selectors_group
       
  1933                  *   : selector [ COMMA S* selector ]*
       
  1934                  *   ;
       
  1935                  */
       
  1936                 var tokenStream = this._tokenStream,
       
  1937                     selectors   = [],
       
  1938                     selector;
       
  1939 
       
  1940                 selector = this._selector();
       
  1941                 if (selector !== null) {
       
  1942 
       
  1943                     selectors.push(selector);
       
  1944                     while (tokenStream.match(Tokens.COMMA)) {
       
  1945                         this._readWhitespace();
       
  1946                         selector = this._selector();
       
  1947                         if (selector !== null) {
       
  1948                             selectors.push(selector);
       
  1949                         } else {
       
  1950                             this._unexpectedToken(tokenStream.LT(1));
       
  1951                         }
       
  1952                     }
       
  1953                 }
       
  1954 
       
  1955                 return selectors.length ? selectors : null;
       
  1956             },
       
  1957 
       
  1958             //CSS3 Selectors
       
  1959             _selector: function() {
       
  1960                 /*
       
  1961                  * selector
       
  1962                  *   : simple_selector_sequence [ combinator simple_selector_sequence ]*
       
  1963                  *   ;
       
  1964                  */
       
  1965 
       
  1966                 var tokenStream = this._tokenStream,
       
  1967                     selector    = [],
       
  1968                     nextSelector = null,
       
  1969                     combinator  = null,
       
  1970                     ws          = null;
       
  1971 
       
  1972                 //if there's no simple selector, then there's no selector
       
  1973                 nextSelector = this._simple_selector_sequence();
       
  1974                 if (nextSelector === null) {
       
  1975                     return null;
       
  1976                 }
       
  1977 
       
  1978                 selector.push(nextSelector);
       
  1979 
       
  1980                 do {
       
  1981 
       
  1982                     //look for a combinator
       
  1983                     combinator = this._combinator();
       
  1984 
       
  1985                     if (combinator !== null) {
       
  1986                         selector.push(combinator);
       
  1987                         nextSelector = this._simple_selector_sequence();
       
  1988 
       
  1989                         //there must be a next selector
       
  1990                         if (nextSelector === null) {
       
  1991                             this._unexpectedToken(tokenStream.LT(1));
       
  1992                         } else {
       
  1993 
       
  1994                             //nextSelector is an instance of SelectorPart
       
  1995                             selector.push(nextSelector);
       
  1996                         }
       
  1997                     } else {
       
  1998 
       
  1999                         //if there's not whitespace, we're done
       
  2000                         if (this._readWhitespace()) {
       
  2001 
       
  2002                             //add whitespace separator
       
  2003                             ws = new Combinator(tokenStream.token().value, tokenStream.token().startLine, tokenStream.token().startCol);
       
  2004 
       
  2005                             //combinator is not required
       
  2006                             combinator = this._combinator();
       
  2007 
       
  2008                             //selector is required if there's a combinator
       
  2009                             nextSelector = this._simple_selector_sequence();
       
  2010                             if (nextSelector === null) {
       
  2011                                 if (combinator !== null) {
       
  2012                                     this._unexpectedToken(tokenStream.LT(1));
       
  2013                                 }
       
  2014                             } else {
       
  2015 
       
  2016                                 if (combinator !== null) {
       
  2017                                     selector.push(combinator);
       
  2018                                 } else {
       
  2019                                     selector.push(ws);
       
  2020                                 }
       
  2021 
       
  2022                                 selector.push(nextSelector);
       
  2023                             }
       
  2024                         } else {
       
  2025                             break;
       
  2026                         }
       
  2027 
       
  2028                     }
       
  2029                 } while (true);
       
  2030 
       
  2031                 return new Selector(selector, selector[0].line, selector[0].col);
       
  2032             },
       
  2033 
       
  2034             //CSS3 Selectors
       
  2035             _simple_selector_sequence: function() {
       
  2036                 /*
       
  2037                  * simple_selector_sequence
       
  2038                  *   : [ type_selector | universal ]
       
  2039                  *     [ HASH | class | attrib | pseudo | negation ]*
       
  2040                  *   | [ HASH | class | attrib | pseudo | negation ]+
       
  2041                  *   ;
       
  2042                  */
       
  2043 
       
  2044                 var tokenStream = this._tokenStream,
       
  2045 
       
  2046                     //parts of a simple selector
       
  2047                     elementName = null,
       
  2048                     modifiers   = [],
       
  2049 
       
  2050                     //complete selector text
       
  2051                     selectorText= "",
       
  2052 
       
  2053                     //the different parts after the element name to search for
       
  2054                     components  = [
       
  2055                         //HASH
       
  2056                         function() {
       
  2057                             return tokenStream.match(Tokens.HASH) ?
       
  2058                                     new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
       
  2059                                     null;
       
  2060                         },
       
  2061                         this._class,
       
  2062                         this._attrib,
       
  2063                         this._pseudo,
       
  2064                         this._negation
       
  2065                     ],
       
  2066                     i           = 0,
       
  2067                     len         = components.length,
       
  2068                     component   = null,
       
  2069                     line,
       
  2070                     col;
       
  2071 
       
  2072 
       
  2073                 //get starting line and column for the selector
       
  2074                 line = tokenStream.LT(1).startLine;
       
  2075                 col = tokenStream.LT(1).startCol;
       
  2076 
       
  2077                 elementName = this._type_selector();
       
  2078                 if (!elementName) {
       
  2079                     elementName = this._universal();
       
  2080                 }
       
  2081 
       
  2082                 if (elementName !== null) {
       
  2083                     selectorText += elementName;
       
  2084                 }
       
  2085 
       
  2086                 while (true) {
       
  2087 
       
  2088                     //whitespace means we're done
       
  2089                     if (tokenStream.peek() === Tokens.S) {
       
  2090                         break;
       
  2091                     }
       
  2092 
       
  2093                     //check for each component
       
  2094                     while (i < len && component === null) {
       
  2095                         component = components[i++].call(this);
       
  2096                     }
       
  2097 
       
  2098                     if (component === null) {
       
  2099 
       
  2100                         //we don't have a selector
       
  2101                         if (selectorText === "") {
       
  2102                             return null;
       
  2103                         } else {
       
  2104                             break;
       
  2105                         }
       
  2106                     } else {
       
  2107                         i = 0;
       
  2108                         modifiers.push(component);
       
  2109                         selectorText += component.toString();
       
  2110                         component = null;
       
  2111                     }
       
  2112                 }
       
  2113 
       
  2114 
       
  2115                 return selectorText !== "" ?
       
  2116                         new SelectorPart(elementName, modifiers, selectorText, line, col) :
       
  2117                         null;
       
  2118             },
       
  2119 
       
  2120             //CSS3 Selectors
       
  2121             _type_selector: function() {
       
  2122                 /*
       
  2123                  * type_selector
       
  2124                  *   : [ namespace_prefix ]? element_name
       
  2125                  *   ;
       
  2126                  */
       
  2127 
       
  2128                 var tokenStream = this._tokenStream,
       
  2129                     ns          = this._namespace_prefix(),
       
  2130                     elementName = this._element_name();
       
  2131 
       
  2132                 if (!elementName) {
       
  2133                     /*
       
  2134                      * Need to back out the namespace that was read due to both
       
  2135                      * type_selector and universal reading namespace_prefix
       
  2136                      * first. Kind of hacky, but only way I can figure out
       
  2137                      * right now how to not change the grammar.
       
  2138                      */
       
  2139                     if (ns) {
       
  2140                         tokenStream.unget();
       
  2141                         if (ns.length > 1) {
       
  2142                             tokenStream.unget();
       
  2143                         }
       
  2144                     }
       
  2145 
       
  2146                     return null;
       
  2147                 } else {
       
  2148                     if (ns) {
       
  2149                         elementName.text = ns + elementName.text;
       
  2150                         elementName.col -= ns.length;
       
  2151                     }
       
  2152                     return elementName;
       
  2153                 }
       
  2154             },
       
  2155 
       
  2156             //CSS3 Selectors
       
  2157             _class: function() {
       
  2158                 /*
       
  2159                  * class
       
  2160                  *   : '.' IDENT
       
  2161                  *   ;
       
  2162                  */
       
  2163 
       
  2164                 var tokenStream = this._tokenStream,
       
  2165                     token;
       
  2166 
       
  2167                 if (tokenStream.match(Tokens.DOT)) {
       
  2168                     tokenStream.mustMatch(Tokens.IDENT);
       
  2169                     token = tokenStream.token();
       
  2170                     return new SelectorSubPart("." + token.value, "class", token.startLine, token.startCol - 1);
       
  2171                 } else {
       
  2172                     return null;
       
  2173                 }
       
  2174 
       
  2175             },
       
  2176 
       
  2177             //CSS3 Selectors
       
  2178             _element_name: function() {
       
  2179                 /*
       
  2180                  * element_name
       
  2181                  *   : IDENT
       
  2182                  *   ;
       
  2183                  */
       
  2184 
       
  2185                 var tokenStream = this._tokenStream,
       
  2186                     token;
       
  2187 
       
  2188                 if (tokenStream.match(Tokens.IDENT)) {
       
  2189                     token = tokenStream.token();
       
  2190                     return new SelectorSubPart(token.value, "elementName", token.startLine, token.startCol);
       
  2191 
       
  2192                 } else {
       
  2193                     return null;
       
  2194                 }
       
  2195             },
       
  2196 
       
  2197             //CSS3 Selectors
       
  2198             _namespace_prefix: function() {
       
  2199                 /*
       
  2200                  * namespace_prefix
       
  2201                  *   : [ IDENT | '*' ]? '|'
       
  2202                  *   ;
       
  2203                  */
       
  2204                 var tokenStream = this._tokenStream,
       
  2205                     value       = "";
       
  2206 
       
  2207                 //verify that this is a namespace prefix
       
  2208                 if (tokenStream.LA(1) === Tokens.PIPE || tokenStream.LA(2) === Tokens.PIPE) {
       
  2209 
       
  2210                     if (tokenStream.match([Tokens.IDENT, Tokens.STAR])) {
       
  2211                         value += tokenStream.token().value;
       
  2212                     }
       
  2213 
       
  2214                     tokenStream.mustMatch(Tokens.PIPE);
       
  2215                     value += "|";
       
  2216 
       
  2217                 }
       
  2218 
       
  2219                 return value.length ? value : null;
       
  2220             },
       
  2221 
       
  2222             //CSS3 Selectors
       
  2223             _universal: function() {
       
  2224                 /*
       
  2225                  * universal
       
  2226                  *   : [ namespace_prefix ]? '*'
       
  2227                  *   ;
       
  2228                  */
       
  2229                 var tokenStream = this._tokenStream,
       
  2230                     value       = "",
       
  2231                     ns;
       
  2232 
       
  2233                 ns = this._namespace_prefix();
       
  2234                 if (ns) {
       
  2235                     value += ns;
       
  2236                 }
       
  2237 
       
  2238                 if (tokenStream.match(Tokens.STAR)) {
       
  2239                     value += "*";
       
  2240                 }
       
  2241 
       
  2242                 return value.length ? value : null;
       
  2243 
       
  2244             },
       
  2245 
       
  2246             //CSS3 Selectors
       
  2247             _attrib: function() {
       
  2248                 /*
       
  2249                  * attrib
       
  2250                  *   : '[' S* [ namespace_prefix ]? IDENT S*
       
  2251                  *         [ [ PREFIXMATCH |
       
  2252                  *             SUFFIXMATCH |
       
  2253                  *             SUBSTRINGMATCH |
       
  2254                  *             '=' |
       
  2255                  *             INCLUDES |
       
  2256                  *             DASHMATCH ] S* [ IDENT | STRING ] S*
       
  2257                  *         ]? ']'
       
  2258                  *   ;
       
  2259                  */
       
  2260 
       
  2261                 var tokenStream = this._tokenStream,
       
  2262                     value       = null,
       
  2263                     ns,
       
  2264                     token;
       
  2265 
       
  2266                 if (tokenStream.match(Tokens.LBRACKET)) {
       
  2267                     token = tokenStream.token();
       
  2268                     value = token.value;
       
  2269                     value += this._readWhitespace();
       
  2270 
       
  2271                     ns = this._namespace_prefix();
       
  2272 
       
  2273                     if (ns) {
       
  2274                         value += ns;
       
  2275                     }
       
  2276 
       
  2277                     tokenStream.mustMatch(Tokens.IDENT);
       
  2278                     value += tokenStream.token().value;
       
  2279                     value += this._readWhitespace();
       
  2280 
       
  2281                     if (tokenStream.match([Tokens.PREFIXMATCH, Tokens.SUFFIXMATCH, Tokens.SUBSTRINGMATCH,
       
  2282                             Tokens.EQUALS, Tokens.INCLUDES, Tokens.DASHMATCH])) {
       
  2283 
       
  2284                         value += tokenStream.token().value;
       
  2285                         value += this._readWhitespace();
       
  2286 
       
  2287                         tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
       
  2288                         value += tokenStream.token().value;
       
  2289                         value += this._readWhitespace();
       
  2290                     }
       
  2291 
       
  2292                     tokenStream.mustMatch(Tokens.RBRACKET);
       
  2293 
       
  2294                     return new SelectorSubPart(value + "]", "attribute", token.startLine, token.startCol);
       
  2295                 } else {
       
  2296                     return null;
       
  2297                 }
       
  2298             },
       
  2299 
       
  2300             //CSS3 Selectors
       
  2301             _pseudo: function() {
       
  2302 
       
  2303                 /*
       
  2304                  * pseudo
       
  2305                  *   : ':' ':'? [ IDENT | functional_pseudo ]
       
  2306                  *   ;
       
  2307                  */
       
  2308 
       
  2309                 var tokenStream = this._tokenStream,
       
  2310                     pseudo      = null,
       
  2311                     colons      = ":",
       
  2312                     line,
       
  2313                     col;
       
  2314 
       
  2315                 if (tokenStream.match(Tokens.COLON)) {
       
  2316 
       
  2317                     if (tokenStream.match(Tokens.COLON)) {
       
  2318                         colons += ":";
       
  2319                     }
       
  2320 
       
  2321                     if (tokenStream.match(Tokens.IDENT)) {
       
  2322                         pseudo = tokenStream.token().value;
       
  2323                         line = tokenStream.token().startLine;
       
  2324                         col = tokenStream.token().startCol - colons.length;
       
  2325                     } else if (tokenStream.peek() === Tokens.FUNCTION) {
       
  2326                         line = tokenStream.LT(1).startLine;
       
  2327                         col = tokenStream.LT(1).startCol - colons.length;
       
  2328                         pseudo = this._functional_pseudo();
       
  2329                     }
       
  2330 
       
  2331                     if (pseudo) {
       
  2332                         pseudo = new SelectorSubPart(colons + pseudo, "pseudo", line, col);
       
  2333                     } else {
       
  2334                         var startLine = tokenStream.LT(1).startLine,
       
  2335                             startCol  = tokenStream.LT(0).startCol;
       
  2336                         throw new SyntaxError("Expected a `FUNCTION` or `IDENT` after colon at line " + startLine + ", col " + startCol + ".", startLine, startCol);
       
  2337                     }
       
  2338                 }
       
  2339 
       
  2340                 return pseudo;
       
  2341             },
       
  2342 
       
  2343             //CSS3 Selectors
       
  2344             _functional_pseudo: function() {
       
  2345                 /*
       
  2346                  * functional_pseudo
       
  2347                  *   : FUNCTION S* expression ')'
       
  2348                  *   ;
       
  2349                 */
       
  2350 
       
  2351                 var tokenStream = this._tokenStream,
       
  2352                     value = null;
       
  2353 
       
  2354                 if (tokenStream.match(Tokens.FUNCTION)) {
       
  2355                     value = tokenStream.token().value;
       
  2356                     value += this._readWhitespace();
       
  2357                     value += this._expression();
       
  2358                     tokenStream.mustMatch(Tokens.RPAREN);
       
  2359                     value += ")";
       
  2360                 }
       
  2361 
       
  2362                 return value;
       
  2363             },
       
  2364 
       
  2365             //CSS3 Selectors
       
  2366             _expression: function() {
       
  2367                 /*
       
  2368                  * expression
       
  2369                  *   : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
       
  2370                  *   ;
       
  2371                  */
       
  2372 
       
  2373                 var tokenStream = this._tokenStream,
       
  2374                     value       = "";
       
  2375 
       
  2376                 while (tokenStream.match([Tokens.PLUS, Tokens.MINUS, Tokens.DIMENSION,
       
  2377                         Tokens.NUMBER, Tokens.STRING, Tokens.IDENT, Tokens.LENGTH,
       
  2378                         Tokens.FREQ, Tokens.ANGLE, Tokens.TIME,
       
  2379                         Tokens.RESOLUTION, Tokens.SLASH])) {
       
  2380 
       
  2381                     value += tokenStream.token().value;
       
  2382                     value += this._readWhitespace();
       
  2383                 }
       
  2384 
       
  2385                 return value.length ? value : null;
       
  2386 
       
  2387             },
       
  2388 
       
  2389             //CSS3 Selectors
       
  2390             _negation: function() {
       
  2391                 /*
       
  2392                  * negation
       
  2393                  *   : NOT S* negation_arg S* ')'
       
  2394                  *   ;
       
  2395                  */
       
  2396 
       
  2397                 var tokenStream = this._tokenStream,
       
  2398                     line,
       
  2399                     col,
       
  2400                     value       = "",
       
  2401                     arg,
       
  2402                     subpart     = null;
       
  2403 
       
  2404                 if (tokenStream.match(Tokens.NOT)) {
       
  2405                     value = tokenStream.token().value;
       
  2406                     line = tokenStream.token().startLine;
       
  2407                     col = tokenStream.token().startCol;
       
  2408                     value += this._readWhitespace();
       
  2409                     arg = this._negation_arg();
       
  2410                     value += arg;
       
  2411                     value += this._readWhitespace();
       
  2412                     tokenStream.match(Tokens.RPAREN);
       
  2413                     value += tokenStream.token().value;
       
  2414 
       
  2415                     subpart = new SelectorSubPart(value, "not", line, col);
       
  2416                     subpart.args.push(arg);
       
  2417                 }
       
  2418 
       
  2419                 return subpart;
       
  2420             },
       
  2421 
       
  2422             //CSS3 Selectors
       
  2423             _negation_arg: function() {
       
  2424                 /*
       
  2425                  * negation_arg
       
  2426                  *   : type_selector | universal | HASH | class | attrib | pseudo
       
  2427                  *   ;
       
  2428                  */
       
  2429 
       
  2430                 var tokenStream = this._tokenStream,
       
  2431                     args        = [
       
  2432                         this._type_selector,
       
  2433                         this._universal,
       
  2434                         function() {
       
  2435                             return tokenStream.match(Tokens.HASH) ?
       
  2436                                     new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
       
  2437                                     null;
       
  2438                         },
       
  2439                         this._class,
       
  2440                         this._attrib,
       
  2441                         this._pseudo
       
  2442                     ],
       
  2443                     arg         = null,
       
  2444                     i           = 0,
       
  2445                     len         = args.length,
       
  2446                     line,
       
  2447                     col,
       
  2448                     part;
       
  2449 
       
  2450                 line = tokenStream.LT(1).startLine;
       
  2451                 col = tokenStream.LT(1).startCol;
       
  2452 
       
  2453                 while (i < len && arg === null) {
       
  2454 
       
  2455                     arg = args[i].call(this);
       
  2456                     i++;
       
  2457                 }
       
  2458 
       
  2459                 //must be a negation arg
       
  2460                 if (arg === null) {
       
  2461                     this._unexpectedToken(tokenStream.LT(1));
       
  2462                 }
       
  2463 
       
  2464                 //it's an element name
       
  2465                 if (arg.type === "elementName") {
       
  2466                     part = new SelectorPart(arg, [], arg.toString(), line, col);
       
  2467                 } else {
       
  2468                     part = new SelectorPart(null, [arg], arg.toString(), line, col);
       
  2469                 }
       
  2470 
       
  2471                 return part;
       
  2472             },
       
  2473 
       
  2474             _declaration: function() {
       
  2475 
       
  2476                 /*
       
  2477                  * declaration
       
  2478                  *   : property ':' S* expr prio?
       
  2479                  *   | /( empty )/
       
  2480                  *   ;
       
  2481                  */
       
  2482 
       
  2483                 var tokenStream = this._tokenStream,
       
  2484                     property    = null,
       
  2485                     expr        = null,
       
  2486                     prio        = null,
       
  2487                     invalid     = null,
       
  2488                     propertyName= "";
       
  2489 
       
  2490                 property = this._property();
       
  2491                 if (property !== null) {
       
  2492 
       
  2493                     tokenStream.mustMatch(Tokens.COLON);
       
  2494                     this._readWhitespace();
       
  2495 
       
  2496                     expr = this._expr();
       
  2497 
       
  2498                     //if there's no parts for the value, it's an error
       
  2499                     if (!expr || expr.length === 0) {
       
  2500                         this._unexpectedToken(tokenStream.LT(1));
       
  2501                     }
       
  2502 
       
  2503                     prio = this._prio();
       
  2504 
       
  2505                     /*
       
  2506                      * If hacks should be allowed, then only check the root
       
  2507                      * property. If hacks should not be allowed, treat
       
  2508                      * _property or *property as invalid properties.
       
  2509                      */
       
  2510                     propertyName = property.toString();
       
  2511                     if (this.options.starHack && property.hack === "*" ||
       
  2512                             this.options.underscoreHack && property.hack === "_") {
       
  2513 
       
  2514                         propertyName = property.text;
       
  2515                     }
       
  2516 
       
  2517                     try {
       
  2518                         this._validateProperty(propertyName, expr);
       
  2519                     } catch (ex) {
       
  2520                         invalid = ex;
       
  2521                     }
       
  2522 
       
  2523                     this.fire({
       
  2524                         type:       "property",
       
  2525                         property:   property,
       
  2526                         value:      expr,
       
  2527                         important:  prio,
       
  2528                         line:       property.line,
       
  2529                         col:        property.col,
       
  2530                         invalid:    invalid
       
  2531                     });
       
  2532 
       
  2533                     return true;
       
  2534                 } else {
       
  2535                     return false;
       
  2536                 }
       
  2537             },
       
  2538 
       
  2539             _prio: function() {
       
  2540                 /*
       
  2541                  * prio
       
  2542                  *   : IMPORTANT_SYM S*
       
  2543                  *   ;
       
  2544                  */
       
  2545 
       
  2546                 var tokenStream = this._tokenStream,
       
  2547                     result      = tokenStream.match(Tokens.IMPORTANT_SYM);
       
  2548 
       
  2549                 this._readWhitespace();
       
  2550                 return result;
       
  2551             },
       
  2552 
       
  2553             _expr: function(inFunction) {
       
  2554                 /*
       
  2555                  * expr
       
  2556                  *   : term [ operator term ]*
       
  2557                  *   ;
       
  2558                  */
       
  2559 
       
  2560                 var values      = [],
       
  2561                     //valueParts    = [],
       
  2562                     value       = null,
       
  2563                     operator    = null;
       
  2564 
       
  2565                 value = this._term(inFunction);
       
  2566                 if (value !== null) {
       
  2567 
       
  2568                     values.push(value);
       
  2569 
       
  2570                     do {
       
  2571                         operator = this._operator(inFunction);
       
  2572 
       
  2573                         //if there's an operator, keep building up the value parts
       
  2574                         if (operator) {
       
  2575                             values.push(operator);
       
  2576                         } /*else {
       
  2577                             //if there's not an operator, you have a full value
       
  2578                             values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
       
  2579                             valueParts = [];
       
  2580                         }*/
       
  2581 
       
  2582                         value = this._term(inFunction);
       
  2583 
       
  2584                         if (value === null) {
       
  2585                             break;
       
  2586                         } else {
       
  2587                             values.push(value);
       
  2588                         }
       
  2589                     } while (true);
       
  2590                 }
       
  2591 
       
  2592                 //cleanup
       
  2593                 /*if (valueParts.length) {
       
  2594                     values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
       
  2595                 }*/
       
  2596 
       
  2597                 return values.length > 0 ? new PropertyValue(values, values[0].line, values[0].col) : null;
       
  2598             },
       
  2599 
       
  2600             _term: function(inFunction) {
       
  2601 
       
  2602                 /*
       
  2603                  * term
       
  2604                  *   : unary_operator?
       
  2605                  *     [ NUMBER S* | PERCENTAGE S* | LENGTH S* | ANGLE S* |
       
  2606                  *       TIME S* | FREQ S* | function | ie_function ]
       
  2607                  *   | STRING S* | IDENT S* | URI S* | UNICODERANGE S* | hexcolor
       
  2608                  *   ;
       
  2609                  */
       
  2610 
       
  2611                 var tokenStream = this._tokenStream,
       
  2612                     unary       = null,
       
  2613                     value       = null,
       
  2614                     endChar     = null,
       
  2615                     part        = null,
       
  2616                     token,
       
  2617                     line,
       
  2618                     col;
       
  2619 
       
  2620                 //returns the operator or null
       
  2621                 unary = this._unary_operator();
       
  2622                 if (unary !== null) {
       
  2623                     line = tokenStream.token().startLine;
       
  2624                     col = tokenStream.token().startCol;
       
  2625                 }
       
  2626 
       
  2627                 //exception for IE filters
       
  2628                 if (tokenStream.peek() === Tokens.IE_FUNCTION && this.options.ieFilters) {
       
  2629 
       
  2630                     value = this._ie_function();
       
  2631                     if (unary === null) {
       
  2632                         line = tokenStream.token().startLine;
       
  2633                         col = tokenStream.token().startCol;
       
  2634                     }
       
  2635 
       
  2636                 //see if it's a simple block
       
  2637                 } else if (inFunction && tokenStream.match([Tokens.LPAREN, Tokens.LBRACE, Tokens.LBRACKET])) {
       
  2638 
       
  2639                     token = tokenStream.token();
       
  2640                     endChar = token.endChar;
       
  2641                     value = token.value + this._expr(inFunction).text;
       
  2642                     if (unary === null) {
       
  2643                         line = tokenStream.token().startLine;
       
  2644                         col = tokenStream.token().startCol;
       
  2645                     }
       
  2646                     tokenStream.mustMatch(Tokens.type(endChar));
       
  2647                     value += endChar;
       
  2648                     this._readWhitespace();
       
  2649 
       
  2650                 //see if there's a simple match
       
  2651                 } else if (tokenStream.match([Tokens.NUMBER, Tokens.PERCENTAGE, Tokens.LENGTH,
       
  2652                         Tokens.ANGLE, Tokens.TIME,
       
  2653                         Tokens.FREQ, Tokens.STRING, Tokens.IDENT, Tokens.URI, Tokens.UNICODE_RANGE])) {
       
  2654 
       
  2655                     value = tokenStream.token().value;
       
  2656                     if (unary === null) {
       
  2657                         line = tokenStream.token().startLine;
       
  2658                         col = tokenStream.token().startCol;
       
  2659                         // Correct potentially-inaccurate IDENT parsing in
       
  2660                         // PropertyValuePart constructor.
       
  2661                         part = PropertyValuePart.fromToken(tokenStream.token());
       
  2662                     }
       
  2663                     this._readWhitespace();
       
  2664                 } else {
       
  2665 
       
  2666                     //see if it's a color
       
  2667                     token = this._hexcolor();
       
  2668                     if (token === null) {
       
  2669 
       
  2670                         //if there's no unary, get the start of the next token for line/col info
       
  2671                         if (unary === null) {
       
  2672                             line = tokenStream.LT(1).startLine;
       
  2673                             col = tokenStream.LT(1).startCol;
       
  2674                         }
       
  2675 
       
  2676                         //has to be a function
       
  2677                         if (value === null) {
       
  2678 
       
  2679                             /*
       
  2680                              * This checks for alpha(opacity=0) style of IE
       
  2681                              * functions. IE_FUNCTION only presents progid: style.
       
  2682                              */
       
  2683                             if (tokenStream.LA(3) === Tokens.EQUALS && this.options.ieFilters) {
       
  2684                                 value = this._ie_function();
       
  2685                             } else {
       
  2686                                 value = this._function();
       
  2687                             }
       
  2688                         }
       
  2689 
       
  2690                         /*if (value === null) {
       
  2691                             return null;
       
  2692                             //throw new Error("Expected identifier at line " + tokenStream.token().startLine + ", character " +  tokenStream.token().startCol + ".");
       
  2693                         }*/
       
  2694 
       
  2695                     } else {
       
  2696                         value = token.value;
       
  2697                         if (unary === null) {
       
  2698                             line = token.startLine;
       
  2699                             col = token.startCol;
       
  2700                         }
       
  2701                     }
       
  2702 
       
  2703                 }
       
  2704 
       
  2705                 return part !== null ? part : value !== null ?
       
  2706                         new PropertyValuePart(unary !== null ? unary + value : value, line, col) :
       
  2707                         null;
       
  2708 
       
  2709             },
       
  2710 
       
  2711             _function: function() {
       
  2712 
       
  2713                 /*
       
  2714                  * function
       
  2715                  *   : FUNCTION S* expr ')' S*
       
  2716                  *   ;
       
  2717                  */
       
  2718 
       
  2719                 var tokenStream = this._tokenStream,
       
  2720                     functionText = null,
       
  2721                     expr        = null,
       
  2722                     lt;
       
  2723 
       
  2724                 if (tokenStream.match(Tokens.FUNCTION)) {
       
  2725                     functionText = tokenStream.token().value;
       
  2726                     this._readWhitespace();
       
  2727                     expr = this._expr(true);
       
  2728                     functionText += expr;
       
  2729 
       
  2730                     //START: Horrible hack in case it's an IE filter
       
  2731                     if (this.options.ieFilters && tokenStream.peek() === Tokens.EQUALS) {
       
  2732                         do {
       
  2733 
       
  2734                             if (this._readWhitespace()) {
       
  2735                                 functionText += tokenStream.token().value;
       
  2736                             }
       
  2737 
       
  2738                             //might be second time in the loop
       
  2739                             if (tokenStream.LA(0) === Tokens.COMMA) {
       
  2740                                 functionText += tokenStream.token().value;
       
  2741                             }
       
  2742 
       
  2743                             tokenStream.match(Tokens.IDENT);
       
  2744                             functionText += tokenStream.token().value;
       
  2745 
       
  2746                             tokenStream.match(Tokens.EQUALS);
       
  2747                             functionText += tokenStream.token().value;
       
  2748 
       
  2749                             //functionText += this._term();
       
  2750                             lt = tokenStream.peek();
       
  2751                             while (lt !== Tokens.COMMA && lt !== Tokens.S && lt !== Tokens.RPAREN) {
       
  2752                                 tokenStream.get();
       
  2753                                 functionText += tokenStream.token().value;
       
  2754                                 lt = tokenStream.peek();
       
  2755                             }
       
  2756                         } while (tokenStream.match([Tokens.COMMA, Tokens.S]));
       
  2757                     }
       
  2758 
       
  2759                     //END: Horrible Hack
       
  2760 
       
  2761                     tokenStream.match(Tokens.RPAREN);
       
  2762                     functionText += ")";
       
  2763                     this._readWhitespace();
       
  2764                 }
       
  2765 
       
  2766                 return functionText;
       
  2767             },
       
  2768 
       
  2769             _ie_function: function() {
       
  2770 
       
  2771                 /* (My own extension)
       
  2772                  * ie_function
       
  2773                  *   : IE_FUNCTION S* IDENT '=' term [S* ','? IDENT '=' term]+ ')' S*
       
  2774                  *   ;
       
  2775                  */
       
  2776 
       
  2777                 var tokenStream = this._tokenStream,
       
  2778                     functionText = null,
       
  2779                     lt;
       
  2780 
       
  2781                 //IE function can begin like a regular function, too
       
  2782                 if (tokenStream.match([Tokens.IE_FUNCTION, Tokens.FUNCTION])) {
       
  2783                     functionText = tokenStream.token().value;
       
  2784 
       
  2785                     do {
       
  2786 
       
  2787                         if (this._readWhitespace()) {
       
  2788                             functionText += tokenStream.token().value;
       
  2789                         }
       
  2790 
       
  2791                         //might be second time in the loop
       
  2792                         if (tokenStream.LA(0) === Tokens.COMMA) {
       
  2793                             functionText += tokenStream.token().value;
       
  2794                         }
       
  2795 
       
  2796                         tokenStream.match(Tokens.IDENT);
       
  2797                         functionText += tokenStream.token().value;
       
  2798 
       
  2799                         tokenStream.match(Tokens.EQUALS);
       
  2800                         functionText += tokenStream.token().value;
       
  2801 
       
  2802                         //functionText += this._term();
       
  2803                         lt = tokenStream.peek();
       
  2804                         while (lt !== Tokens.COMMA && lt !== Tokens.S && lt !== Tokens.RPAREN) {
       
  2805                             tokenStream.get();
       
  2806                             functionText += tokenStream.token().value;
       
  2807                             lt = tokenStream.peek();
       
  2808                         }
       
  2809                     } while (tokenStream.match([Tokens.COMMA, Tokens.S]));
       
  2810 
       
  2811                     tokenStream.match(Tokens.RPAREN);
       
  2812                     functionText += ")";
       
  2813                     this._readWhitespace();
       
  2814                 }
       
  2815 
       
  2816                 return functionText;
       
  2817             },
       
  2818 
       
  2819             _hexcolor: function() {
       
  2820                 /*
       
  2821                  * There is a constraint on the color that it must
       
  2822                  * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
       
  2823                  * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
       
  2824                  *
       
  2825                  * hexcolor
       
  2826                  *   : HASH S*
       
  2827                  *   ;
       
  2828                  */
       
  2829 
       
  2830                 var tokenStream = this._tokenStream,
       
  2831                     token = null,
       
  2832                     color;
       
  2833 
       
  2834                 if (tokenStream.match(Tokens.HASH)) {
       
  2835 
       
  2836                     //need to do some validation here
       
  2837 
       
  2838                     token = tokenStream.token();
       
  2839                     color = token.value;
       
  2840                     if (!/#[a-f0-9]{3,6}/i.test(color)) {
       
  2841                         throw new SyntaxError("Expected a hex color but found '" + color + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
       
  2842                     }
       
  2843                     this._readWhitespace();
       
  2844                 }
       
  2845 
       
  2846                 return token;
       
  2847             },
       
  2848 
       
  2849             //-----------------------------------------------------------------
       
  2850             // Animations methods
       
  2851             //-----------------------------------------------------------------
       
  2852 
       
  2853             _keyframes: function() {
       
  2854 
       
  2855                 /*
       
  2856                  * keyframes:
       
  2857                  *   : KEYFRAMES_SYM S* keyframe_name S* '{' S* keyframe_rule* '}' {
       
  2858                  *   ;
       
  2859                  */
       
  2860                 var tokenStream = this._tokenStream,
       
  2861                     token,
       
  2862                     tt,
       
  2863                     name,
       
  2864                     prefix = "";
       
  2865 
       
  2866                 tokenStream.mustMatch(Tokens.KEYFRAMES_SYM);
       
  2867                 token = tokenStream.token();
       
  2868                 if (/^@\-([^\-]+)\-/.test(token.value)) {
       
  2869                     prefix = RegExp.$1;
       
  2870                 }
       
  2871 
       
  2872                 this._readWhitespace();
       
  2873                 name = this._keyframe_name();
       
  2874 
       
  2875                 this._readWhitespace();
       
  2876                 tokenStream.mustMatch(Tokens.LBRACE);
       
  2877 
       
  2878                 this.fire({
       
  2879                     type:   "startkeyframes",
       
  2880                     name:   name,
       
  2881                     prefix: prefix,
       
  2882                     line:   token.startLine,
       
  2883                     col:    token.startCol
       
  2884                 });
       
  2885 
       
  2886                 this._readWhitespace();
       
  2887                 tt = tokenStream.peek();
       
  2888 
       
  2889                 //check for key
       
  2890                 while (tt === Tokens.IDENT || tt === Tokens.PERCENTAGE) {
       
  2891                     this._keyframe_rule();
       
  2892                     this._readWhitespace();
       
  2893                     tt = tokenStream.peek();
       
  2894                 }
       
  2895 
       
  2896                 this.fire({
       
  2897                     type:   "endkeyframes",
       
  2898                     name:   name,
       
  2899                     prefix: prefix,
       
  2900                     line:   token.startLine,
       
  2901                     col:    token.startCol
       
  2902                 });
       
  2903 
       
  2904                 this._readWhitespace();
       
  2905                 tokenStream.mustMatch(Tokens.RBRACE);
       
  2906                 this._readWhitespace();
       
  2907 
       
  2908             },
       
  2909 
       
  2910             _keyframe_name: function() {
       
  2911 
       
  2912                 /*
       
  2913                  * keyframe_name:
       
  2914                  *   : IDENT
       
  2915                  *   | STRING
       
  2916                  *   ;
       
  2917                  */
       
  2918                 var tokenStream = this._tokenStream;
       
  2919 
       
  2920                 tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
       
  2921                 return SyntaxUnit.fromToken(tokenStream.token());
       
  2922             },
       
  2923 
       
  2924             _keyframe_rule: function() {
       
  2925 
       
  2926                 /*
       
  2927                  * keyframe_rule:
       
  2928                  *   : key_list S*
       
  2929                  *     '{' S* declaration [ ';' S* declaration ]* '}' S*
       
  2930                  *   ;
       
  2931                  */
       
  2932                 var keyList = this._key_list();
       
  2933 
       
  2934                 this.fire({
       
  2935                     type:   "startkeyframerule",
       
  2936                     keys:   keyList,
       
  2937                     line:   keyList[0].line,
       
  2938                     col:    keyList[0].col
       
  2939                 });
       
  2940 
       
  2941                 this._readDeclarations(true);
       
  2942 
       
  2943                 this.fire({
       
  2944                     type:   "endkeyframerule",
       
  2945                     keys:   keyList,
       
  2946                     line:   keyList[0].line,
       
  2947                     col:    keyList[0].col
       
  2948                 });
       
  2949 
       
  2950             },
       
  2951 
       
  2952             _key_list: function() {
       
  2953 
       
  2954                 /*
       
  2955                  * key_list:
       
  2956                  *   : key [ S* ',' S* key]*
       
  2957                  *   ;
       
  2958                  */
       
  2959                 var tokenStream = this._tokenStream,
       
  2960                     keyList = [];
       
  2961 
       
  2962                 //must be least one key
       
  2963                 keyList.push(this._key());
       
  2964 
       
  2965                 this._readWhitespace();
       
  2966 
       
  2967                 while (tokenStream.match(Tokens.COMMA)) {
       
  2968                     this._readWhitespace();
       
  2969                     keyList.push(this._key());
       
  2970                     this._readWhitespace();
       
  2971                 }
       
  2972 
       
  2973                 return keyList;
       
  2974             },
       
  2975 
       
  2976             _key: function() {
       
  2977                 /*
       
  2978                  * There is a restriction that IDENT can be only "from" or "to".
       
  2979                  *
       
  2980                  * key
       
  2981                  *   : PERCENTAGE
       
  2982                  *   | IDENT
       
  2983                  *   ;
       
  2984                  */
       
  2985 
       
  2986                 var tokenStream = this._tokenStream,
       
  2987                     token;
       
  2988 
       
  2989                 if (tokenStream.match(Tokens.PERCENTAGE)) {
       
  2990                     return SyntaxUnit.fromToken(tokenStream.token());
       
  2991                 } else if (tokenStream.match(Tokens.IDENT)) {
       
  2992                     token = tokenStream.token();
       
  2993 
       
  2994                     if (/from|to/i.test(token.value)) {
       
  2995                         return SyntaxUnit.fromToken(token);
       
  2996                     }
       
  2997 
       
  2998                     tokenStream.unget();
       
  2999                 }
       
  3000 
       
  3001                 //if it gets here, there wasn't a valid token, so time to explode
       
  3002                 this._unexpectedToken(tokenStream.LT(1));
       
  3003             },
       
  3004 
       
  3005             //-----------------------------------------------------------------
       
  3006             // Helper methods
       
  3007             //-----------------------------------------------------------------
       
  3008 
       
  3009             /**
       
  3010              * Not part of CSS grammar, but useful for skipping over
       
  3011              * combination of white space and HTML-style comments.
       
  3012              * @return {void}
       
  3013              * @method _skipCruft
       
  3014              * @private
       
  3015              */
       
  3016             _skipCruft: function() {
       
  3017                 while (this._tokenStream.match([Tokens.S, Tokens.CDO, Tokens.CDC])) {
       
  3018                     //noop
       
  3019                 }
       
  3020             },
       
  3021 
       
  3022             /**
       
  3023              * Not part of CSS grammar, but this pattern occurs frequently
       
  3024              * in the official CSS grammar. Split out here to eliminate
       
  3025              * duplicate code.
       
  3026              * @param {Boolean} checkStart Indicates if the rule should check
       
  3027              *      for the left brace at the beginning.
       
  3028              * @param {Boolean} readMargins Indicates if the rule should check
       
  3029              *      for margin patterns.
       
  3030              * @return {void}
       
  3031              * @method _readDeclarations
       
  3032              * @private
       
  3033              */
       
  3034             _readDeclarations: function(checkStart, readMargins) {
       
  3035                 /*
       
  3036                  * Reads the pattern
       
  3037                  * S* '{' S* declaration [ ';' S* declaration ]* '}' S*
       
  3038                  * or
       
  3039                  * S* '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
       
  3040                  * Note that this is how it is described in CSS3 Paged Media, but is actually incorrect.
       
  3041                  * A semicolon is only necessary following a declaration if there's another declaration
       
  3042                  * or margin afterwards.
       
  3043                  */
       
  3044                 var tokenStream = this._tokenStream,
       
  3045                     tt;
       
  3046 
       
  3047 
       
  3048                 this._readWhitespace();
       
  3049 
       
  3050                 if (checkStart) {
       
  3051                     tokenStream.mustMatch(Tokens.LBRACE);
       
  3052                 }
       
  3053 
       
  3054                 this._readWhitespace();
       
  3055 
       
  3056                 try {
       
  3057 
       
  3058                     while (true) {
       
  3059 
       
  3060                         if (tokenStream.match(Tokens.SEMICOLON) || (readMargins && this._margin())) {
       
  3061                             //noop
       
  3062                         } else if (this._declaration()) {
       
  3063                             if (!tokenStream.match(Tokens.SEMICOLON)) {
       
  3064                                 break;
       
  3065                             }
       
  3066                         } else {
       
  3067                             break;
       
  3068                         }
       
  3069 
       
  3070                         //if ((!this._margin() && !this._declaration()) || !tokenStream.match(Tokens.SEMICOLON)){
       
  3071                         //    break;
       
  3072                         //}
       
  3073                         this._readWhitespace();
       
  3074                     }
       
  3075 
       
  3076                     tokenStream.mustMatch(Tokens.RBRACE);
       
  3077                     this._readWhitespace();
       
  3078 
       
  3079                 } catch (ex) {
       
  3080                     if (ex instanceof SyntaxError && !this.options.strict) {
       
  3081 
       
  3082                         //fire error event
       
  3083                         this.fire({
       
  3084                             type:       "error",
       
  3085                             error:      ex,
       
  3086                             message:    ex.message,
       
  3087                             line:       ex.line,
       
  3088                             col:        ex.col
       
  3089                         });
       
  3090 
       
  3091                         //see if there's another declaration
       
  3092                         tt = tokenStream.advance([Tokens.SEMICOLON, Tokens.RBRACE]);
       
  3093                         if (tt === Tokens.SEMICOLON) {
       
  3094                             //if there's a semicolon, then there might be another declaration
       
  3095                             this._readDeclarations(false, readMargins);
       
  3096                         } else if (tt !== Tokens.RBRACE) {
       
  3097                             //if there's a right brace, the rule is finished so don't do anything
       
  3098                             //otherwise, rethrow the error because it wasn't handled properly
       
  3099                             throw ex;
       
  3100                         }
       
  3101 
       
  3102                     } else {
       
  3103                         //not a syntax error, rethrow it
       
  3104                         throw ex;
       
  3105                     }
       
  3106                 }
       
  3107 
       
  3108             },
       
  3109 
       
  3110             /**
       
  3111              * In some cases, you can end up with two white space tokens in a
       
  3112              * row. Instead of making a change in every function that looks for
       
  3113              * white space, this function is used to match as much white space
       
  3114              * as necessary.
       
  3115              * @method _readWhitespace
       
  3116              * @return {String} The white space if found, empty string if not.
       
  3117              * @private
       
  3118              */
       
  3119             _readWhitespace: function() {
       
  3120 
       
  3121                 var tokenStream = this._tokenStream,
       
  3122                     ws = "";
       
  3123 
       
  3124                 while (tokenStream.match(Tokens.S)) {
       
  3125                     ws += tokenStream.token().value;
       
  3126                 }
       
  3127 
       
  3128                 return ws;
       
  3129             },
       
  3130 
       
  3131 
       
  3132             /**
       
  3133              * Throws an error when an unexpected token is found.
       
  3134              * @param {Object} token The token that was found.
       
  3135              * @method _unexpectedToken
       
  3136              * @return {void}
       
  3137              * @private
       
  3138              */
       
  3139             _unexpectedToken: function(token) {
       
  3140                 throw new SyntaxError("Unexpected token '" + token.value + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
       
  3141             },
       
  3142 
       
  3143             /**
       
  3144              * Helper method used for parsing subparts of a style sheet.
       
  3145              * @return {void}
       
  3146              * @method _verifyEnd
       
  3147              * @private
       
  3148              */
       
  3149             _verifyEnd: function() {
       
  3150                 if (this._tokenStream.LA(1) !== Tokens.EOF) {
       
  3151                     this._unexpectedToken(this._tokenStream.LT(1));
       
  3152                 }
       
  3153             },
       
  3154 
       
  3155             //-----------------------------------------------------------------
       
  3156             // Validation methods
       
  3157             //-----------------------------------------------------------------
       
  3158             _validateProperty: function(property, value) {
       
  3159                 Validation.validate(property, value);
       
  3160             },
       
  3161 
       
  3162             //-----------------------------------------------------------------
       
  3163             // Parsing methods
       
  3164             //-----------------------------------------------------------------
       
  3165 
       
  3166             parse: function(input) {
       
  3167                 this._tokenStream = new TokenStream(input, Tokens);
       
  3168                 this._stylesheet();
       
  3169             },
       
  3170 
       
  3171             parseStyleSheet: function(input) {
       
  3172                 //just passthrough
       
  3173                 return this.parse(input);
       
  3174             },
       
  3175 
       
  3176             parseMediaQuery: function(input) {
       
  3177                 this._tokenStream = new TokenStream(input, Tokens);
       
  3178                 var result = this._media_query();
       
  3179 
       
  3180                 //if there's anything more, then it's an invalid selector
       
  3181                 this._verifyEnd();
       
  3182 
       
  3183                 //otherwise return result
       
  3184                 return result;
       
  3185             },
       
  3186 
       
  3187             /**
       
  3188              * Parses a property value (everything after the semicolon).
       
  3189              * @return {parserlib.css.PropertyValue} The property value.
       
  3190              * @throws parserlib.util.SyntaxError If an unexpected token is found.
       
  3191              * @method parserPropertyValue
       
  3192              */
       
  3193             parsePropertyValue: function(input) {
       
  3194 
       
  3195                 this._tokenStream = new TokenStream(input, Tokens);
       
  3196                 this._readWhitespace();
       
  3197 
       
  3198                 var result = this._expr();
       
  3199 
       
  3200                 //okay to have a trailing white space
       
  3201                 this._readWhitespace();
       
  3202 
       
  3203                 //if there's anything more, then it's an invalid selector
       
  3204                 this._verifyEnd();
       
  3205 
       
  3206                 //otherwise return result
       
  3207                 return result;
       
  3208             },
       
  3209 
       
  3210             /**
       
  3211              * Parses a complete CSS rule, including selectors and
       
  3212              * properties.
       
  3213              * @param {String} input The text to parser.
       
  3214              * @return {Boolean} True if the parse completed successfully, false if not.
       
  3215              * @method parseRule
       
  3216              */
       
  3217             parseRule: function(input) {
       
  3218                 this._tokenStream = new TokenStream(input, Tokens);
       
  3219 
       
  3220                 //skip any leading white space
       
  3221                 this._readWhitespace();
       
  3222 
       
  3223                 var result = this._ruleset();
       
  3224 
       
  3225                 //skip any trailing white space
       
  3226                 this._readWhitespace();
       
  3227 
       
  3228                 //if there's anything more, then it's an invalid selector
       
  3229                 this._verifyEnd();
       
  3230 
       
  3231                 //otherwise return result
       
  3232                 return result;
       
  3233             },
       
  3234 
       
  3235             /**
       
  3236              * Parses a single CSS selector (no comma)
       
  3237              * @param {String} input The text to parse as a CSS selector.
       
  3238              * @return {Selector} An object representing the selector.
       
  3239              * @throws parserlib.util.SyntaxError If an unexpected token is found.
       
  3240              * @method parseSelector
       
  3241              */
       
  3242             parseSelector: function(input) {
       
  3243 
       
  3244                 this._tokenStream = new TokenStream(input, Tokens);
       
  3245 
       
  3246                 //skip any leading white space
       
  3247                 this._readWhitespace();
       
  3248 
       
  3249                 var result = this._selector();
       
  3250 
       
  3251                 //skip any trailing white space
       
  3252                 this._readWhitespace();
       
  3253 
       
  3254                 //if there's anything more, then it's an invalid selector
       
  3255                 this._verifyEnd();
       
  3256 
       
  3257                 //otherwise return result
       
  3258                 return result;
       
  3259             },
       
  3260 
       
  3261             /**
       
  3262              * Parses an HTML style attribute: a set of CSS declarations
       
  3263              * separated by semicolons.
       
  3264              * @param {String} input The text to parse as a style attribute
       
  3265              * @return {void}
       
  3266              * @method parseStyleAttribute
       
  3267              */
       
  3268             parseStyleAttribute: function(input) {
       
  3269                 input += "}"; // for error recovery in _readDeclarations()
       
  3270                 this._tokenStream = new TokenStream(input, Tokens);
       
  3271                 this._readDeclarations();
       
  3272             }
       
  3273         };
       
  3274 
       
  3275     //copy over onto prototype
       
  3276     for (prop in additions) {
       
  3277         if (Object.prototype.hasOwnProperty.call(additions, prop)) {
       
  3278             proto[prop] = additions[prop];
       
  3279         }
       
  3280     }
       
  3281 
       
  3282     return proto;
       
  3283 }();
       
  3284 
       
  3285 
       
  3286 /*
       
  3287 nth
       
  3288   : S* [ ['-'|'+']? INTEGER? {N} [ S* ['-'|'+'] S* INTEGER ]? |
       
  3289          ['-'|'+']? INTEGER | {O}{D}{D} | {E}{V}{E}{N} ] S*
       
  3290   ;
       
  3291 */
       
  3292 
       
  3293 },{"../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){
       
  3294 "use strict";
       
  3295 
       
  3296 /* exported Properties */
       
  3297 
       
  3298 var Properties = module.exports = {
       
  3299     __proto__: null,
       
  3300 
       
  3301     //A
       
  3302     "align-items"                   : "flex-start | flex-end | center | baseline | stretch",
       
  3303     "align-content"                 : "flex-start | flex-end | center | space-between | space-around | stretch",
       
  3304     "align-self"                    : "auto | flex-start | flex-end | center | baseline | stretch",
       
  3305     "all"                           : "initial | inherit | unset",
       
  3306     "-webkit-align-items"           : "flex-start | flex-end | center | baseline | stretch",
       
  3307     "-webkit-align-content"         : "flex-start | flex-end | center | space-between | space-around | stretch",
       
  3308     "-webkit-align-self"            : "auto | flex-start | flex-end | center | baseline | stretch",
       
  3309     "alignment-adjust"              : "auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | <percentage> | <length>",
       
  3310     "alignment-baseline"            : "auto | baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
       
  3311     "animation"                     : 1,
       
  3312     "animation-delay"               : "<time>#",
       
  3313     "animation-direction"           : "<single-animation-direction>#",
       
  3314     "animation-duration"            : "<time>#",
       
  3315     "animation-fill-mode"           : "[ none | forwards | backwards | both ]#",
       
  3316     "animation-iteration-count"     : "[ <number> | infinite ]#",
       
  3317     "animation-name"                : "[ none | <single-animation-name> ]#",
       
  3318     "animation-play-state"          : "[ running | paused ]#",
       
  3319     "animation-timing-function"     : 1,
       
  3320 
       
  3321     //vendor prefixed
       
  3322     "-moz-animation-delay"               : "<time>#",
       
  3323     "-moz-animation-direction"           : "[ normal | alternate ]#",
       
  3324     "-moz-animation-duration"            : "<time>#",
       
  3325     "-moz-animation-iteration-count"     : "[ <number> | infinite ]#",
       
  3326     "-moz-animation-name"                : "[ none | <single-animation-name> ]#",
       
  3327     "-moz-animation-play-state"          : "[ running | paused ]#",
       
  3328 
       
  3329     "-ms-animation-delay"               : "<time>#",
       
  3330     "-ms-animation-direction"           : "[ normal | alternate ]#",
       
  3331     "-ms-animation-duration"            : "<time>#",
       
  3332     "-ms-animation-iteration-count"     : "[ <number> | infinite ]#",
       
  3333     "-ms-animation-name"                : "[ none | <single-animation-name> ]#",
       
  3334     "-ms-animation-play-state"          : "[ running | paused ]#",
       
  3335 
       
  3336     "-webkit-animation-delay"               : "<time>#",
       
  3337     "-webkit-animation-direction"           : "[ normal | alternate ]#",
       
  3338     "-webkit-animation-duration"            : "<time>#",
       
  3339     "-webkit-animation-fill-mode"           : "[ none | forwards | backwards | both ]#",
       
  3340     "-webkit-animation-iteration-count"     : "[ <number> | infinite ]#",
       
  3341     "-webkit-animation-name"                : "[ none | <single-animation-name> ]#",
       
  3342     "-webkit-animation-play-state"          : "[ running | paused ]#",
       
  3343 
       
  3344     "-o-animation-delay"               : "<time>#",
       
  3345     "-o-animation-direction"           : "[ normal | alternate ]#",
       
  3346     "-o-animation-duration"            : "<time>#",
       
  3347     "-o-animation-iteration-count"     : "[ <number> | infinite ]#",
       
  3348     "-o-animation-name"                : "[ none | <single-animation-name> ]#",
       
  3349     "-o-animation-play-state"          : "[ running | paused ]#",
       
  3350 
       
  3351     "appearance"                    : "none | auto",
       
  3352     "-moz-appearance"               : "none | button | button-arrow-down | button-arrow-next | button-arrow-previous | button-arrow-up | button-bevel | button-focus | caret | checkbox | checkbox-container | checkbox-label | checkmenuitem | dualbutton | groupbox | listbox | listitem | menuarrow | menubar | menucheckbox | menuimage | menuitem | menuitemtext | menulist | menulist-button | menulist-text | menulist-textfield | menupopup | menuradio | menuseparator | meterbar | meterchunk | progressbar | progressbar-vertical | progresschunk | progresschunk-vertical | radio | radio-container | radio-label | radiomenuitem | range | range-thumb | resizer | resizerpanel | scale-horizontal | scalethumbend | scalethumb-horizontal | scalethumbstart | scalethumbtick | scalethumb-vertical | scale-vertical | scrollbarbutton-down | scrollbarbutton-left | scrollbarbutton-right | scrollbarbutton-up | scrollbarthumb-horizontal | scrollbarthumb-vertical | scrollbartrack-horizontal | scrollbartrack-vertical | searchfield | separator | sheet | spinner | spinner-downbutton | spinner-textfield | spinner-upbutton | splitter | statusbar | statusbarpanel | tab | tabpanel | tabpanels | tab-scroll-arrow-back | tab-scroll-arrow-forward | textfield | textfield-multiline | toolbar | toolbarbutton | toolbarbutton-dropdown | toolbargripper | toolbox | tooltip | treeheader | treeheadercell | treeheadersortarrow | treeitem | treeline | treetwisty | treetwistyopen | treeview | -moz-mac-unified-toolbar | -moz-win-borderless-glass | -moz-win-browsertabbar-toolbox | -moz-win-communicationstext | -moz-win-communications-toolbox | -moz-win-exclude-glass | -moz-win-glass | -moz-win-mediatext | -moz-win-media-toolbox | -moz-window-button-box | -moz-window-button-box-maximized | -moz-window-button-close | -moz-window-button-maximize | -moz-window-button-minimize | -moz-window-button-restore | -moz-window-frame-bottom | -moz-window-frame-left | -moz-window-frame-right | -moz-window-titlebar | -moz-window-titlebar-maximized",
       
  3353     "-ms-appearance"                : "none | icon | window | desktop | workspace | document | tooltip | dialog | button | push-button | hyperlink | radio | radio-button | checkbox | menu-item | tab | menu | menubar | pull-down-menu | pop-up-menu | list-menu | radio-group | checkbox-group | outline-tree | range | field | combo-box | signature | password | normal",
       
  3354     "-webkit-appearance"            : "none | button | button-bevel | caps-lock-indicator | caret | checkbox | default-button | listbox	| listitem | media-fullscreen-button | media-mute-button | media-play-button | media-seek-back-button	| media-seek-forward-button	| media-slider | media-sliderthumb | menulist	| menulist-button	| menulist-text	| menulist-textfield | push-button	| radio	| searchfield	| searchfield-cancel-button	| searchfield-decoration | searchfield-results-button | searchfield-results-decoration | slider-horizontal | slider-vertical | sliderthumb-horizontal | sliderthumb-vertical	| square-button	| textarea	| textfield	| scrollbarbutton-down | scrollbarbutton-left | scrollbarbutton-right | scrollbarbutton-up | scrollbargripper-horizontal | scrollbargripper-vertical | scrollbarthumb-horizontal | scrollbarthumb-vertical | scrollbartrack-horizontal | scrollbartrack-vertical",
       
  3355     "-o-appearance"                 : "none | window | desktop | workspace | document | tooltip | dialog | button | push-button | hyperlink | radio | radio-button | checkbox | menu-item | tab | menu | menubar | pull-down-menu | pop-up-menu | list-menu | radio-group | checkbox-group | outline-tree | range | field | combo-box | signature | password | normal",
       
  3356 
       
  3357     "azimuth"                       : "<azimuth>",
       
  3358 
       
  3359     //B
       
  3360     "backface-visibility"           : "visible | hidden",
       
  3361     "background"                    : 1,
       
  3362     "background-attachment"         : "<attachment>#",
       
  3363     "background-clip"               : "<box>#",
       
  3364     "background-color"              : "<color>",
       
  3365     "background-image"              : "<bg-image>#",
       
  3366     "background-origin"             : "<box>#",
       
  3367     "background-position"           : "<bg-position>",
       
  3368     "background-repeat"             : "<repeat-style>#",
       
  3369     "background-size"               : "<bg-size>#",
       
  3370     "baseline-shift"                : "baseline | sub | super | <percentage> | <length>",
       
  3371     "behavior"                      : 1,
       
  3372     "binding"                       : 1,
       
  3373     "bleed"                         : "<length>",
       
  3374     "bookmark-label"                : "<content> | <attr> | <string>",
       
  3375     "bookmark-level"                : "none | <integer>",
       
  3376     "bookmark-state"                : "open | closed",
       
  3377     "bookmark-target"               : "none | <uri> | <attr>",
       
  3378     "border"                        : "<border-width> || <border-style> || <color>",
       
  3379     "border-bottom"                 : "<border-width> || <border-style> || <color>",
       
  3380     "border-bottom-color"           : "<color>",
       
  3381     "border-bottom-left-radius"     :  "<x-one-radius>",
       
  3382     "border-bottom-right-radius"    :  "<x-one-radius>",
       
  3383     "border-bottom-style"           : "<border-style>",
       
  3384     "border-bottom-width"           : "<border-width>",
       
  3385     "border-collapse"               : "collapse | separate",
       
  3386     "border-color"                  : "<color>{1,4}",
       
  3387     "border-image"                  : 1,
       
  3388     "border-image-outset"           : "[ <length> | <number> ]{1,4}",
       
  3389     "border-image-repeat"           : "[ stretch | repeat | round ]{1,2}",
       
  3390     "border-image-slice"            : "<border-image-slice>",
       
  3391     "border-image-source"           : "<image> | none",
       
  3392     "border-image-width"            : "[ <length> | <percentage> | <number> | auto ]{1,4}",
       
  3393     "border-left"                   : "<border-width> || <border-style> || <color>",
       
  3394     "border-left-color"             : "<color>",
       
  3395     "border-left-style"             : "<border-style>",
       
  3396     "border-left-width"             : "<border-width>",
       
  3397     "border-radius"                 : "<border-radius>",
       
  3398     "border-right"                  : "<border-width> || <border-style> || <color>",
       
  3399     "border-right-color"            : "<color>",
       
  3400     "border-right-style"            : "<border-style>",
       
  3401     "border-right-width"            : "<border-width>",
       
  3402     "border-spacing"                : "<length>{1,2}",
       
  3403     "border-style"                  : "<border-style>{1,4}",
       
  3404     "border-top"                    : "<border-width> || <border-style> || <color>",
       
  3405     "border-top-color"              : "<color>",
       
  3406     "border-top-left-radius"        : "<x-one-radius>",
       
  3407     "border-top-right-radius"       : "<x-one-radius>",
       
  3408     "border-top-style"              : "<border-style>",
       
  3409     "border-top-width"              : "<border-width>",
       
  3410     "border-width"                  : "<border-width>{1,4}",
       
  3411     "bottom"                        : "<margin-width>",
       
  3412     "-moz-box-align"                : "start | end | center | baseline | stretch",
       
  3413     "-moz-box-decoration-break"     : "slice | clone",
       
  3414     "-moz-box-direction"            : "normal | reverse",
       
  3415     "-moz-box-flex"                 : "<number>",
       
  3416     "-moz-box-flex-group"           : "<integer>",
       
  3417     "-moz-box-lines"                : "single | multiple",
       
  3418     "-moz-box-ordinal-group"        : "<integer>",
       
  3419     "-moz-box-orient"               : "horizontal | vertical | inline-axis | block-axis",
       
  3420     "-moz-box-pack"                 : "start | end | center | justify",
       
  3421     "-o-box-decoration-break"       : "slice | clone",
       
  3422     "-webkit-box-align"             : "start | end | center | baseline | stretch",
       
  3423     "-webkit-box-decoration-break"  : "slice | clone",
       
  3424     "-webkit-box-direction"         : "normal | reverse",
       
  3425     "-webkit-box-flex"              : "<number>",
       
  3426     "-webkit-box-flex-group"        : "<integer>",
       
  3427     "-webkit-box-lines"             : "single | multiple",
       
  3428     "-webkit-box-ordinal-group"     : "<integer>",
       
  3429     "-webkit-box-orient"            : "horizontal | vertical | inline-axis | block-axis",
       
  3430     "-webkit-box-pack"              : "start | end | center | justify",
       
  3431     "box-decoration-break"          : "slice | clone",
       
  3432     "box-shadow"                    : "<box-shadow>",
       
  3433     "box-sizing"                    : "content-box | border-box",
       
  3434     "break-after"                   : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
       
  3435     "break-before"                  : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
       
  3436     "break-inside"                  : "auto | avoid | avoid-page | avoid-column",
       
  3437 
       
  3438     //C
       
  3439     "caption-side"                  : "top | bottom",
       
  3440     "clear"                         : "none | right | left | both",
       
  3441     "clip"                          : "<shape> | auto",
       
  3442     "-webkit-clip-path"             : "<clip-source> | <clip-path> | none",
       
  3443     "clip-path"                     : "<clip-source> | <clip-path> | none",
       
  3444     "clip-rule"                     : "nonzero | evenodd",
       
  3445     "color"                         : "<color>",
       
  3446     "color-interpolation"           : "auto | sRGB | linearRGB",
       
  3447     "color-interpolation-filters"   : "auto | sRGB | linearRGB",
       
  3448     "color-profile"                 : 1,
       
  3449     "color-rendering"               : "auto | optimizeSpeed | optimizeQuality",
       
  3450     "column-count"                  : "<integer> | auto",                      //https://www.w3.org/TR/css3-multicol/
       
  3451     "column-fill"                   : "auto | balance",
       
  3452     "column-gap"                    : "<length> | normal",
       
  3453     "column-rule"                   : "<border-width> || <border-style> || <color>",
       
  3454     "column-rule-color"             : "<color>",
       
  3455     "column-rule-style"             : "<border-style>",
       
  3456     "column-rule-width"             : "<border-width>",
       
  3457     "column-span"                   : "none | all",
       
  3458     "column-width"                  : "<length> | auto",
       
  3459     "columns"                       : 1,
       
  3460     "content"                       : 1,
       
  3461     "counter-increment"             : 1,
       
  3462     "counter-reset"                 : 1,
       
  3463     "crop"                          : "<shape> | auto",
       
  3464     "cue"                           : "cue-after | cue-before",
       
  3465     "cue-after"                     : 1,
       
  3466     "cue-before"                    : 1,
       
  3467     "cursor"                        : 1,
       
  3468 
       
  3469     //D
       
  3470     "direction"                     : "ltr | rtl",
       
  3471     "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 | grid | inline-grid | run-in | ruby | ruby-base | ruby-text | ruby-base-container | ruby-text-container | contents | none | -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 | -ms-flexbox | -ms-inline-flexbox | flex | -webkit-flex | inline-flex | -webkit-inline-flex",
       
  3472     "dominant-baseline"             : "auto | use-script | no-change | reset-size | ideographic | alphabetic | hanging | mathematical | central | middle | text-after-edge | text-before-edge",
       
  3473     "drop-initial-after-adjust"     : "central | middle | after-edge | text-after-edge | ideographic | alphabetic | mathematical | <percentage> | <length>",
       
  3474     "drop-initial-after-align"      : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
       
  3475     "drop-initial-before-adjust"    : "before-edge | text-before-edge | central | middle | hanging | mathematical | <percentage> | <length>",
       
  3476     "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",
       
  3477     "drop-initial-size"             : "auto | line | <length> | <percentage>",
       
  3478     "drop-initial-value"            : "<integer>",
       
  3479 
       
  3480     //E
       
  3481     "elevation"                     : "<angle> | below | level | above | higher | lower",
       
  3482     "empty-cells"                   : "show | hide",
       
  3483     "enable-background"             : 1,
       
  3484 
       
  3485     //F
       
  3486     "fill"                          : "<paint>",
       
  3487     "fill-opacity"                  : "<opacity-value>",
       
  3488     "fill-rule"                     : "nonzero | evenodd",
       
  3489     "filter"                        : "<filter-function-list> | none",
       
  3490     "fit"                           : "fill | hidden | meet | slice",
       
  3491     "fit-position"                  : 1,
       
  3492     "flex"                          : "<flex>",
       
  3493     "flex-basis"                    : "<width>",
       
  3494     "flex-direction"                : "row | row-reverse | column | column-reverse",
       
  3495     "flex-flow"                     : "<flex-direction> || <flex-wrap>",
       
  3496     "flex-grow"                     : "<number>",
       
  3497     "flex-shrink"                   : "<number>",
       
  3498     "flex-wrap"                     : "nowrap | wrap | wrap-reverse",
       
  3499     "-webkit-flex"                  : "<flex>",
       
  3500     "-webkit-flex-basis"            : "<width>",
       
  3501     "-webkit-flex-direction"        : "row | row-reverse | column | column-reverse",
       
  3502     "-webkit-flex-flow"             : "<flex-direction> || <flex-wrap>",
       
  3503     "-webkit-flex-grow"             : "<number>",
       
  3504     "-webkit-flex-shrink"           : "<number>",
       
  3505     "-webkit-flex-wrap"             : "nowrap | wrap | wrap-reverse",
       
  3506     "-ms-flex"                      : "<flex>",
       
  3507     "-ms-flex-align"                : "start | end | center | stretch | baseline",
       
  3508     "-ms-flex-direction"            : "row | row-reverse | column | column-reverse",
       
  3509     "-ms-flex-order"                : "<number>",
       
  3510     "-ms-flex-pack"                 : "start | end | center | justify",
       
  3511     "-ms-flex-wrap"                 : "nowrap | wrap | wrap-reverse",
       
  3512     "float"                         : "left | right | none",
       
  3513     "float-offset"                  : 1,
       
  3514     "flood-color"                   : 1,
       
  3515     "flood-opacity"                 : "<opacity-value>",
       
  3516     "font"                          : "<font-shorthand> | caption | icon | menu | message-box | small-caption | status-bar",
       
  3517     "font-family"                   : "<font-family>",
       
  3518     "font-feature-settings"         : "<feature-tag-value> | normal",
       
  3519     "font-kerning"                  : "auto | normal | none",
       
  3520     "font-size"                     : "<font-size>",
       
  3521     "font-size-adjust"              : "<number> | none",
       
  3522     "font-stretch"                  : "<font-stretch>",
       
  3523     "font-style"                    : "<font-style>",
       
  3524     "font-variant"                  : "<font-variant> | normal | none",
       
  3525     "font-variant-alternates"       : "<font-variant-alternates> | normal",
       
  3526     "font-variant-caps"             : "<font-variant-caps> | normal",
       
  3527     "font-variant-east-asian"       : "<font-variant-east-asian> | normal",
       
  3528     "font-variant-ligatures"        : "<font-variant-ligatures> | normal | none",
       
  3529     "font-variant-numeric"          : "<font-variant-numeric> | normal",
       
  3530     "font-variant-position"         : "normal | sub | super",
       
  3531     "font-weight"                   : "<font-weight>",
       
  3532 
       
  3533     //G
       
  3534     "glyph-orientation-horizontal"  : "<glyph-angle>",
       
  3535     "glyph-orientation-vertical"    : "auto | <glyph-angle>",
       
  3536     "grid"                          : 1,
       
  3537     "grid-area"                     : 1,
       
  3538     "grid-auto-columns"             : 1,
       
  3539     "grid-auto-flow"                : 1,
       
  3540     "grid-auto-position"            : 1,
       
  3541     "grid-auto-rows"                : 1,
       
  3542     "grid-cell-stacking"            : "columns | rows | layer",
       
  3543     "grid-column"                   : 1,
       
  3544     "grid-columns"                  : 1,
       
  3545     "grid-column-align"             : "start | end | center | stretch",
       
  3546     "grid-column-sizing"            : 1,
       
  3547     "grid-column-start"             : 1,
       
  3548     "grid-column-end"               : 1,
       
  3549     "grid-column-span"              : "<integer>",
       
  3550     "grid-flow"                     : "none | rows | columns",
       
  3551     "grid-layer"                    : "<integer>",
       
  3552     "grid-row"                      : 1,
       
  3553     "grid-rows"                     : 1,
       
  3554     "grid-row-align"                : "start | end | center | stretch",
       
  3555     "grid-row-start"                : 1,
       
  3556     "grid-row-end"                  : 1,
       
  3557     "grid-row-span"                 : "<integer>",
       
  3558     "grid-row-sizing"               : 1,
       
  3559     "grid-template"                 : 1,
       
  3560     "grid-template-areas"           : 1,
       
  3561     "grid-template-columns"         : 1,
       
  3562     "grid-template-rows"            : 1,
       
  3563 
       
  3564     //H
       
  3565     "hanging-punctuation"           : 1,
       
  3566     "height"                        : "<margin-width> | <content-sizing>",
       
  3567     "hyphenate-after"               : "<integer> | auto",
       
  3568     "hyphenate-before"              : "<integer> | auto",
       
  3569     "hyphenate-character"           : "<string> | auto",
       
  3570     "hyphenate-lines"               : "no-limit | <integer>",
       
  3571     "hyphenate-resource"            : 1,
       
  3572     "hyphens"                       : "none | manual | auto",
       
  3573 
       
  3574     //I
       
  3575     "icon"                          : 1,
       
  3576     "image-orientation"             : "angle | auto",
       
  3577     "image-rendering"               : "auto | optimizeSpeed | optimizeQuality",
       
  3578     "image-resolution"              : 1,
       
  3579     "ime-mode"                      : "auto | normal | active | inactive | disabled",
       
  3580     "inline-box-align"              : "last | <integer>",
       
  3581 
       
  3582     //J
       
  3583     "justify-content"               : "flex-start | flex-end | center | space-between | space-around",
       
  3584     "-webkit-justify-content"       : "flex-start | flex-end | center | space-between | space-around",
       
  3585 
       
  3586     //K
       
  3587     "kerning"                       : "auto | <length>",
       
  3588 
       
  3589     //L
       
  3590     "left"                          : "<margin-width>",
       
  3591     "letter-spacing"                : "<length> | normal",
       
  3592     "line-height"                   : "<line-height>",
       
  3593     "line-break"                    : "auto | loose | normal | strict",
       
  3594     "line-stacking"                 : 1,
       
  3595     "line-stacking-ruby"            : "exclude-ruby | include-ruby",
       
  3596     "line-stacking-shift"           : "consider-shifts | disregard-shifts",
       
  3597     "line-stacking-strategy"        : "inline-line-height | block-line-height | max-height | grid-height",
       
  3598     "list-style"                    : 1,
       
  3599     "list-style-image"              : "<uri> | none",
       
  3600     "list-style-position"           : "inside | outside",
       
  3601     "list-style-type"               : "disc | circle | square | decimal | decimal-leading-zero | lower-roman | upper-roman | lower-greek | lower-latin | upper-latin | armenian | georgian | lower-alpha | upper-alpha | none",
       
  3602 
       
  3603     //M
       
  3604     "margin"                        : "<margin-width>{1,4}",
       
  3605     "margin-bottom"                 : "<margin-width>",
       
  3606     "margin-left"                   : "<margin-width>",
       
  3607     "margin-right"                  : "<margin-width>",
       
  3608     "margin-top"                    : "<margin-width>",
       
  3609     "mark"                          : 1,
       
  3610     "mark-after"                    : 1,
       
  3611     "mark-before"                   : 1,
       
  3612     "marker"                        : 1,
       
  3613     "marker-end"                    : 1,
       
  3614     "marker-mid"                    : 1,
       
  3615     "marker-start"                  : 1,
       
  3616     "marks"                         : 1,
       
  3617     "marquee-direction"             : 1,
       
  3618     "marquee-play-count"            : 1,
       
  3619     "marquee-speed"                 : 1,
       
  3620     "marquee-style"                 : 1,
       
  3621     "mask"                          : 1,
       
  3622     "max-height"                    : "<length> | <percentage> | <content-sizing> | none",
       
  3623     "max-width"                     : "<length> | <percentage> | <content-sizing> | none",
       
  3624     "min-height"                    : "<length> | <percentage> | <content-sizing> | contain-floats | -moz-contain-floats | -webkit-contain-floats",
       
  3625     "min-width"                     : "<length> | <percentage> | <content-sizing> | contain-floats | -moz-contain-floats | -webkit-contain-floats",
       
  3626     "move-to"                       : 1,
       
  3627 
       
  3628     //N
       
  3629     "nav-down"                      : 1,
       
  3630     "nav-index"                     : 1,
       
  3631     "nav-left"                      : 1,
       
  3632     "nav-right"                     : 1,
       
  3633     "nav-up"                        : 1,
       
  3634 
       
  3635     //O
       
  3636     "object-fit"                    : "fill | contain | cover | none | scale-down",
       
  3637     "object-position"               : "<position>",
       
  3638     "opacity"                       : "<opacity-value>",
       
  3639     "order"                         : "<integer>",
       
  3640     "-webkit-order"                 : "<integer>",
       
  3641     "orphans"                       : "<integer>",
       
  3642     "outline"                       : 1,
       
  3643     "outline-color"                 : "<color> | invert",
       
  3644     "outline-offset"                : 1,
       
  3645     "outline-style"                 : "<border-style>",
       
  3646     "outline-width"                 : "<border-width>",
       
  3647     "overflow"                      : "visible | hidden | scroll | auto",
       
  3648     "overflow-style"                : 1,
       
  3649     "overflow-wrap"                 : "normal | break-word",
       
  3650     "overflow-x"                    : 1,
       
  3651     "overflow-y"                    : 1,
       
  3652 
       
  3653     //P
       
  3654     "padding"                       : "<padding-width>{1,4}",
       
  3655     "padding-bottom"                : "<padding-width>",
       
  3656     "padding-left"                  : "<padding-width>",
       
  3657     "padding-right"                 : "<padding-width>",
       
  3658     "padding-top"                   : "<padding-width>",
       
  3659     "page"                          : 1,
       
  3660     "page-break-after"              : "auto | always | avoid | left | right",
       
  3661     "page-break-before"             : "auto | always | avoid | left | right",
       
  3662     "page-break-inside"             : "auto | avoid",
       
  3663     "page-policy"                   : 1,
       
  3664     "pause"                         : 1,
       
  3665     "pause-after"                   : 1,
       
  3666     "pause-before"                  : 1,
       
  3667     "perspective"                   : 1,
       
  3668     "perspective-origin"            : 1,
       
  3669     "phonemes"                      : 1,
       
  3670     "pitch"                         : 1,
       
  3671     "pitch-range"                   : 1,
       
  3672     "play-during"                   : 1,
       
  3673     "pointer-events"                : "auto | none | visiblePainted | visibleFill | visibleStroke | visible | painted | fill | stroke | all",
       
  3674     "position"                      : "static | relative | absolute | fixed",
       
  3675     "presentation-level"            : 1,
       
  3676     "punctuation-trim"              : 1,
       
  3677 
       
  3678     //Q
       
  3679     "quotes"                        : 1,
       
  3680 
       
  3681     //R
       
  3682     "rendering-intent"              : 1,
       
  3683     "resize"                        : 1,
       
  3684     "rest"                          : 1,
       
  3685     "rest-after"                    : 1,
       
  3686     "rest-before"                   : 1,
       
  3687     "richness"                      : 1,
       
  3688     "right"                         : "<margin-width>",
       
  3689     "rotation"                      : 1,
       
  3690     "rotation-point"                : 1,
       
  3691     "ruby-align"                    : 1,
       
  3692     "ruby-overhang"                 : 1,
       
  3693     "ruby-position"                 : 1,
       
  3694     "ruby-span"                     : 1,
       
  3695 
       
  3696     //S
       
  3697     "shape-rendering"               : "auto | optimizeSpeed | crispEdges | geometricPrecision",
       
  3698     "size"                          : 1,
       
  3699     "speak"                         : "normal | none | spell-out",
       
  3700     "speak-header"                  : "once | always",
       
  3701     "speak-numeral"                 : "digits | continuous",
       
  3702     "speak-punctuation"             : "code | none",
       
  3703     "speech-rate"                   : 1,
       
  3704     "src"                           : 1,
       
  3705     "stop-color"                    : 1,
       
  3706     "stop-opacity"                  : "<opacity-value>",
       
  3707     "stress"                        : 1,
       
  3708     "string-set"                    : 1,
       
  3709     "stroke"                        : "<paint>",
       
  3710     "stroke-dasharray"              : "none | <dasharray>",
       
  3711     "stroke-dashoffset"             : "<percentage> | <length>",
       
  3712     "stroke-linecap"                : "butt | round | square",
       
  3713     "stroke-linejoin"               : "miter | round | bevel",
       
  3714     "stroke-miterlimit"             : "<miterlimit>",
       
  3715     "stroke-opacity"                : "<opacity-value>",
       
  3716     "stroke-width"                  : "<percentage> | <length>",
       
  3717 
       
  3718     "table-layout"                  : "auto | fixed",
       
  3719     "tab-size"                      : "<integer> | <length>",
       
  3720     "target"                        : 1,
       
  3721     "target-name"                   : 1,
       
  3722     "target-new"                    : 1,
       
  3723     "target-position"               : 1,
       
  3724     "text-align"                    : "left | right | center | justify | match-parent | start | end",
       
  3725     "text-align-last"               : 1,
       
  3726     "text-anchor"                   : "start | middle | end",
       
  3727     "text-decoration"               : "<text-decoration-line> || <text-decoration-style> || <text-decoration-color>",
       
  3728     "text-decoration-color"         : "<text-decoration-color>",
       
  3729     "text-decoration-line"          : "<text-decoration-line>",
       
  3730     "text-decoration-style"         : "<text-decoration-style>",
       
  3731     "text-emphasis"                 : 1,
       
  3732     "text-height"                   : 1,
       
  3733     "text-indent"                   : "<length> | <percentage>",
       
  3734     "text-justify"                  : "auto | none | inter-word | inter-ideograph | inter-cluster | distribute | kashida",
       
  3735     "text-outline"                  : 1,
       
  3736     "text-overflow"                 : 1,
       
  3737     "text-rendering"                : "auto | optimizeSpeed | optimizeLegibility | geometricPrecision",
       
  3738     "text-shadow"                   : 1,
       
  3739     "text-transform"                : "capitalize | uppercase | lowercase | none",
       
  3740     "text-wrap"                     : "normal | none | avoid",
       
  3741     "top"                           : "<margin-width>",
       
  3742     "-ms-touch-action"              : "auto | none | pan-x | pan-y | pan-left | pan-right | pan-up | pan-down | manipulation",
       
  3743     "touch-action"                  : "auto | none | pan-x | pan-y | pan-left | pan-right | pan-up | pan-down | manipulation",
       
  3744     "transform"                     : 1,
       
  3745     "transform-origin"              : 1,
       
  3746     "transform-style"               : 1,
       
  3747     "transition"                    : 1,
       
  3748     "transition-delay"              : 1,
       
  3749     "transition-duration"           : 1,
       
  3750     "transition-property"           : 1,
       
  3751     "transition-timing-function"    : 1,
       
  3752 
       
  3753     //U
       
  3754     "unicode-bidi"                  : "normal | embed | isolate | bidi-override | isolate-override | plaintext",
       
  3755     "user-modify"                   : "read-only | read-write | write-only",
       
  3756     "user-select"                   : "none | text | toggle | element | elements | all",
       
  3757 
       
  3758     //V
       
  3759     "vertical-align"                : "auto | use-script | baseline | sub | super | top | text-top | central | middle | bottom | text-bottom | <percentage> | <length>",
       
  3760     "visibility"                    : "visible | hidden | collapse",
       
  3761     "voice-balance"                 : 1,
       
  3762     "voice-duration"                : 1,
       
  3763     "voice-family"                  : 1,
       
  3764     "voice-pitch"                   : 1,
       
  3765     "voice-pitch-range"             : 1,
       
  3766     "voice-rate"                    : 1,
       
  3767     "voice-stress"                  : 1,
       
  3768     "voice-volume"                  : 1,
       
  3769     "volume"                        : 1,
       
  3770 
       
  3771     //W
       
  3772     "white-space"                   : "normal | pre | nowrap | pre-wrap | pre-line | -pre-wrap | -o-pre-wrap | -moz-pre-wrap | -hp-pre-wrap",   // https://perishablepress.com/wrapping-content/
       
  3773     "white-space-collapse"          : 1,
       
  3774     "widows"                        : "<integer>",
       
  3775     "width"                         : "<length> | <percentage> | <content-sizing> | auto",
       
  3776     "will-change"                   : "<will-change>",
       
  3777     "word-break"                    : "normal | keep-all | break-all",
       
  3778     "word-spacing"                  : "<length> | normal",
       
  3779     "word-wrap"                     : "normal | break-word",
       
  3780     "writing-mode"                  : "horizontal-tb | vertical-rl | vertical-lr | lr-tb | rl-tb | tb-rl | bt-rl | tb-lr | bt-lr | lr-bt | rl-bt | lr | rl | tb",
       
  3781 
       
  3782     //Z
       
  3783     "z-index"                       : "<integer> | auto",
       
  3784     "zoom"                          : "<number> | <percentage> | normal"
       
  3785 };
       
  3786 
       
  3787 },{}],8:[function(require,module,exports){
       
  3788 "use strict";
       
  3789 
       
  3790 module.exports = PropertyName;
       
  3791 
       
  3792 var SyntaxUnit = require("../util/SyntaxUnit");
       
  3793 
       
  3794 var Parser = require("./Parser");
       
  3795 
       
  3796 /**
       
  3797  * Represents a selector combinator (whitespace, +, >).
       
  3798  * @namespace parserlib.css
       
  3799  * @class PropertyName
       
  3800  * @extends parserlib.util.SyntaxUnit
       
  3801  * @constructor
       
  3802  * @param {String} text The text representation of the unit.
       
  3803  * @param {String} hack The type of IE hack applied ("*", "_", or null).
       
  3804  * @param {int} line The line of text on which the unit resides.
       
  3805  * @param {int} col The column of text on which the unit resides.
       
  3806  */
       
  3807 function PropertyName(text, hack, line, col) {
       
  3808 
       
  3809     SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_NAME_TYPE);
       
  3810 
       
  3811     /**
       
  3812      * The type of IE hack applied ("*", "_", or null).
       
  3813      * @type String
       
  3814      * @property hack
       
  3815      */
       
  3816     this.hack = hack;
       
  3817 
       
  3818 }
       
  3819 
       
  3820 PropertyName.prototype = new SyntaxUnit();
       
  3821 PropertyName.prototype.constructor = PropertyName;
       
  3822 PropertyName.prototype.toString = function() {
       
  3823     return (this.hack ? this.hack : "") + this.text;
       
  3824 };
       
  3825 
       
  3826 },{"../util/SyntaxUnit":26,"./Parser":6}],9:[function(require,module,exports){
       
  3827 "use strict";
       
  3828 
       
  3829 module.exports = PropertyValue;
       
  3830 
       
  3831 var SyntaxUnit = require("../util/SyntaxUnit");
       
  3832 
       
  3833 var Parser = require("./Parser");
       
  3834 
       
  3835 /**
       
  3836  * Represents a single part of a CSS property value, meaning that it represents
       
  3837  * just everything single part between ":" and ";". If there are multiple values
       
  3838  * separated by commas, this type represents just one of the values.
       
  3839  * @param {String[]} parts An array of value parts making up this value.
       
  3840  * @param {int} line The line of text on which the unit resides.
       
  3841  * @param {int} col The column of text on which the unit resides.
       
  3842  * @namespace parserlib.css
       
  3843  * @class PropertyValue
       
  3844  * @extends parserlib.util.SyntaxUnit
       
  3845  * @constructor
       
  3846  */
       
  3847 function PropertyValue(parts, line, col) {
       
  3848 
       
  3849     SyntaxUnit.call(this, parts.join(" "), line, col, Parser.PROPERTY_VALUE_TYPE);
       
  3850 
       
  3851     /**
       
  3852      * The parts that make up the selector.
       
  3853      * @type Array
       
  3854      * @property parts
       
  3855      */
       
  3856     this.parts = parts;
       
  3857 
       
  3858 }
       
  3859 
       
  3860 PropertyValue.prototype = new SyntaxUnit();
       
  3861 PropertyValue.prototype.constructor = PropertyValue;
       
  3862 
       
  3863 
       
  3864 },{"../util/SyntaxUnit":26,"./Parser":6}],10:[function(require,module,exports){
       
  3865 "use strict";
       
  3866 
       
  3867 module.exports = PropertyValueIterator;
       
  3868 
       
  3869 /**
       
  3870  * A utility class that allows for easy iteration over the various parts of a
       
  3871  * property value.
       
  3872  * @param {parserlib.css.PropertyValue} value The property value to iterate over.
       
  3873  * @namespace parserlib.css
       
  3874  * @class PropertyValueIterator
       
  3875  * @constructor
       
  3876  */
       
  3877 function PropertyValueIterator(value) {
       
  3878 
       
  3879     /**
       
  3880      * Iterator value
       
  3881      * @type int
       
  3882      * @property _i
       
  3883      * @private
       
  3884      */
       
  3885     this._i = 0;
       
  3886 
       
  3887     /**
       
  3888      * The parts that make up the value.
       
  3889      * @type Array
       
  3890      * @property _parts
       
  3891      * @private
       
  3892      */
       
  3893     this._parts = value.parts;
       
  3894 
       
  3895     /**
       
  3896      * Keeps track of bookmarks along the way.
       
  3897      * @type Array
       
  3898      * @property _marks
       
  3899      * @private
       
  3900      */
       
  3901     this._marks = [];
       
  3902 
       
  3903     /**
       
  3904      * Holds the original property value.
       
  3905      * @type parserlib.css.PropertyValue
       
  3906      * @property value
       
  3907      */
       
  3908     this.value = value;
       
  3909 
       
  3910 }
       
  3911 
       
  3912 /**
       
  3913  * Returns the total number of parts in the value.
       
  3914  * @return {int} The total number of parts in the value.
       
  3915  * @method count
       
  3916  */
       
  3917 PropertyValueIterator.prototype.count = function() {
       
  3918     return this._parts.length;
       
  3919 };
       
  3920 
       
  3921 /**
       
  3922  * Indicates if the iterator is positioned at the first item.
       
  3923  * @return {Boolean} True if positioned at first item, false if not.
       
  3924  * @method isFirst
       
  3925  */
       
  3926 PropertyValueIterator.prototype.isFirst = function() {
       
  3927     return this._i === 0;
       
  3928 };
       
  3929 
       
  3930 /**
       
  3931  * Indicates if there are more parts of the property value.
       
  3932  * @return {Boolean} True if there are more parts, false if not.
       
  3933  * @method hasNext
       
  3934  */
       
  3935 PropertyValueIterator.prototype.hasNext = function() {
       
  3936     return this._i < this._parts.length;
       
  3937 };
       
  3938 
       
  3939 /**
       
  3940  * Marks the current spot in the iteration so it can be restored to
       
  3941  * later on.
       
  3942  * @return {void}
       
  3943  * @method mark
       
  3944  */
       
  3945 PropertyValueIterator.prototype.mark = function() {
       
  3946     this._marks.push(this._i);
       
  3947 };
       
  3948 
       
  3949 /**
       
  3950  * Returns the next part of the property value or null if there is no next
       
  3951  * part. Does not move the internal counter forward.
       
  3952  * @return {parserlib.css.PropertyValuePart} The next part of the property value or null if there is no next
       
  3953  * part.
       
  3954  * @method peek
       
  3955  */
       
  3956 PropertyValueIterator.prototype.peek = function(count) {
       
  3957     return this.hasNext() ? this._parts[this._i + (count || 0)] : null;
       
  3958 };
       
  3959 
       
  3960 /**
       
  3961  * Returns the next part of the property value or null if there is no next
       
  3962  * part.
       
  3963  * @return {parserlib.css.PropertyValuePart} The next part of the property value or null if there is no next
       
  3964  * part.
       
  3965  * @method next
       
  3966  */
       
  3967 PropertyValueIterator.prototype.next = function() {
       
  3968     return this.hasNext() ? this._parts[this._i++] : null;
       
  3969 };
       
  3970 
       
  3971 /**
       
  3972  * Returns the previous part of the property value or null if there is no
       
  3973  * previous part.
       
  3974  * @return {parserlib.css.PropertyValuePart} The previous part of the
       
  3975  * property value or null if there is no previous part.
       
  3976  * @method previous
       
  3977  */
       
  3978 PropertyValueIterator.prototype.previous = function() {
       
  3979     return this._i > 0 ? this._parts[--this._i] : null;
       
  3980 };
       
  3981 
       
  3982 /**
       
  3983  * Restores the last saved bookmark.
       
  3984  * @return {void}
       
  3985  * @method restore
       
  3986  */
       
  3987 PropertyValueIterator.prototype.restore = function() {
       
  3988     if (this._marks.length) {
       
  3989         this._i = this._marks.pop();
       
  3990     }
       
  3991 };
       
  3992 
       
  3993 /**
       
  3994  * Drops the last saved bookmark.
       
  3995  * @return {void}
       
  3996  * @method drop
       
  3997  */
       
  3998 PropertyValueIterator.prototype.drop = function() {
       
  3999     this._marks.pop();
       
  4000 };
       
  4001 
       
  4002 },{}],11:[function(require,module,exports){
       
  4003 "use strict";
       
  4004 
       
  4005 module.exports = PropertyValuePart;
       
  4006 
       
  4007 var SyntaxUnit = require("../util/SyntaxUnit");
       
  4008 
       
  4009 var Colors = require("./Colors");
       
  4010 var Parser = require("./Parser");
       
  4011 var Tokens = require("./Tokens");
       
  4012 
       
  4013 /**
       
  4014  * Represents a single part of a CSS property value, meaning that it represents
       
  4015  * just one part of the data between ":" and ";".
       
  4016  * @param {String} text The text representation of the unit.
       
  4017  * @param {int} line The line of text on which the unit resides.
       
  4018  * @param {int} col The column of text on which the unit resides.
       
  4019  * @namespace parserlib.css
       
  4020  * @class PropertyValuePart
       
  4021  * @extends parserlib.util.SyntaxUnit
       
  4022  * @constructor
       
  4023  */
       
  4024 function PropertyValuePart(text, line, col, optionalHint) {
       
  4025     var hint = optionalHint || {};
       
  4026 
       
  4027     SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_VALUE_PART_TYPE);
       
  4028 
       
  4029     /**
       
  4030      * Indicates the type of value unit.
       
  4031      * @type String
       
  4032      * @property type
       
  4033      */
       
  4034     this.type = "unknown";
       
  4035 
       
  4036     //figure out what type of data it is
       
  4037 
       
  4038     var temp;
       
  4039 
       
  4040     //it is a measurement?
       
  4041     if (/^([+\-]?[\d\.]+)([a-z]+)$/i.test(text)) {  //dimension
       
  4042         this.type = "dimension";
       
  4043         this.value = +RegExp.$1;
       
  4044         this.units = RegExp.$2;
       
  4045 
       
  4046         //try to narrow down
       
  4047         switch (this.units.toLowerCase()) {
       
  4048 
       
  4049             case "em":
       
  4050             case "rem":
       
  4051             case "ex":
       
  4052             case "px":
       
  4053             case "cm":
       
  4054             case "mm":
       
  4055             case "in":
       
  4056             case "pt":
       
  4057             case "pc":
       
  4058             case "ch":
       
  4059             case "vh":
       
  4060             case "vw":
       
  4061             case "vmax":
       
  4062             case "vmin":
       
  4063                 this.type = "length";
       
  4064                 break;
       
  4065 
       
  4066             case "fr":
       
  4067                 this.type = "grid";
       
  4068                 break;
       
  4069 
       
  4070             case "deg":
       
  4071             case "rad":
       
  4072             case "grad":
       
  4073             case "turn":
       
  4074                 this.type = "angle";
       
  4075                 break;
       
  4076 
       
  4077             case "ms":
       
  4078             case "s":
       
  4079                 this.type = "time";
       
  4080                 break;
       
  4081 
       
  4082             case "hz":
       
  4083             case "khz":
       
  4084                 this.type = "frequency";
       
  4085                 break;
       
  4086 
       
  4087             case "dpi":
       
  4088             case "dpcm":
       
  4089                 this.type = "resolution";
       
  4090                 break;
       
  4091 
       
  4092             //default
       
  4093 
       
  4094         }
       
  4095 
       
  4096     } else if (/^([+\-]?[\d\.]+)%$/i.test(text)) {  //percentage
       
  4097         this.type = "percentage";
       
  4098         this.value = +RegExp.$1;
       
  4099     } else if (/^([+\-]?\d+)$/i.test(text)) {  //integer
       
  4100         this.type = "integer";
       
  4101         this.value = +RegExp.$1;
       
  4102     } else if (/^([+\-]?[\d\.]+)$/i.test(text)) {  //number
       
  4103         this.type = "number";
       
  4104         this.value = +RegExp.$1;
       
  4105 
       
  4106     } else if (/^#([a-f0-9]{3,6})/i.test(text)) {  //hexcolor
       
  4107         this.type = "color";
       
  4108         temp = RegExp.$1;
       
  4109         if (temp.length === 3) {
       
  4110             this.red    = parseInt(temp.charAt(0)+temp.charAt(0), 16);
       
  4111             this.green  = parseInt(temp.charAt(1)+temp.charAt(1), 16);
       
  4112             this.blue   = parseInt(temp.charAt(2)+temp.charAt(2), 16);
       
  4113         } else {
       
  4114             this.red    = parseInt(temp.substring(0, 2), 16);
       
  4115             this.green  = parseInt(temp.substring(2, 4), 16);
       
  4116             this.blue   = parseInt(temp.substring(4, 6), 16);
       
  4117         }
       
  4118     } else if (/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i.test(text)) { //rgb() color with absolute numbers
       
  4119         this.type   = "color";
       
  4120         this.red    = +RegExp.$1;
       
  4121         this.green  = +RegExp.$2;
       
  4122         this.blue   = +RegExp.$3;
       
  4123     } else if (/^rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)) { //rgb() color with percentages
       
  4124         this.type   = "color";
       
  4125         this.red    = +RegExp.$1 * 255 / 100;
       
  4126         this.green  = +RegExp.$2 * 255 / 100;
       
  4127         this.blue   = +RegExp.$3 * 255 / 100;
       
  4128     } else if (/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //rgba() color with absolute numbers
       
  4129         this.type   = "color";
       
  4130         this.red    = +RegExp.$1;
       
  4131         this.green  = +RegExp.$2;
       
  4132         this.blue   = +RegExp.$3;
       
  4133         this.alpha  = +RegExp.$4;
       
  4134     } else if (/^rgba\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //rgba() color with percentages
       
  4135         this.type   = "color";
       
  4136         this.red    = +RegExp.$1 * 255 / 100;
       
  4137         this.green  = +RegExp.$2 * 255 / 100;
       
  4138         this.blue   = +RegExp.$3 * 255 / 100;
       
  4139         this.alpha  = +RegExp.$4;
       
  4140     } else if (/^hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)) { //hsl()
       
  4141         this.type   = "color";
       
  4142         this.hue    = +RegExp.$1;
       
  4143         this.saturation = +RegExp.$2 / 100;
       
  4144         this.lightness  = +RegExp.$3 / 100;
       
  4145     } else if (/^hsla\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //hsla() color with percentages
       
  4146         this.type   = "color";
       
  4147         this.hue    = +RegExp.$1;
       
  4148         this.saturation = +RegExp.$2 / 100;
       
  4149         this.lightness  = +RegExp.$3 / 100;
       
  4150         this.alpha  = +RegExp.$4;
       
  4151     } else if (/^url\(("([^\\"]|\\.)*")\)/i.test(text)) { //URI
       
  4152         // generated by TokenStream.readURI, so always double-quoted.
       
  4153         this.type   = "uri";
       
  4154         this.uri    = PropertyValuePart.parseString(RegExp.$1);
       
  4155     } else if (/^([^\(]+)\(/i.test(text)) {
       
  4156         this.type   = "function";
       
  4157         this.name   = RegExp.$1;
       
  4158         this.value  = text;
       
  4159     } else if (/^"([^\n\r\f\\"]|\\\r\n|\\[^\r0-9a-f]|\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)*"/i.test(text)) {    //double-quoted string
       
  4160         this.type   = "string";
       
  4161         this.value  = PropertyValuePart.parseString(text);
       
  4162     } else if (/^'([^\n\r\f\\']|\\\r\n|\\[^\r0-9a-f]|\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)*'/i.test(text)) {    //single-quoted string
       
  4163         this.type   = "string";
       
  4164         this.value  = PropertyValuePart.parseString(text);
       
  4165     } else if (Colors[text.toLowerCase()]) {  //named color
       
  4166         this.type   = "color";
       
  4167         temp        = Colors[text.toLowerCase()].substring(1);
       
  4168         this.red    = parseInt(temp.substring(0, 2), 16);
       
  4169         this.green  = parseInt(temp.substring(2, 4), 16);
       
  4170         this.blue   = parseInt(temp.substring(4, 6), 16);
       
  4171     } else if (/^[,\/]$/.test(text)) {
       
  4172         this.type   = "operator";
       
  4173         this.value  = text;
       
  4174     } else if (/^-?[a-z_\u00A0-\uFFFF][a-z0-9\-_\u00A0-\uFFFF]*$/i.test(text)) {
       
  4175         this.type   = "identifier";
       
  4176         this.value  = text;
       
  4177     }
       
  4178 
       
  4179     // There can be ambiguity with escape sequences in identifiers, as
       
  4180     // well as with "color" parts which are also "identifiers", so record
       
  4181     // an explicit hint when the token generating this PropertyValuePart
       
  4182     // was an identifier.
       
  4183     this.wasIdent = Boolean(hint.ident);
       
  4184 
       
  4185 }
       
  4186 
       
  4187 PropertyValuePart.prototype = new SyntaxUnit();
       
  4188 PropertyValuePart.prototype.constructor = PropertyValuePart;
       
  4189 
       
  4190 /**
       
  4191  * Helper method to parse a CSS string.
       
  4192  */
       
  4193 PropertyValuePart.parseString = function(str) {
       
  4194     str = str.slice(1, -1); // Strip surrounding single/double quotes
       
  4195     var replacer = function(match, esc) {
       
  4196         if (/^(\n|\r\n|\r|\f)$/.test(esc)) {
       
  4197             return "";
       
  4198         }
       
  4199         var m = /^[0-9a-f]{1,6}/i.exec(esc);
       
  4200         if (m) {
       
  4201             var codePoint = parseInt(m[0], 16);
       
  4202             if (String.fromCodePoint) {
       
  4203                 return String.fromCodePoint(codePoint);
       
  4204             } else {
       
  4205                 // XXX No support for surrogates on old JavaScript engines.
       
  4206                 return String.fromCharCode(codePoint);
       
  4207             }
       
  4208         }
       
  4209         return esc;
       
  4210     };
       
  4211     return str.replace(/\\(\r\n|[^\r0-9a-f]|[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)/ig,
       
  4212                        replacer);
       
  4213 };
       
  4214 
       
  4215 /**
       
  4216  * Helper method to serialize a CSS string.
       
  4217  */
       
  4218 PropertyValuePart.serializeString = function(value) {
       
  4219     var replacer = function(match, c) {
       
  4220         if (c === "\"") {
       
  4221             return "\\" + c;
       
  4222         }
       
  4223         var cp = String.codePointAt ? String.codePointAt(0) :
       
  4224             // We only escape non-surrogate chars, so using charCodeAt
       
  4225             // is harmless here.
       
  4226             String.charCodeAt(0);
       
  4227         return "\\" + cp.toString(16) + " ";
       
  4228     };
       
  4229     return "\"" + value.replace(/["\r\n\f]/g, replacer) + "\"";
       
  4230 };
       
  4231 
       
  4232 /**
       
  4233  * Create a new syntax unit based solely on the given token.
       
  4234  * Convenience method for creating a new syntax unit when
       
  4235  * it represents a single token instead of multiple.
       
  4236  * @param {Object} token The token object to represent.
       
  4237  * @return {parserlib.css.PropertyValuePart} The object representing the token.
       
  4238  * @static
       
  4239  * @method fromToken
       
  4240  */
       
  4241 PropertyValuePart.fromToken = function(token) {
       
  4242     var part = new PropertyValuePart(token.value, token.startLine, token.startCol, {
       
  4243         // Tokens can have escaped characters that would fool the type
       
  4244         // identification in the PropertyValuePart constructor, so pass
       
  4245         // in a hint if this was an identifier.
       
  4246         ident: token.type === Tokens.IDENT
       
  4247     });
       
  4248     return part;
       
  4249 };
       
  4250 
       
  4251 },{"../util/SyntaxUnit":26,"./Colors":1,"./Parser":6,"./Tokens":18}],12:[function(require,module,exports){
       
  4252 "use strict";
       
  4253 
       
  4254 var Pseudos = module.exports = {
       
  4255     __proto__:       null,
       
  4256     ":first-letter": 1,
       
  4257     ":first-line":   1,
       
  4258     ":before":       1,
       
  4259     ":after":        1
       
  4260 };
       
  4261 
       
  4262 Pseudos.ELEMENT = 1;
       
  4263 Pseudos.CLASS = 2;
       
  4264 
       
  4265 Pseudos.isElement = function(pseudo) {
       
  4266     return pseudo.indexOf("::") === 0 || Pseudos[pseudo.toLowerCase()] === Pseudos.ELEMENT;
       
  4267 };
       
  4268 
       
  4269 },{}],13:[function(require,module,exports){
       
  4270 "use strict";
       
  4271 
       
  4272 module.exports = Selector;
       
  4273 
       
  4274 var SyntaxUnit = require("../util/SyntaxUnit");
       
  4275 
       
  4276 var Parser = require("./Parser");
       
  4277 var Specificity = require("./Specificity");
       
  4278 
       
  4279 /**
       
  4280  * Represents an entire single selector, including all parts but not
       
  4281  * including multiple selectors (those separated by commas).
       
  4282  * @namespace parserlib.css
       
  4283  * @class Selector
       
  4284  * @extends parserlib.util.SyntaxUnit
       
  4285  * @constructor
       
  4286  * @param {Array} parts Array of selectors parts making up this selector.
       
  4287  * @param {int} line The line of text on which the unit resides.
       
  4288  * @param {int} col The column of text on which the unit resides.
       
  4289  */
       
  4290 function Selector(parts, line, col) {
       
  4291 
       
  4292     SyntaxUnit.call(this, parts.join(" "), line, col, Parser.SELECTOR_TYPE);
       
  4293 
       
  4294     /**
       
  4295      * The parts that make up the selector.
       
  4296      * @type Array
       
  4297      * @property parts
       
  4298      */
       
  4299     this.parts = parts;
       
  4300 
       
  4301     /**
       
  4302      * The specificity of the selector.
       
  4303      * @type parserlib.css.Specificity
       
  4304      * @property specificity
       
  4305      */
       
  4306     this.specificity = Specificity.calculate(this);
       
  4307 
       
  4308 }
       
  4309 
       
  4310 Selector.prototype = new SyntaxUnit();
       
  4311 Selector.prototype.constructor = Selector;
       
  4312 
       
  4313 
       
  4314 },{"../util/SyntaxUnit":26,"./Parser":6,"./Specificity":16}],14:[function(require,module,exports){
       
  4315 "use strict";
       
  4316 
       
  4317 module.exports = SelectorPart;
       
  4318 
       
  4319 var SyntaxUnit = require("../util/SyntaxUnit");
       
  4320 
       
  4321 var Parser = require("./Parser");
       
  4322 
       
  4323 /**
       
  4324  * Represents a single part of a selector string, meaning a single set of
       
  4325  * element name and modifiers. This does not include combinators such as
       
  4326  * spaces, +, >, etc.
       
  4327  * @namespace parserlib.css
       
  4328  * @class SelectorPart
       
  4329  * @extends parserlib.util.SyntaxUnit
       
  4330  * @constructor
       
  4331  * @param {String} elementName The element name in the selector or null
       
  4332  *      if there is no element name.
       
  4333  * @param {Array} modifiers Array of individual modifiers for the element.
       
  4334  *      May be empty if there are none.
       
  4335  * @param {String} text The text representation of the unit.
       
  4336  * @param {int} line The line of text on which the unit resides.
       
  4337  * @param {int} col The column of text on which the unit resides.
       
  4338  */
       
  4339 function SelectorPart(elementName, modifiers, text, line, col) {
       
  4340 
       
  4341     SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_PART_TYPE);
       
  4342 
       
  4343     /**
       
  4344      * The tag name of the element to which this part
       
  4345      * of the selector affects.
       
  4346      * @type String
       
  4347      * @property elementName
       
  4348      */
       
  4349     this.elementName = elementName;
       
  4350 
       
  4351     /**
       
  4352      * The parts that come after the element name, such as class names, IDs,
       
  4353      * pseudo classes/elements, etc.
       
  4354      * @type Array
       
  4355      * @property modifiers
       
  4356      */
       
  4357     this.modifiers = modifiers;
       
  4358 
       
  4359 }
       
  4360 
       
  4361 SelectorPart.prototype = new SyntaxUnit();
       
  4362 SelectorPart.prototype.constructor = SelectorPart;
       
  4363 
       
  4364 
       
  4365 },{"../util/SyntaxUnit":26,"./Parser":6}],15:[function(require,module,exports){
       
  4366 "use strict";
       
  4367 
       
  4368 module.exports = SelectorSubPart;
       
  4369 
       
  4370 var SyntaxUnit = require("../util/SyntaxUnit");
       
  4371 
       
  4372 var Parser = require("./Parser");
       
  4373 
       
  4374 /**
       
  4375  * Represents a selector modifier string, meaning a class name, element name,
       
  4376  * element ID, pseudo rule, etc.
       
  4377  * @namespace parserlib.css
       
  4378  * @class SelectorSubPart
       
  4379  * @extends parserlib.util.SyntaxUnit
       
  4380  * @constructor
       
  4381  * @param {String} text The text representation of the unit.
       
  4382  * @param {String} type The type of selector modifier.
       
  4383  * @param {int} line The line of text on which the unit resides.
       
  4384  * @param {int} col The column of text on which the unit resides.
       
  4385  */
       
  4386 function SelectorSubPart(text, type, line, col) {
       
  4387 
       
  4388     SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_SUB_PART_TYPE);
       
  4389 
       
  4390     /**
       
  4391      * The type of modifier.
       
  4392      * @type String
       
  4393      * @property type
       
  4394      */
       
  4395     this.type = type;
       
  4396 
       
  4397     /**
       
  4398      * Some subparts have arguments, this represents them.
       
  4399      * @type Array
       
  4400      * @property args
       
  4401      */
       
  4402     this.args = [];
       
  4403 
       
  4404 }
       
  4405 
       
  4406 SelectorSubPart.prototype = new SyntaxUnit();
       
  4407 SelectorSubPart.prototype.constructor = SelectorSubPart;
       
  4408 
       
  4409 
       
  4410 },{"../util/SyntaxUnit":26,"./Parser":6}],16:[function(require,module,exports){
       
  4411 "use strict";
       
  4412 
       
  4413 module.exports = Specificity;
       
  4414 
       
  4415 var Pseudos = require("./Pseudos");
       
  4416 var SelectorPart = require("./SelectorPart");
       
  4417 
       
  4418 /**
       
  4419  * Represents a selector's specificity.
       
  4420  * @namespace parserlib.css
       
  4421  * @class Specificity
       
  4422  * @constructor
       
  4423  * @param {int} a Should be 1 for inline styles, zero for stylesheet styles
       
  4424  * @param {int} b Number of ID selectors
       
  4425  * @param {int} c Number of classes and pseudo classes
       
  4426  * @param {int} d Number of element names and pseudo elements
       
  4427  */
       
  4428 function Specificity(a, b, c, d) {
       
  4429     this.a = a;
       
  4430     this.b = b;
       
  4431     this.c = c;
       
  4432     this.d = d;
       
  4433 }
       
  4434 
       
  4435 Specificity.prototype = {
       
  4436     constructor: Specificity,
       
  4437 
       
  4438     /**
       
  4439      * Compare this specificity to another.
       
  4440      * @param {Specificity} other The other specificity to compare to.
       
  4441      * @return {int} -1 if the other specificity is larger, 1 if smaller, 0 if equal.
       
  4442      * @method compare
       
  4443      */
       
  4444     compare: function(other) {
       
  4445         var comps = ["a", "b", "c", "d"],
       
  4446             i, len;
       
  4447 
       
  4448         for (i=0, len=comps.length; i < len; i++) {
       
  4449             if (this[comps[i]] < other[comps[i]]) {
       
  4450                 return -1;
       
  4451             } else if (this[comps[i]] > other[comps[i]]) {
       
  4452                 return 1;
       
  4453             }
       
  4454         }
       
  4455 
       
  4456         return 0;
       
  4457     },
       
  4458 
       
  4459     /**
       
  4460      * Creates a numeric value for the specificity.
       
  4461      * @return {int} The numeric value for the specificity.
       
  4462      * @method valueOf
       
  4463      */
       
  4464     valueOf: function() {
       
  4465         return (this.a * 1000) + (this.b * 100) + (this.c * 10) + this.d;
       
  4466     },
       
  4467 
       
  4468     /**
       
  4469      * Returns a string representation for specificity.
       
  4470      * @return {String} The string representation of specificity.
       
  4471      * @method toString
       
  4472      */
       
  4473     toString: function() {
       
  4474         return this.a + "," + this.b + "," + this.c + "," + this.d;
       
  4475     }
       
  4476 
       
  4477 };
       
  4478 
       
  4479 /**
       
  4480  * Calculates the specificity of the given selector.
       
  4481  * @param {parserlib.css.Selector} The selector to calculate specificity for.
       
  4482  * @return {parserlib.css.Specificity} The specificity of the selector.
       
  4483  * @static
       
  4484  * @method calculate
       
  4485  */
       
  4486 Specificity.calculate = function(selector) {
       
  4487 
       
  4488     var i, len,
       
  4489         part,
       
  4490         b=0, c=0, d=0;
       
  4491 
       
  4492     function updateValues(part) {
       
  4493 
       
  4494         var i, j, len, num,
       
  4495             elementName = part.elementName ? part.elementName.text : "",
       
  4496             modifier;
       
  4497 
       
  4498         if (elementName && elementName.charAt(elementName.length-1) !== "*") {
       
  4499             d++;
       
  4500         }
       
  4501 
       
  4502         for (i=0, len=part.modifiers.length; i < len; i++) {
       
  4503             modifier = part.modifiers[i];
       
  4504             switch (modifier.type) {
       
  4505                 case "class":
       
  4506                 case "attribute":
       
  4507                     c++;
       
  4508                     break;
       
  4509 
       
  4510                 case "id":
       
  4511                     b++;
       
  4512                     break;
       
  4513 
       
  4514                 case "pseudo":
       
  4515                     if (Pseudos.isElement(modifier.text)) {
       
  4516                         d++;
       
  4517                     } else {
       
  4518                         c++;
       
  4519                     }
       
  4520                     break;
       
  4521 
       
  4522                 case "not":
       
  4523                     for (j=0, num=modifier.args.length; j < num; j++) {
       
  4524                         updateValues(modifier.args[j]);
       
  4525                     }
       
  4526             }
       
  4527         }
       
  4528     }
       
  4529 
       
  4530     for (i=0, len=selector.parts.length; i < len; i++) {
       
  4531         part = selector.parts[i];
       
  4532 
       
  4533         if (part instanceof SelectorPart) {
       
  4534             updateValues(part);
       
  4535         }
       
  4536     }
       
  4537 
       
  4538     return new Specificity(0, b, c, d);
       
  4539 };
       
  4540 
       
  4541 },{"./Pseudos":12,"./SelectorPart":14}],17:[function(require,module,exports){
       
  4542 "use strict";
       
  4543 
       
  4544 module.exports = TokenStream;
       
  4545 
       
  4546 var TokenStreamBase = require("../util/TokenStreamBase");
       
  4547 
       
  4548 var PropertyValuePart = require("./PropertyValuePart");
       
  4549 var Tokens = require("./Tokens");
       
  4550 
       
  4551 var h = /^[0-9a-fA-F]$/,
       
  4552     nonascii = /^[\u00A0-\uFFFF]$/,
       
  4553     nl = /\n|\r\n|\r|\f/,
       
  4554     whitespace = /\u0009|\u000a|\u000c|\u000d|\u0020/;
       
  4555 
       
  4556 //-----------------------------------------------------------------------------
       
  4557 // Helper functions
       
  4558 //-----------------------------------------------------------------------------
       
  4559 
       
  4560 
       
  4561 function isHexDigit(c) {
       
  4562     return c !== null && h.test(c);
       
  4563 }
       
  4564 
       
  4565 function isDigit(c) {
       
  4566     return c !== null && /\d/.test(c);
       
  4567 }
       
  4568 
       
  4569 function isWhitespace(c) {
       
  4570     return c !== null && whitespace.test(c);
       
  4571 }
       
  4572 
       
  4573 function isNewLine(c) {
       
  4574     return c !== null && nl.test(c);
       
  4575 }
       
  4576 
       
  4577 function isNameStart(c) {
       
  4578     return c !== null && /[a-z_\u00A0-\uFFFF\\]/i.test(c);
       
  4579 }
       
  4580 
       
  4581 function isNameChar(c) {
       
  4582     return c !== null && (isNameStart(c) || /[0-9\-\\]/.test(c));
       
  4583 }
       
  4584 
       
  4585 function isIdentStart(c) {
       
  4586     return c !== null && (isNameStart(c) || /\-\\/.test(c));
       
  4587 }
       
  4588 
       
  4589 function mix(receiver, supplier) {
       
  4590     for (var prop in supplier) {
       
  4591         if (Object.prototype.hasOwnProperty.call(supplier, prop)) {
       
  4592             receiver[prop] = supplier[prop];
       
  4593         }
       
  4594     }
       
  4595     return receiver;
       
  4596 }
       
  4597 
       
  4598 //-----------------------------------------------------------------------------
       
  4599 // CSS Token Stream
       
  4600 //-----------------------------------------------------------------------------
       
  4601 
       
  4602 
       
  4603 /**
       
  4604  * A token stream that produces CSS tokens.
       
  4605  * @param {String|Reader} input The source of text to tokenize.
       
  4606  * @constructor
       
  4607  * @class TokenStream
       
  4608  * @namespace parserlib.css
       
  4609  */
       
  4610 function TokenStream(input) {
       
  4611     TokenStreamBase.call(this, input, Tokens);
       
  4612 }
       
  4613 
       
  4614 TokenStream.prototype = mix(new TokenStreamBase(), {
       
  4615 
       
  4616     /**
       
  4617      * Overrides the TokenStreamBase method of the same name
       
  4618      * to produce CSS tokens.
       
  4619      * @return {Object} A token object representing the next token.
       
  4620      * @method _getToken
       
  4621      * @private
       
  4622      */
       
  4623     _getToken: function() {
       
  4624 
       
  4625         var c,
       
  4626             reader = this._reader,
       
  4627             token   = null,
       
  4628             startLine   = reader.getLine(),
       
  4629             startCol    = reader.getCol();
       
  4630 
       
  4631         c = reader.read();
       
  4632 
       
  4633 
       
  4634         while (c) {
       
  4635             switch (c) {
       
  4636 
       
  4637                 /*
       
  4638                  * Potential tokens:
       
  4639                  * - COMMENT
       
  4640                  * - SLASH
       
  4641                  * - CHAR
       
  4642                  */
       
  4643                 case "/":
       
  4644 
       
  4645                     if (reader.peek() === "*") {
       
  4646                         token = this.commentToken(c, startLine, startCol);
       
  4647                     } else {
       
  4648                         token = this.charToken(c, startLine, startCol);
       
  4649                     }
       
  4650                     break;
       
  4651 
       
  4652                 /*
       
  4653                  * Potential tokens:
       
  4654                  * - DASHMATCH
       
  4655                  * - INCLUDES
       
  4656                  * - PREFIXMATCH
       
  4657                  * - SUFFIXMATCH
       
  4658                  * - SUBSTRINGMATCH
       
  4659                  * - CHAR
       
  4660                  */
       
  4661                 case "|":
       
  4662                 case "~":
       
  4663                 case "^":
       
  4664                 case "$":
       
  4665                 case "*":
       
  4666                     if (reader.peek() === "=") {
       
  4667                         token = this.comparisonToken(c, startLine, startCol);
       
  4668                     } else {
       
  4669                         token = this.charToken(c, startLine, startCol);
       
  4670                     }
       
  4671                     break;
       
  4672 
       
  4673                 /*
       
  4674                  * Potential tokens:
       
  4675                  * - STRING
       
  4676                  * - INVALID
       
  4677                  */
       
  4678                 case "\"":
       
  4679                 case "'":
       
  4680                     token = this.stringToken(c, startLine, startCol);
       
  4681                     break;
       
  4682 
       
  4683                 /*
       
  4684                  * Potential tokens:
       
  4685                  * - HASH
       
  4686                  * - CHAR
       
  4687                  */
       
  4688                 case "#":
       
  4689                     if (isNameChar(reader.peek())) {
       
  4690                         token = this.hashToken(c, startLine, startCol);
       
  4691                     } else {
       
  4692                         token = this.charToken(c, startLine, startCol);
       
  4693                     }
       
  4694                     break;
       
  4695 
       
  4696                 /*
       
  4697                  * Potential tokens:
       
  4698                  * - DOT
       
  4699                  * - NUMBER
       
  4700                  * - DIMENSION
       
  4701                  * - PERCENTAGE
       
  4702                  */
       
  4703                 case ".":
       
  4704                     if (isDigit(reader.peek())) {
       
  4705                         token = this.numberToken(c, startLine, startCol);
       
  4706                     } else {
       
  4707                         token = this.charToken(c, startLine, startCol);
       
  4708                     }
       
  4709                     break;
       
  4710 
       
  4711                 /*
       
  4712                  * Potential tokens:
       
  4713                  * - CDC
       
  4714                  * - MINUS
       
  4715                  * - NUMBER
       
  4716                  * - DIMENSION
       
  4717                  * - PERCENTAGE
       
  4718                  */
       
  4719                 case "-":
       
  4720                     if (reader.peek() === "-") {  //could be closing HTML-style comment
       
  4721                         token = this.htmlCommentEndToken(c, startLine, startCol);
       
  4722                     } else if (isNameStart(reader.peek())) {
       
  4723                         token = this.identOrFunctionToken(c, startLine, startCol);
       
  4724                     } else {
       
  4725                         token = this.charToken(c, startLine, startCol);
       
  4726                     }
       
  4727                     break;
       
  4728 
       
  4729                 /*
       
  4730                  * Potential tokens:
       
  4731                  * - IMPORTANT_SYM
       
  4732                  * - CHAR
       
  4733                  */
       
  4734                 case "!":
       
  4735                     token = this.importantToken(c, startLine, startCol);
       
  4736                     break;
       
  4737 
       
  4738                 /*
       
  4739                  * Any at-keyword or CHAR
       
  4740                  */
       
  4741                 case "@":
       
  4742                     token = this.atRuleToken(c, startLine, startCol);
       
  4743                     break;
       
  4744 
       
  4745                 /*
       
  4746                  * Potential tokens:
       
  4747                  * - NOT
       
  4748                  * - CHAR
       
  4749                  */
       
  4750                 case ":":
       
  4751                     token = this.notToken(c, startLine, startCol);
       
  4752                     break;
       
  4753 
       
  4754                 /*
       
  4755                  * Potential tokens:
       
  4756                  * - CDO
       
  4757                  * - CHAR
       
  4758                  */
       
  4759                 case "<":
       
  4760                     token = this.htmlCommentStartToken(c, startLine, startCol);
       
  4761                     break;
       
  4762 
       
  4763                 /*
       
  4764                  * Potential tokens:
       
  4765                  * - IDENT
       
  4766                  * - CHAR
       
  4767                  */
       
  4768                 case "\\":
       
  4769                     if (/[^\r\n\f]/.test(reader.peek())) {
       
  4770                         token = this.identOrFunctionToken(this.readEscape(c, true), startLine, startCol);
       
  4771                     } else {
       
  4772                         token = this.charToken(c, startLine, startCol);
       
  4773                     }
       
  4774                     break;
       
  4775 
       
  4776                 /*
       
  4777                  * Potential tokens:
       
  4778                  * - UNICODE_RANGE
       
  4779                  * - URL
       
  4780                  * - CHAR
       
  4781                  */
       
  4782                 case "U":
       
  4783                 case "u":
       
  4784                     if (reader.peek() === "+") {
       
  4785                         token = this.unicodeRangeToken(c, startLine, startCol);
       
  4786                         break;
       
  4787                     }
       
  4788                     /* falls through */
       
  4789                 default:
       
  4790 
       
  4791                     /*
       
  4792                      * Potential tokens:
       
  4793                      * - NUMBER
       
  4794                      * - DIMENSION
       
  4795                      * - LENGTH
       
  4796                      * - FREQ
       
  4797                      * - TIME
       
  4798                      * - EMS
       
  4799                      * - EXS
       
  4800                      * - ANGLE
       
  4801                      */
       
  4802                     if (isDigit(c)) {
       
  4803                         token = this.numberToken(c, startLine, startCol);
       
  4804                     } else
       
  4805 
       
  4806                     /*
       
  4807                      * Potential tokens:
       
  4808                      * - S
       
  4809                      */
       
  4810                     if (isWhitespace(c)) {
       
  4811                         token = this.whitespaceToken(c, startLine, startCol);
       
  4812                     } else
       
  4813 
       
  4814                     /*
       
  4815                      * Potential tokens:
       
  4816                      * - IDENT
       
  4817                      */
       
  4818                     if (isIdentStart(c)) {
       
  4819                         token = this.identOrFunctionToken(c, startLine, startCol);
       
  4820                     } else {
       
  4821                        /*
       
  4822                         * Potential tokens:
       
  4823                         * - CHAR
       
  4824                         * - PLUS
       
  4825                         */
       
  4826                         token = this.charToken(c, startLine, startCol);
       
  4827                     }
       
  4828 
       
  4829             }
       
  4830 
       
  4831             //make sure this token is wanted
       
  4832             //TODO: check channel
       
  4833             break;
       
  4834         }
       
  4835 
       
  4836         if (!token && c === null) {
       
  4837             token = this.createToken(Tokens.EOF, null, startLine, startCol);
       
  4838         }
       
  4839 
       
  4840         return token;
       
  4841     },
       
  4842 
       
  4843     //-------------------------------------------------------------------------
       
  4844     // Methods to create tokens
       
  4845     //-------------------------------------------------------------------------
       
  4846 
       
  4847     /**
       
  4848      * Produces a token based on available data and the current
       
  4849      * reader position information. This method is called by other
       
  4850      * private methods to create tokens and is never called directly.
       
  4851      * @param {int} tt The token type.
       
  4852      * @param {String} value The text value of the token.
       
  4853      * @param {int} startLine The beginning line for the character.
       
  4854      * @param {int} startCol The beginning column for the character.
       
  4855      * @param {Object} options (Optional) Specifies a channel property
       
  4856      *      to indicate that a different channel should be scanned
       
  4857      *      and/or a hide property indicating that the token should
       
  4858      *      be hidden.
       
  4859      * @return {Object} A token object.
       
  4860      * @method createToken
       
  4861      */
       
  4862     createToken: function(tt, value, startLine, startCol, options) {
       
  4863         var reader = this._reader;
       
  4864         options = options || {};
       
  4865 
       
  4866         return {
       
  4867             value:      value,
       
  4868             type:       tt,
       
  4869             channel:    options.channel,
       
  4870             endChar:    options.endChar,
       
  4871             hide:       options.hide || false,
       
  4872             startLine:  startLine,
       
  4873             startCol:   startCol,
       
  4874             endLine:    reader.getLine(),
       
  4875             endCol:     reader.getCol()
       
  4876         };
       
  4877     },
       
  4878 
       
  4879     //-------------------------------------------------------------------------
       
  4880     // Methods to create specific tokens
       
  4881     //-------------------------------------------------------------------------
       
  4882 
       
  4883     /**
       
  4884      * Produces a token for any at-rule. If the at-rule is unknown, then
       
  4885      * the token is for a single "@" character.
       
  4886      * @param {String} first The first character for the token.
       
  4887      * @param {int} startLine The beginning line for the character.
       
  4888      * @param {int} startCol The beginning column for the character.
       
  4889      * @return {Object} A token object.
       
  4890      * @method atRuleToken
       
  4891      */
       
  4892     atRuleToken: function(first, startLine, startCol) {
       
  4893         var rule    = first,
       
  4894             reader  = this._reader,
       
  4895             tt      = Tokens.CHAR,
       
  4896             ident;
       
  4897 
       
  4898         /*
       
  4899          * First, mark where we are. There are only four @ rules,
       
  4900          * so anything else is really just an invalid token.
       
  4901          * Basically, if this doesn't match one of the known @
       
  4902          * rules, just return '@' as an unknown token and allow
       
  4903          * parsing to continue after that point.
       
  4904          */
       
  4905         reader.mark();
       
  4906 
       
  4907         //try to find the at-keyword
       
  4908         ident = this.readName();
       
  4909         rule = first + ident;
       
  4910         tt = Tokens.type(rule.toLowerCase());
       
  4911 
       
  4912         //if it's not valid, use the first character only and reset the reader
       
  4913         if (tt === Tokens.CHAR || tt === Tokens.UNKNOWN) {
       
  4914             if (rule.length > 1) {
       
  4915                 tt = Tokens.UNKNOWN_SYM;
       
  4916             } else {
       
  4917                 tt = Tokens.CHAR;
       
  4918                 rule = first;
       
  4919                 reader.reset();
       
  4920             }
       
  4921         }
       
  4922 
       
  4923         return this.createToken(tt, rule, startLine, startCol);
       
  4924     },
       
  4925 
       
  4926     /**
       
  4927      * Produces a character token based on the given character
       
  4928      * and location in the stream. If there's a special (non-standard)
       
  4929      * token name, this is used; otherwise CHAR is used.
       
  4930      * @param {String} c The character for the token.
       
  4931      * @param {int} startLine The beginning line for the character.
       
  4932      * @param {int} startCol The beginning column for the character.
       
  4933      * @return {Object} A token object.
       
  4934      * @method charToken
       
  4935      */
       
  4936     charToken: function(c, startLine, startCol) {
       
  4937         var tt = Tokens.type(c);
       
  4938         var opts = {};
       
  4939 
       
  4940         if (tt === -1) {
       
  4941             tt = Tokens.CHAR;
       
  4942         } else {
       
  4943             opts.endChar = Tokens[tt].endChar;
       
  4944         }
       
  4945 
       
  4946         return this.createToken(tt, c, startLine, startCol, opts);
       
  4947     },
       
  4948 
       
  4949     /**
       
  4950      * Produces a character token based on the given character
       
  4951      * and location in the stream. If there's a special (non-standard)
       
  4952      * token name, this is used; otherwise CHAR is used.
       
  4953      * @param {String} first The first character for the token.
       
  4954      * @param {int} startLine The beginning line for the character.
       
  4955      * @param {int} startCol The beginning column for the character.
       
  4956      * @return {Object} A token object.
       
  4957      * @method commentToken
       
  4958      */
       
  4959     commentToken: function(first, startLine, startCol) {
       
  4960         var comment = this.readComment(first);
       
  4961 
       
  4962         return this.createToken(Tokens.COMMENT, comment, startLine, startCol);
       
  4963     },
       
  4964 
       
  4965     /**
       
  4966      * Produces a comparison token based on the given character
       
  4967      * and location in the stream. The next character must be
       
  4968      * read and is already known to be an equals sign.
       
  4969      * @param {String} c The character for the token.
       
  4970      * @param {int} startLine The beginning line for the character.
       
  4971      * @param {int} startCol The beginning column for the character.
       
  4972      * @return {Object} A token object.
       
  4973      * @method comparisonToken
       
  4974      */
       
  4975     comparisonToken: function(c, startLine, startCol) {
       
  4976         var reader  = this._reader,
       
  4977             comparison  = c + reader.read(),
       
  4978             tt      = Tokens.type(comparison) || Tokens.CHAR;
       
  4979 
       
  4980         return this.createToken(tt, comparison, startLine, startCol);
       
  4981     },
       
  4982 
       
  4983     /**
       
  4984      * Produces a hash token based on the specified information. The
       
  4985      * first character provided is the pound sign (#) and then this
       
  4986      * method reads a name afterward.
       
  4987      * @param {String} first The first character (#) in the hash name.
       
  4988      * @param {int} startLine The beginning line for the character.
       
  4989      * @param {int} startCol The beginning column for the character.
       
  4990      * @return {Object} A token object.
       
  4991      * @method hashToken
       
  4992      */
       
  4993     hashToken: function(first, startLine, startCol) {
       
  4994         var name    = this.readName(first);
       
  4995 
       
  4996         return this.createToken(Tokens.HASH, name, startLine, startCol);
       
  4997     },
       
  4998 
       
  4999     /**
       
  5000      * Produces a CDO or CHAR token based on the specified information. The
       
  5001      * first character is provided and the rest is read by the function to determine
       
  5002      * the correct token to create.
       
  5003      * @param {String} first The first character in the token.
       
  5004      * @param {int} startLine The beginning line for the character.
       
  5005      * @param {int} startCol The beginning column for the character.
       
  5006      * @return {Object} A token object.
       
  5007      * @method htmlCommentStartToken
       
  5008      */
       
  5009     htmlCommentStartToken: function(first, startLine, startCol) {
       
  5010         var reader      = this._reader,
       
  5011             text        = first;
       
  5012 
       
  5013         reader.mark();
       
  5014         text += reader.readCount(3);
       
  5015 
       
  5016         if (text === "<!--") {
       
  5017             return this.createToken(Tokens.CDO, text, startLine, startCol);
       
  5018         } else {
       
  5019             reader.reset();
       
  5020             return this.charToken(first, startLine, startCol);
       
  5021         }
       
  5022     },
       
  5023 
       
  5024     /**
       
  5025      * Produces a CDC or CHAR token based on the specified information. The
       
  5026      * first character is provided and the rest is read by the function to determine
       
  5027      * the correct token to create.
       
  5028      * @param {String} first The first character in the token.
       
  5029      * @param {int} startLine The beginning line for the character.
       
  5030      * @param {int} startCol The beginning column for the character.
       
  5031      * @return {Object} A token object.
       
  5032      * @method htmlCommentEndToken
       
  5033      */
       
  5034     htmlCommentEndToken: function(first, startLine, startCol) {
       
  5035         var reader      = this._reader,
       
  5036             text        = first;
       
  5037 
       
  5038         reader.mark();
       
  5039         text += reader.readCount(2);
       
  5040 
       
  5041         if (text === "-->") {
       
  5042             return this.createToken(Tokens.CDC, text, startLine, startCol);
       
  5043         } else {
       
  5044             reader.reset();
       
  5045             return this.charToken(first, startLine, startCol);
       
  5046         }
       
  5047     },
       
  5048 
       
  5049     /**
       
  5050      * Produces an IDENT or FUNCTION token based on the specified information. The
       
  5051      * first character is provided and the rest is read by the function to determine
       
  5052      * the correct token to create.
       
  5053      * @param {String} first The first character in the identifier.
       
  5054      * @param {int} startLine The beginning line for the character.
       
  5055      * @param {int} startCol The beginning column for the character.
       
  5056      * @return {Object} A token object.
       
  5057      * @method identOrFunctionToken
       
  5058      */
       
  5059     identOrFunctionToken: function(first, startLine, startCol) {
       
  5060         var reader  = this._reader,
       
  5061             ident   = this.readName(first),
       
  5062             tt      = Tokens.IDENT,
       
  5063             uriFns  = ["url(", "url-prefix(", "domain("],
       
  5064             uri;
       
  5065 
       
  5066         //if there's a left paren immediately after, it's a URI or function
       
  5067         if (reader.peek() === "(") {
       
  5068             ident += reader.read();
       
  5069             if (uriFns.indexOf(ident.toLowerCase()) > -1) {
       
  5070                 reader.mark();
       
  5071                 uri = this.readURI(ident);
       
  5072                 if (uri === null) {
       
  5073                     //didn't find a valid URL or there's no closing paren
       
  5074                     reader.reset();
       
  5075                     tt = Tokens.FUNCTION;
       
  5076                 } else {
       
  5077                     tt = Tokens.URI;
       
  5078                     ident = uri;
       
  5079                 }
       
  5080             } else {
       
  5081                 tt = Tokens.FUNCTION;
       
  5082             }
       
  5083         } else if (reader.peek() === ":") {  //might be an IE function
       
  5084 
       
  5085             //IE-specific functions always being with progid:
       
  5086             if (ident.toLowerCase() === "progid") {
       
  5087                 ident += reader.readTo("(");
       
  5088                 tt = Tokens.IE_FUNCTION;
       
  5089             }
       
  5090         }
       
  5091 
       
  5092         return this.createToken(tt, ident, startLine, startCol);
       
  5093     },
       
  5094 
       
  5095     /**
       
  5096      * Produces an IMPORTANT_SYM or CHAR token based on the specified information. The
       
  5097      * first character is provided and the rest is read by the function to determine
       
  5098      * the correct token to create.
       
  5099      * @param {String} first The first character in the token.
       
  5100      * @param {int} startLine The beginning line for the character.
       
  5101      * @param {int} startCol The beginning column for the character.
       
  5102      * @return {Object} A token object.
       
  5103      * @method importantToken
       
  5104      */
       
  5105     importantToken: function(first, startLine, startCol) {
       
  5106         var reader      = this._reader,
       
  5107             important   = first,
       
  5108             tt          = Tokens.CHAR,
       
  5109             temp,
       
  5110             c;
       
  5111 
       
  5112         reader.mark();
       
  5113         c = reader.read();
       
  5114 
       
  5115         while (c) {
       
  5116 
       
  5117             //there can be a comment in here
       
  5118             if (c === "/") {
       
  5119 
       
  5120                 //if the next character isn't a star, then this isn't a valid !important token
       
  5121                 if (reader.peek() !== "*") {
       
  5122                     break;
       
  5123                 } else {
       
  5124                     temp = this.readComment(c);
       
  5125                     if (temp === "") {    //broken!
       
  5126                         break;
       
  5127                     }
       
  5128                 }
       
  5129             } else if (isWhitespace(c)) {
       
  5130                 important += c + this.readWhitespace();
       
  5131             } else if (/i/i.test(c)) {
       
  5132                 temp = reader.readCount(8);
       
  5133                 if (/mportant/i.test(temp)) {
       
  5134                     important += c + temp;
       
  5135                     tt = Tokens.IMPORTANT_SYM;
       
  5136 
       
  5137                 }
       
  5138                 break;  //we're done
       
  5139             } else {
       
  5140                 break;
       
  5141             }
       
  5142 
       
  5143             c = reader.read();
       
  5144         }
       
  5145 
       
  5146         if (tt === Tokens.CHAR) {
       
  5147             reader.reset();
       
  5148             return this.charToken(first, startLine, startCol);
       
  5149         } else {
       
  5150             return this.createToken(tt, important, startLine, startCol);
       
  5151         }
       
  5152 
       
  5153 
       
  5154     },
       
  5155 
       
  5156     /**
       
  5157      * Produces a NOT or CHAR token based on the specified information. The
       
  5158      * first character is provided and the rest is read by the function to determine
       
  5159      * the correct token to create.
       
  5160      * @param {String} first The first character in the token.
       
  5161      * @param {int} startLine The beginning line for the character.
       
  5162      * @param {int} startCol The beginning column for the character.
       
  5163      * @return {Object} A token object.
       
  5164      * @method notToken
       
  5165      */
       
  5166     notToken: function(first, startLine, startCol) {
       
  5167         var reader      = this._reader,
       
  5168             text        = first;
       
  5169 
       
  5170         reader.mark();
       
  5171         text += reader.readCount(4);
       
  5172 
       
  5173         if (text.toLowerCase() === ":not(") {
       
  5174             return this.createToken(Tokens.NOT, text, startLine, startCol);
       
  5175         } else {
       
  5176             reader.reset();
       
  5177             return this.charToken(first, startLine, startCol);
       
  5178         }
       
  5179     },
       
  5180 
       
  5181     /**
       
  5182      * Produces a number token based on the given character
       
  5183      * and location in the stream. This may return a token of
       
  5184      * NUMBER, EMS, EXS, LENGTH, ANGLE, TIME, FREQ, DIMENSION,
       
  5185      * or PERCENTAGE.
       
  5186      * @param {String} first The first character for the token.
       
  5187      * @param {int} startLine The beginning line for the character.
       
  5188      * @param {int} startCol The beginning column for the character.
       
  5189      * @return {Object} A token object.
       
  5190      * @method numberToken
       
  5191      */
       
  5192     numberToken: function(first, startLine, startCol) {
       
  5193         var reader  = this._reader,
       
  5194             value   = this.readNumber(first),
       
  5195             ident,
       
  5196             tt      = Tokens.NUMBER,
       
  5197             c       = reader.peek();
       
  5198 
       
  5199         if (isIdentStart(c)) {
       
  5200             ident = this.readName(reader.read());
       
  5201             value += ident;
       
  5202 
       
  5203             if (/^em$|^ex$|^px$|^gd$|^rem$|^vw$|^vh$|^vmax$|^vmin$|^ch$|^cm$|^mm$|^in$|^pt$|^pc$/i.test(ident)) {
       
  5204                 tt = Tokens.LENGTH;
       
  5205             } else if (/^deg|^rad$|^grad$|^turn$/i.test(ident)) {
       
  5206                 tt = Tokens.ANGLE;
       
  5207             } else if (/^ms$|^s$/i.test(ident)) {
       
  5208                 tt = Tokens.TIME;
       
  5209             } else if (/^hz$|^khz$/i.test(ident)) {
       
  5210                 tt = Tokens.FREQ;
       
  5211             } else if (/^dpi$|^dpcm$/i.test(ident)) {
       
  5212                 tt = Tokens.RESOLUTION;
       
  5213             } else {
       
  5214                 tt = Tokens.DIMENSION;
       
  5215             }
       
  5216 
       
  5217         } else if (c === "%") {
       
  5218             value += reader.read();
       
  5219             tt = Tokens.PERCENTAGE;
       
  5220         }
       
  5221 
       
  5222         return this.createToken(tt, value, startLine, startCol);
       
  5223     },
       
  5224 
       
  5225     /**
       
  5226      * Produces a string token based on the given character
       
  5227      * and location in the stream. Since strings may be indicated
       
  5228      * by single or double quotes, a failure to match starting
       
  5229      * and ending quotes results in an INVALID token being generated.
       
  5230      * The first character in the string is passed in and then
       
  5231      * the rest are read up to and including the final quotation mark.
       
  5232      * @param {String} first The first character in the string.
       
  5233      * @param {int} startLine The beginning line for the character.
       
  5234      * @param {int} startCol The beginning column for the character.
       
  5235      * @return {Object} A token object.
       
  5236      * @method stringToken
       
  5237      */
       
  5238     stringToken: function(first, startLine, startCol) {
       
  5239         var delim   = first,
       
  5240             string  = first,
       
  5241             reader  = this._reader,
       
  5242             tt      = Tokens.STRING,
       
  5243             c       = reader.read(),
       
  5244             i;
       
  5245 
       
  5246         while (c) {
       
  5247             string += c;
       
  5248 
       
  5249             if (c === "\\") {
       
  5250                 c = reader.read();
       
  5251                 if (c === null) {
       
  5252                     break; // premature EOF after backslash
       
  5253                 } else if (/[^\r\n\f0-9a-f]/i.test(c)) {
       
  5254                     // single-character escape
       
  5255                     string += c;
       
  5256                 } else {
       
  5257                     // read up to six hex digits
       
  5258                     for (i=0; isHexDigit(c) && i<6; i++) {
       
  5259                         string += c;
       
  5260                         c = reader.read();
       
  5261                     }
       
  5262                     // swallow trailing newline or space
       
  5263                     if (c === "\r" && reader.peek() === "\n") {
       
  5264                         string += c;
       
  5265                         c = reader.read();
       
  5266                     }
       
  5267                     if (isWhitespace(c)) {
       
  5268                         string += c;
       
  5269                     } else {
       
  5270                         // This character is null or not part of the escape;
       
  5271                         // jump back to the top to process it.
       
  5272                         continue;
       
  5273                     }
       
  5274                 }
       
  5275             } else if (c === delim) {
       
  5276                 break; // delimiter found.
       
  5277             } else if (isNewLine(reader.peek())) {
       
  5278                 // newline without an escapement: it's an invalid string
       
  5279                 tt = Tokens.INVALID;
       
  5280                 break;
       
  5281             }
       
  5282             c = reader.read();
       
  5283         }
       
  5284 
       
  5285         //if c is null, that means we're out of input and the string was never closed
       
  5286         if (c === null) {
       
  5287             tt = Tokens.INVALID;
       
  5288         }
       
  5289 
       
  5290         return this.createToken(tt, string, startLine, startCol);
       
  5291     },
       
  5292 
       
  5293     unicodeRangeToken: function(first, startLine, startCol) {
       
  5294         var reader  = this._reader,
       
  5295             value   = first,
       
  5296             temp,
       
  5297             tt      = Tokens.CHAR;
       
  5298 
       
  5299         //then it should be a unicode range
       
  5300         if (reader.peek() === "+") {
       
  5301             reader.mark();
       
  5302             value += reader.read();
       
  5303             value += this.readUnicodeRangePart(true);
       
  5304 
       
  5305             //ensure there's an actual unicode range here
       
  5306             if (value.length === 2) {
       
  5307                 reader.reset();
       
  5308             } else {
       
  5309 
       
  5310                 tt = Tokens.UNICODE_RANGE;
       
  5311 
       
  5312                 //if there's a ? in the first part, there can't be a second part
       
  5313                 if (value.indexOf("?") === -1) {
       
  5314 
       
  5315                     if (reader.peek() === "-") {
       
  5316                         reader.mark();
       
  5317                         temp = reader.read();
       
  5318                         temp += this.readUnicodeRangePart(false);
       
  5319 
       
  5320                         //if there's not another value, back up and just take the first
       
  5321                         if (temp.length === 1) {
       
  5322                             reader.reset();
       
  5323                         } else {
       
  5324                             value += temp;
       
  5325                         }
       
  5326                     }
       
  5327 
       
  5328                 }
       
  5329             }
       
  5330         }
       
  5331 
       
  5332         return this.createToken(tt, value, startLine, startCol);
       
  5333     },
       
  5334 
       
  5335     /**
       
  5336      * Produces a S token based on the specified information. Since whitespace
       
  5337      * may have multiple characters, this consumes all whitespace characters
       
  5338      * into a single token.
       
  5339      * @param {String} first The first character in the token.
       
  5340      * @param {int} startLine The beginning line for the character.
       
  5341      * @param {int} startCol The beginning column for the character.
       
  5342      * @return {Object} A token object.
       
  5343      * @method whitespaceToken
       
  5344      */
       
  5345     whitespaceToken: function(first, startLine, startCol) {
       
  5346         var value   = first + this.readWhitespace();
       
  5347         return this.createToken(Tokens.S, value, startLine, startCol);
       
  5348     },
       
  5349 
       
  5350 
       
  5351     //-------------------------------------------------------------------------
       
  5352     // Methods to read values from the string stream
       
  5353     //-------------------------------------------------------------------------
       
  5354 
       
  5355     readUnicodeRangePart: function(allowQuestionMark) {
       
  5356         var reader  = this._reader,
       
  5357             part = "",
       
  5358             c       = reader.peek();
       
  5359 
       
  5360         //first read hex digits
       
  5361         while (isHexDigit(c) && part.length < 6) {
       
  5362             reader.read();
       
  5363             part += c;
       
  5364             c = reader.peek();
       
  5365         }
       
  5366 
       
  5367         //then read question marks if allowed
       
  5368         if (allowQuestionMark) {
       
  5369             while (c === "?" && part.length < 6) {
       
  5370                 reader.read();
       
  5371                 part += c;
       
  5372                 c = reader.peek();
       
  5373             }
       
  5374         }
       
  5375 
       
  5376         //there can't be any other characters after this point
       
  5377 
       
  5378         return part;
       
  5379     },
       
  5380 
       
  5381     readWhitespace: function() {
       
  5382         var reader  = this._reader,
       
  5383             whitespace = "",
       
  5384             c       = reader.peek();
       
  5385 
       
  5386         while (isWhitespace(c)) {
       
  5387             reader.read();
       
  5388             whitespace += c;
       
  5389             c = reader.peek();
       
  5390         }
       
  5391 
       
  5392         return whitespace;
       
  5393     },
       
  5394     readNumber: function(first) {
       
  5395         var reader  = this._reader,
       
  5396             number  = first,
       
  5397             hasDot  = (first === "."),
       
  5398             c       = reader.peek();
       
  5399 
       
  5400 
       
  5401         while (c) {
       
  5402             if (isDigit(c)) {
       
  5403                 number += reader.read();
       
  5404             } else if (c === ".") {
       
  5405                 if (hasDot) {
       
  5406                     break;
       
  5407                 } else {
       
  5408                     hasDot = true;
       
  5409                     number += reader.read();
       
  5410                 }
       
  5411             } else {
       
  5412                 break;
       
  5413             }
       
  5414 
       
  5415             c = reader.peek();
       
  5416         }
       
  5417 
       
  5418         return number;
       
  5419     },
       
  5420 
       
  5421     // returns null w/o resetting reader if string is invalid.
       
  5422     readString: function() {
       
  5423         var token = this.stringToken(this._reader.read(), 0, 0);
       
  5424         return token.type === Tokens.INVALID ? null : token.value;
       
  5425     },
       
  5426 
       
  5427     // returns null w/o resetting reader if URI is invalid.
       
  5428     readURI: function(first) {
       
  5429         var reader  = this._reader,
       
  5430             uri     = first,
       
  5431             inner   = "",
       
  5432             c       = reader.peek();
       
  5433 
       
  5434         //skip whitespace before
       
  5435         while (c && isWhitespace(c)) {
       
  5436             reader.read();
       
  5437             c = reader.peek();
       
  5438         }
       
  5439 
       
  5440         //it's a string
       
  5441         if (c === "'" || c === "\"") {
       
  5442             inner = this.readString();
       
  5443             if (inner !== null) {
       
  5444                 inner = PropertyValuePart.parseString(inner);
       
  5445             }
       
  5446         } else {
       
  5447             inner = this.readUnquotedURL();
       
  5448         }
       
  5449 
       
  5450         c = reader.peek();
       
  5451 
       
  5452         //skip whitespace after
       
  5453         while (c && isWhitespace(c)) {
       
  5454             reader.read();
       
  5455             c = reader.peek();
       
  5456         }
       
  5457 
       
  5458         //if there was no inner value or the next character isn't closing paren, it's not a URI
       
  5459         if (inner === null || c !== ")") {
       
  5460             uri = null;
       
  5461         } else {
       
  5462             // Ensure argument to URL is always double-quoted
       
  5463             // (This simplifies later processing in PropertyValuePart.)
       
  5464             uri += PropertyValuePart.serializeString(inner) + reader.read();
       
  5465         }
       
  5466 
       
  5467         return uri;
       
  5468     },
       
  5469     // This method never fails, although it may return an empty string.
       
  5470     readUnquotedURL: function(first) {
       
  5471         var reader  = this._reader,
       
  5472             url     = first || "",
       
  5473             c;
       
  5474 
       
  5475         for (c = reader.peek(); c; c = reader.peek()) {
       
  5476             // Note that the grammar at
       
  5477             // https://www.w3.org/TR/CSS2/grammar.html#scanner
       
  5478             // incorrectly includes the backslash character in the
       
  5479             // `url` production, although it is correctly omitted in
       
  5480             // the `baduri1` production.
       
  5481             if (nonascii.test(c) || /^[\-!#$%&*-\[\]-~]$/.test(c)) {
       
  5482                 url += c;
       
  5483                 reader.read();
       
  5484             } else if (c === "\\") {
       
  5485                 if (/^[^\r\n\f]$/.test(reader.peek(2))) {
       
  5486                     url += this.readEscape(reader.read(), true);
       
  5487                 } else {
       
  5488                     break; // bad escape sequence.
       
  5489                 }
       
  5490             } else {
       
  5491                 break; // bad character
       
  5492             }
       
  5493         }
       
  5494 
       
  5495         return url;
       
  5496     },
       
  5497 
       
  5498     readName: function(first) {
       
  5499         var reader  = this._reader,
       
  5500             ident   = first || "",
       
  5501             c;
       
  5502 
       
  5503         for (c = reader.peek(); c; c = reader.peek()) {
       
  5504             if (c === "\\") {
       
  5505                 if (/^[^\r\n\f]$/.test(reader.peek(2))) {
       
  5506                     ident += this.readEscape(reader.read(), true);
       
  5507                 } else {
       
  5508                     // Bad escape sequence.
       
  5509                     break;
       
  5510                 }
       
  5511             } else if (isNameChar(c)) {
       
  5512                 ident += reader.read();
       
  5513             } else {
       
  5514                 break;
       
  5515             }
       
  5516         }
       
  5517 
       
  5518         return ident;
       
  5519     },
       
  5520 
       
  5521     readEscape: function(first, unescape) {
       
  5522         var reader  = this._reader,
       
  5523             cssEscape = first || "",
       
  5524             i       = 0,
       
  5525             c       = reader.peek();
       
  5526 
       
  5527         if (isHexDigit(c)) {
       
  5528             do {
       
  5529                 cssEscape += reader.read();
       
  5530                 c = reader.peek();
       
  5531             } while (c && isHexDigit(c) && ++i < 6);
       
  5532         }
       
  5533 
       
  5534         if (cssEscape.length === 1) {
       
  5535             if (/^[^\r\n\f0-9a-f]$/.test(c)) {
       
  5536                 reader.read();
       
  5537                 if (unescape) {
       
  5538                     return c;
       
  5539                 }
       
  5540             } else {
       
  5541                 // We should never get here (readName won't call readEscape
       
  5542                 // if the escape sequence is bad).
       
  5543                 throw new Error("Bad escape sequence.");
       
  5544             }
       
  5545         } else if (c === "\r") {
       
  5546             reader.read();
       
  5547             if (reader.peek() === "\n") {
       
  5548                 c += reader.read();
       
  5549             }
       
  5550         } else if (/^[ \t\n\f]$/.test(c)) {
       
  5551             reader.read();
       
  5552         } else {
       
  5553             c = "";
       
  5554         }
       
  5555 
       
  5556         if (unescape) {
       
  5557             var cp = parseInt(cssEscape.slice(first.length), 16);
       
  5558             return String.fromCodePoint ? String.fromCodePoint(cp) :
       
  5559                 String.fromCharCode(cp);
       
  5560         }
       
  5561         return cssEscape + c;
       
  5562     },
       
  5563 
       
  5564     readComment: function(first) {
       
  5565         var reader  = this._reader,
       
  5566             comment = first || "",
       
  5567             c       = reader.read();
       
  5568 
       
  5569         if (c === "*") {
       
  5570             while (c) {
       
  5571                 comment += c;
       
  5572 
       
  5573                 //look for end of comment
       
  5574                 if (comment.length > 2 && c === "*" && reader.peek() === "/") {
       
  5575                     comment += reader.read();
       
  5576                     break;
       
  5577                 }
       
  5578 
       
  5579                 c = reader.read();
       
  5580             }
       
  5581 
       
  5582             return comment;
       
  5583         } else {
       
  5584             return "";
       
  5585         }
       
  5586 
       
  5587     }
       
  5588 });
       
  5589 
       
  5590 },{"../util/TokenStreamBase":27,"./PropertyValuePart":11,"./Tokens":18}],18:[function(require,module,exports){
       
  5591 "use strict";
       
  5592 
       
  5593 var Tokens = module.exports = [
       
  5594 
       
  5595     /*
       
  5596      * The following token names are defined in CSS3 Grammar: https://www.w3.org/TR/css3-syntax/#lexical
       
  5597      */
       
  5598 
       
  5599     // HTML-style comments
       
  5600     { name: "CDO" },
       
  5601     { name: "CDC" },
       
  5602 
       
  5603     // ignorables
       
  5604     { name: "S", whitespace: true/*, channel: "ws"*/ },
       
  5605     { name: "COMMENT", comment: true, hide: true, channel: "comment" },
       
  5606 
       
  5607     // attribute equality
       
  5608     { name: "INCLUDES", text: "~=" },
       
  5609     { name: "DASHMATCH", text: "|=" },
       
  5610     { name: "PREFIXMATCH", text: "^=" },
       
  5611     { name: "SUFFIXMATCH", text: "$=" },
       
  5612     { name: "SUBSTRINGMATCH", text: "*=" },
       
  5613 
       
  5614     // identifier types
       
  5615     { name: "STRING" },
       
  5616     { name: "IDENT" },
       
  5617     { name: "HASH" },
       
  5618 
       
  5619     // at-keywords
       
  5620     { name: "IMPORT_SYM", text: "@import" },
       
  5621     { name: "PAGE_SYM", text: "@page" },
       
  5622     { name: "MEDIA_SYM", text: "@media" },
       
  5623     { name: "FONT_FACE_SYM", text: "@font-face" },
       
  5624     { name: "CHARSET_SYM", text: "@charset" },
       
  5625     { name: "NAMESPACE_SYM", text: "@namespace" },
       
  5626     { name: "SUPPORTS_SYM", text: "@supports" },
       
  5627     { name: "VIEWPORT_SYM", text: ["@viewport", "@-ms-viewport", "@-o-viewport"] },
       
  5628     { name: "DOCUMENT_SYM", text: ["@document", "@-moz-document"] },
       
  5629     { name: "UNKNOWN_SYM" },
       
  5630     //{ name: "ATKEYWORD"},
       
  5631 
       
  5632     // CSS3 animations
       
  5633     { name: "KEYFRAMES_SYM", text: [ "@keyframes", "@-webkit-keyframes", "@-moz-keyframes", "@-o-keyframes" ] },
       
  5634 
       
  5635     // important symbol
       
  5636     { name: "IMPORTANT_SYM" },
       
  5637 
       
  5638     // measurements
       
  5639     { name: "LENGTH" },
       
  5640     { name: "ANGLE" },
       
  5641     { name: "TIME" },
       
  5642     { name: "FREQ" },
       
  5643     { name: "DIMENSION" },
       
  5644     { name: "PERCENTAGE" },
       
  5645     { name: "NUMBER" },
       
  5646 
       
  5647     // functions
       
  5648     { name: "URI" },
       
  5649     { name: "FUNCTION" },
       
  5650 
       
  5651     // Unicode ranges
       
  5652     { name: "UNICODE_RANGE" },
       
  5653 
       
  5654     /*
       
  5655      * The following token names are defined in CSS3 Selectors: https://www.w3.org/TR/css3-selectors/#selector-syntax
       
  5656      */
       
  5657 
       
  5658     // invalid string
       
  5659     { name: "INVALID" },
       
  5660 
       
  5661     // combinators
       
  5662     { name: "PLUS", text: "+" },
       
  5663     { name: "GREATER", text: ">" },
       
  5664     { name: "COMMA", text: "," },
       
  5665     { name: "TILDE", text: "~" },
       
  5666 
       
  5667     // modifier
       
  5668     { name: "NOT" },
       
  5669 
       
  5670     /*
       
  5671      * Defined in CSS3 Paged Media
       
  5672      */
       
  5673     { name: "TOPLEFTCORNER_SYM", text: "@top-left-corner" },
       
  5674     { name: "TOPLEFT_SYM", text: "@top-left" },
       
  5675     { name: "TOPCENTER_SYM", text: "@top-center" },
       
  5676     { name: "TOPRIGHT_SYM", text: "@top-right" },
       
  5677     { name: "TOPRIGHTCORNER_SYM", text: "@top-right-corner" },
       
  5678     { name: "BOTTOMLEFTCORNER_SYM", text: "@bottom-left-corner" },
       
  5679     { name: "BOTTOMLEFT_SYM", text: "@bottom-left" },
       
  5680     { name: "BOTTOMCENTER_SYM", text: "@bottom-center" },
       
  5681     { name: "BOTTOMRIGHT_SYM", text: "@bottom-right" },
       
  5682     { name: "BOTTOMRIGHTCORNER_SYM", text: "@bottom-right-corner" },
       
  5683     { name: "LEFTTOP_SYM", text: "@left-top" },
       
  5684     { name: "LEFTMIDDLE_SYM", text: "@left-middle" },
       
  5685     { name: "LEFTBOTTOM_SYM", text: "@left-bottom" },
       
  5686     { name: "RIGHTTOP_SYM", text: "@right-top" },
       
  5687     { name: "RIGHTMIDDLE_SYM", text: "@right-middle" },
       
  5688     { name: "RIGHTBOTTOM_SYM", text: "@right-bottom" },
       
  5689 
       
  5690     /*
       
  5691      * The following token names are defined in CSS3 Media Queries: https://www.w3.org/TR/css3-mediaqueries/#syntax
       
  5692      */
       
  5693     /*{ name: "MEDIA_ONLY", state: "media"},
       
  5694     { name: "MEDIA_NOT", state: "media"},
       
  5695     { name: "MEDIA_AND", state: "media"},*/
       
  5696     { name: "RESOLUTION", state: "media" },
       
  5697 
       
  5698     /*
       
  5699      * The following token names are not defined in any CSS specification but are used by the lexer.
       
  5700      */
       
  5701 
       
  5702     // not a real token, but useful for stupid IE filters
       
  5703     { name: "IE_FUNCTION" },
       
  5704 
       
  5705     // part of CSS3 grammar but not the Flex code
       
  5706     { name: "CHAR" },
       
  5707 
       
  5708     // TODO: Needed?
       
  5709     // Not defined as tokens, but might as well be
       
  5710     {
       
  5711         name: "PIPE",
       
  5712         text: "|"
       
  5713     },
       
  5714     {
       
  5715         name: "SLASH",
       
  5716         text: "/"
       
  5717     },
       
  5718     {
       
  5719         name: "MINUS",
       
  5720         text: "-"
       
  5721     },
       
  5722     {
       
  5723         name: "STAR",
       
  5724         text: "*"
       
  5725     },
       
  5726 
       
  5727     {
       
  5728         name: "LBRACE",
       
  5729         endChar: "}",
       
  5730         text: "{"
       
  5731     },
       
  5732     {
       
  5733         name: "RBRACE",
       
  5734         text: "}"
       
  5735     },
       
  5736     {
       
  5737         name: "LBRACKET",
       
  5738         endChar: "]",
       
  5739         text: "["
       
  5740     },
       
  5741     {
       
  5742         name: "RBRACKET",
       
  5743         text: "]"
       
  5744     },
       
  5745     {
       
  5746         name: "EQUALS",
       
  5747         text: "="
       
  5748     },
       
  5749     {
       
  5750         name: "COLON",
       
  5751         text: ":"
       
  5752     },
       
  5753     {
       
  5754         name: "SEMICOLON",
       
  5755         text: ";"
       
  5756     },
       
  5757     {
       
  5758         name: "LPAREN",
       
  5759         endChar: ")",
       
  5760         text: "("
       
  5761     },
       
  5762     {
       
  5763         name: "RPAREN",
       
  5764         text: ")"
       
  5765     },
       
  5766     {
       
  5767         name: "DOT",
       
  5768         text: "."
       
  5769     }
       
  5770 ];
       
  5771 
       
  5772 (function() {
       
  5773     var nameMap = [],
       
  5774         typeMap = Object.create(null);
       
  5775 
       
  5776     Tokens.UNKNOWN = -1;
       
  5777     Tokens.unshift({ name:"EOF" });
       
  5778     for (var i=0, len = Tokens.length; i < len; i++) {
       
  5779         nameMap.push(Tokens[i].name);
       
  5780         Tokens[Tokens[i].name] = i;
       
  5781         if (Tokens[i].text) {
       
  5782             if (Tokens[i].text instanceof Array) {
       
  5783                 for (var j=0; j < Tokens[i].text.length; j++) {
       
  5784                     typeMap[Tokens[i].text[j]] = i;
       
  5785                 }
       
  5786             } else {
       
  5787                 typeMap[Tokens[i].text] = i;
       
  5788             }
       
  5789         }
       
  5790     }
       
  5791 
       
  5792     Tokens.name = function(tt) {
       
  5793         return nameMap[tt];
       
  5794     };
       
  5795 
       
  5796     Tokens.type = function(c) {
       
  5797         return typeMap[c] || -1;
       
  5798     };
       
  5799 })();
       
  5800 
       
  5801 },{}],19:[function(require,module,exports){
       
  5802 "use strict";
       
  5803 
       
  5804 /* exported Validation */
       
  5805 
       
  5806 var Matcher = require("./Matcher");
       
  5807 var Properties = require("./Properties");
       
  5808 var ValidationTypes = require("./ValidationTypes");
       
  5809 var ValidationError = require("./ValidationError");
       
  5810 var PropertyValueIterator = require("./PropertyValueIterator");
       
  5811 
       
  5812 var Validation = module.exports = {
       
  5813 
       
  5814     validate: function(property, value) {
       
  5815 
       
  5816         //normalize name
       
  5817         var name        = property.toString().toLowerCase(),
       
  5818             expression  = new PropertyValueIterator(value),
       
  5819             spec        = Properties[name],
       
  5820             part;
       
  5821 
       
  5822         if (!spec) {
       
  5823             if (name.indexOf("-") !== 0) {    //vendor prefixed are ok
       
  5824                 throw new ValidationError("Unknown property '" + property + "'.", property.line, property.col);
       
  5825             }
       
  5826         } else if (typeof spec !== "number") {
       
  5827 
       
  5828             // All properties accept some CSS-wide values.
       
  5829             // https://drafts.csswg.org/css-values-3/#common-keywords
       
  5830             if (ValidationTypes.isAny(expression, "inherit | initial | unset")) {
       
  5831                 if (expression.hasNext()) {
       
  5832                     part = expression.next();
       
  5833                     throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
       
  5834                 }
       
  5835                 return;
       
  5836             }
       
  5837 
       
  5838             // Property-specific validation.
       
  5839             this.singleProperty(spec, expression);
       
  5840 
       
  5841         }
       
  5842 
       
  5843     },
       
  5844 
       
  5845     singleProperty: function(types, expression) {
       
  5846 
       
  5847         var result      = false,
       
  5848             value       = expression.value,
       
  5849             part;
       
  5850 
       
  5851         result = Matcher.parse(types).match(expression);
       
  5852 
       
  5853         if (!result) {
       
  5854             if (expression.hasNext() && !expression.isFirst()) {
       
  5855                 part = expression.peek();
       
  5856                 throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
       
  5857             } else {
       
  5858                 throw new ValidationError("Expected (" + ValidationTypes.describe(types) + ") but found '" + value + "'.", value.line, value.col);
       
  5859             }
       
  5860         } else if (expression.hasNext()) {
       
  5861             part = expression.next();
       
  5862             throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
       
  5863         }
       
  5864 
       
  5865     }
       
  5866 
       
  5867 };
       
  5868 
       
  5869 },{"./Matcher":3,"./Properties":7,"./PropertyValueIterator":10,"./ValidationError":20,"./ValidationTypes":21}],20:[function(require,module,exports){
       
  5870 "use strict";
       
  5871 
       
  5872 module.exports = ValidationError;
       
  5873 
       
  5874 /**
       
  5875  * Type to use when a validation error occurs.
       
  5876  * @class ValidationError
       
  5877  * @namespace parserlib.util
       
  5878  * @constructor
       
  5879  * @param {String} message The error message.
       
  5880  * @param {int} line The line at which the error occurred.
       
  5881  * @param {int} col The column at which the error occurred.
       
  5882  */
       
  5883 function ValidationError(message, line, col) {
       
  5884 
       
  5885     /**
       
  5886      * The column at which the error occurred.
       
  5887      * @type int
       
  5888      * @property col
       
  5889      */
       
  5890     this.col = col;
       
  5891 
       
  5892     /**
       
  5893      * The line at which the error occurred.
       
  5894      * @type int
       
  5895      * @property line
       
  5896      */
       
  5897     this.line = line;
       
  5898 
       
  5899     /**
       
  5900      * The text representation of the unit.
       
  5901      * @type String
       
  5902      * @property text
       
  5903      */
       
  5904     this.message = message;
       
  5905 
       
  5906 }
       
  5907 
       
  5908 //inherit from Error
       
  5909 ValidationError.prototype = new Error();
       
  5910 
       
  5911 },{}],21:[function(require,module,exports){
       
  5912 "use strict";
       
  5913 
       
  5914 var ValidationTypes = module.exports;
       
  5915 
       
  5916 var Matcher = require("./Matcher");
       
  5917 
       
  5918 function copy(to, from) {
       
  5919     Object.keys(from).forEach(function(prop) {
       
  5920         to[prop] = from[prop];
       
  5921     });
       
  5922 }
       
  5923 copy(ValidationTypes, {
       
  5924 
       
  5925     isLiteral: function (part, literals) {
       
  5926         var text = part.text.toString().toLowerCase(),
       
  5927             args = literals.split(" | "),
       
  5928             i, len, found = false;
       
  5929 
       
  5930         for (i=0, len=args.length; i < len && !found; i++) {
       
  5931             if (args[i].charAt(0) === "<") {
       
  5932                 found = this.simple[args[i]](part);
       
  5933             } else if (args[i].slice(-2) === "()") {
       
  5934                 found = (part.type === "function" &&
       
  5935                          part.name === args[i].slice(0, -2));
       
  5936             } else if (text === args[i].toLowerCase()) {
       
  5937                 found = true;
       
  5938             }
       
  5939         }
       
  5940 
       
  5941         return found;
       
  5942     },
       
  5943 
       
  5944     isSimple: function(type) {
       
  5945         return Boolean(this.simple[type]);
       
  5946     },
       
  5947 
       
  5948     isComplex: function(type) {
       
  5949         return Boolean(this.complex[type]);
       
  5950     },
       
  5951 
       
  5952     describe: function(type) {
       
  5953         if (this.complex[type] instanceof Matcher) {
       
  5954             return this.complex[type].toString(0);
       
  5955         }
       
  5956         return type;
       
  5957     },
       
  5958 
       
  5959     /**
       
  5960      * Determines if the next part(s) of the given expression
       
  5961      * are any of the given types.
       
  5962      */
       
  5963     isAny: function (expression, types) {
       
  5964         var args = types.split(" | "),
       
  5965             i, len, found = false;
       
  5966 
       
  5967         for (i=0, len=args.length; i < len && !found && expression.hasNext(); i++) {
       
  5968             found = this.isType(expression, args[i]);
       
  5969         }
       
  5970 
       
  5971         return found;
       
  5972     },
       
  5973 
       
  5974     /**
       
  5975      * Determines if the next part(s) of the given expression
       
  5976      * are one of a group.
       
  5977      */
       
  5978     isAnyOfGroup: function(expression, types) {
       
  5979         var args = types.split(" || "),
       
  5980             i, len, found = false;
       
  5981 
       
  5982         for (i=0, len=args.length; i < len && !found; i++) {
       
  5983             found = this.isType(expression, args[i]);
       
  5984         }
       
  5985 
       
  5986         return found ? args[i-1] : false;
       
  5987     },
       
  5988 
       
  5989     /**
       
  5990      * Determines if the next part(s) of the given expression
       
  5991      * are of a given type.
       
  5992      */
       
  5993     isType: function (expression, type) {
       
  5994         var part = expression.peek(),
       
  5995             result = false;
       
  5996 
       
  5997         if (type.charAt(0) !== "<") {
       
  5998             result = this.isLiteral(part, type);
       
  5999             if (result) {
       
  6000                 expression.next();
       
  6001             }
       
  6002         } else if (this.simple[type]) {
       
  6003             result = this.simple[type](part);
       
  6004             if (result) {
       
  6005                 expression.next();
       
  6006             }
       
  6007         } else if (this.complex[type] instanceof Matcher) {
       
  6008             result = this.complex[type].match(expression);
       
  6009         } else {
       
  6010             result = this.complex[type](expression);
       
  6011         }
       
  6012 
       
  6013         return result;
       
  6014     },
       
  6015 
       
  6016 
       
  6017     simple: {
       
  6018         __proto__: null,
       
  6019 
       
  6020         "<absolute-size>":
       
  6021             "xx-small | x-small | small | medium | large | x-large | xx-large",
       
  6022 
       
  6023         "<animateable-feature>":
       
  6024             "scroll-position | contents | <animateable-feature-name>",
       
  6025 
       
  6026         "<animateable-feature-name>": function(part) {
       
  6027             return this["<ident>"](part) &&
       
  6028                 !/^(unset|initial|inherit|will-change|auto|scroll-position|contents)$/i.test(part);
       
  6029         },
       
  6030 
       
  6031         "<angle>": function(part) {
       
  6032             return part.type === "angle";
       
  6033         },
       
  6034 
       
  6035         "<attachment>": "scroll | fixed | local",
       
  6036 
       
  6037         "<attr>": "attr()",
       
  6038 
       
  6039         // inset() = inset( <shape-arg>{1,4} [round <border-radius>]? )
       
  6040         // circle() = circle( [<shape-radius>]? [at <position>]? )
       
  6041         // ellipse() = ellipse( [<shape-radius>{2}]? [at <position>]? )
       
  6042         // polygon() = polygon( [<fill-rule>,]? [<shape-arg> <shape-arg>]# )
       
  6043         "<basic-shape>": "inset() | circle() | ellipse() | polygon()",
       
  6044 
       
  6045         "<bg-image>": "<image> | <gradient> | none",
       
  6046 
       
  6047         "<border-style>":
       
  6048             "none | hidden | dotted | dashed | solid | double | groove | " +
       
  6049             "ridge | inset | outset",
       
  6050 
       
  6051         "<border-width>": "<length> | thin | medium | thick",
       
  6052 
       
  6053         "<box>": "padding-box | border-box | content-box",
       
  6054 
       
  6055         "<clip-source>": "<uri>",
       
  6056 
       
  6057         "<color>": function(part) {
       
  6058             return part.type === "color" || String(part) === "transparent" || String(part) === "currentColor";
       
  6059         },
       
  6060 
       
  6061         // The SVG <color> spec doesn't include "currentColor" or "transparent" as a color.
       
  6062         "<color-svg>": function(part) {
       
  6063             return part.type === "color";
       
  6064         },
       
  6065 
       
  6066         "<content>": "content()",
       
  6067 
       
  6068         // https://www.w3.org/TR/css3-sizing/#width-height-keywords
       
  6069         "<content-sizing>":
       
  6070             "fill-available | -moz-available | -webkit-fill-available | " +
       
  6071             "max-content | -moz-max-content | -webkit-max-content | " +
       
  6072             "min-content | -moz-min-content | -webkit-min-content | " +
       
  6073             "fit-content | -moz-fit-content | -webkit-fit-content",
       
  6074 
       
  6075         "<feature-tag-value>": function(part) {
       
  6076             return part.type === "function" && /^[A-Z0-9]{4}$/i.test(part);
       
  6077         },
       
  6078 
       
  6079         // custom() isn't actually in the spec
       
  6080         "<filter-function>":
       
  6081             "blur() | brightness() | contrast() | custom() | " +
       
  6082             "drop-shadow() | grayscale() | hue-rotate() | invert() | " +
       
  6083             "opacity() | saturate() | sepia()",
       
  6084 
       
  6085         "<flex-basis>": "<width>",
       
  6086 
       
  6087         "<flex-direction>": "row | row-reverse | column | column-reverse",
       
  6088 
       
  6089         "<flex-grow>": "<number>",
       
  6090 
       
  6091         "<flex-shrink>": "<number>",
       
  6092 
       
  6093         "<flex-wrap>": "nowrap | wrap | wrap-reverse",
       
  6094 
       
  6095         "<font-size>":
       
  6096             "<absolute-size> | <relative-size> | <length> | <percentage>",
       
  6097 
       
  6098         "<font-stretch>":
       
  6099             "normal | ultra-condensed | extra-condensed | condensed | " +
       
  6100             "semi-condensed | semi-expanded | expanded | extra-expanded | " +
       
  6101             "ultra-expanded",
       
  6102 
       
  6103         "<font-style>": "normal | italic | oblique",
       
  6104 
       
  6105         "<font-variant-caps>":
       
  6106             "small-caps | all-small-caps | petite-caps | all-petite-caps | " +
       
  6107             "unicase | titling-caps",
       
  6108 
       
  6109         "<font-variant-css21>": "normal | small-caps",
       
  6110 
       
  6111         "<font-weight>":
       
  6112             "normal | bold | bolder | lighter | " +
       
  6113             "100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900",
       
  6114 
       
  6115         "<generic-family>":
       
  6116             "serif | sans-serif | cursive | fantasy | monospace",
       
  6117 
       
  6118         "<geometry-box>": "<shape-box> | fill-box | stroke-box | view-box",
       
  6119 
       
  6120         "<glyph-angle>": function(part) {
       
  6121             return part.type === "angle" && part.units === "deg";
       
  6122         },
       
  6123 
       
  6124         "<gradient>": function(part) {
       
  6125             return part.type === "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?(?:repeating\-)?(?:radial\-|linear\-)?gradient/i.test(part);
       
  6126         },
       
  6127 
       
  6128         "<icccolor>":
       
  6129             "cielab() | cielch() | cielchab() | " +
       
  6130             "icc-color() | icc-named-color()",
       
  6131 
       
  6132         //any identifier
       
  6133         "<ident>": function(part) {
       
  6134             return part.type === "identifier" || part.wasIdent;
       
  6135         },
       
  6136 
       
  6137         "<ident-not-generic-family>": function(part) {
       
  6138             return this["<ident>"](part) && !this["<generic-family>"](part);
       
  6139         },
       
  6140 
       
  6141         "<image>": "<uri>",
       
  6142 
       
  6143         "<integer>": function(part) {
       
  6144             return part.type === "integer";
       
  6145         },
       
  6146 
       
  6147         "<length>": function(part) {
       
  6148             if (part.type === "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?calc/i.test(part)) {
       
  6149                 return true;
       
  6150             } else {
       
  6151                 return part.type === "length" || part.type === "number" || part.type === "integer" || String(part) === "0";
       
  6152             }
       
  6153         },
       
  6154 
       
  6155         "<line>": function(part) {
       
  6156             return part.type === "integer";
       
  6157         },
       
  6158 
       
  6159         "<line-height>": "<number> | <length> | <percentage> | normal",
       
  6160 
       
  6161         "<margin-width>": "<length> | <percentage> | auto",
       
  6162 
       
  6163         "<miterlimit>": function(part) {
       
  6164             return this["<number>"](part) && part.value >= 1;
       
  6165         },
       
  6166 
       
  6167         "<nonnegative-length-or-percentage>": function(part) {
       
  6168             return (this["<length>"](part) || this["<percentage>"](part)) &&
       
  6169                 (String(part) === "0" || part.type === "function" || (part.value) >= 0);
       
  6170         },
       
  6171 
       
  6172         "<nonnegative-number-or-percentage>": function(part) {
       
  6173             return (this["<number>"](part) || this["<percentage>"](part)) &&
       
  6174                 (String(part) === "0" || part.type === "function" || (part.value) >= 0);
       
  6175         },
       
  6176 
       
  6177         "<number>": function(part) {
       
  6178             return part.type === "number" || this["<integer>"](part);
       
  6179         },
       
  6180 
       
  6181         "<opacity-value>": function(part) {
       
  6182             return this["<number>"](part) && part.value >= 0 && part.value <= 1;
       
  6183         },
       
  6184 
       
  6185         "<padding-width>": "<nonnegative-length-or-percentage>",
       
  6186 
       
  6187         "<percentage>": function(part) {
       
  6188             return part.type === "percentage" || String(part) === "0";
       
  6189         },
       
  6190 
       
  6191         "<relative-size>": "smaller | larger",
       
  6192 
       
  6193         "<shape>": "rect() | inset-rect()",
       
  6194 
       
  6195         "<shape-box>": "<box> | margin-box",
       
  6196 
       
  6197         "<single-animation-direction>":
       
  6198             "normal | reverse | alternate | alternate-reverse",
       
  6199 
       
  6200         "<single-animation-name>": function(part) {
       
  6201             return this["<ident>"](part) &&
       
  6202                 /^-?[a-z_][-a-z0-9_]+$/i.test(part) &&
       
  6203                 !/^(none|unset|initial|inherit)$/i.test(part);
       
  6204         },
       
  6205 
       
  6206         "<string>": function(part) {
       
  6207             return part.type === "string";
       
  6208         },
       
  6209 
       
  6210         "<time>": function(part) {
       
  6211             return part.type === "time";
       
  6212         },
       
  6213 
       
  6214         "<uri>": function(part) {
       
  6215             return part.type === "uri";
       
  6216         },
       
  6217 
       
  6218         "<width>": "<margin-width>"
       
  6219     },
       
  6220 
       
  6221     complex: {
       
  6222         __proto__: null,
       
  6223 
       
  6224         "<azimuth>":
       
  6225             "<angle>" +
       
  6226             " | " +
       
  6227             "[ [ left-side | far-left | left | center-left | center | " +
       
  6228             "center-right | right | far-right | right-side ] || behind ]" +
       
  6229             " | "+
       
  6230             "leftwards | rightwards",
       
  6231 
       
  6232         "<bg-position>": "<position>#",
       
  6233 
       
  6234         "<bg-size>":
       
  6235             "[ <length> | <percentage> | auto ]{1,2} | cover | contain",
       
  6236 
       
  6237         "<border-image-slice>":
       
  6238         // [<number> | <percentage>]{1,4} && fill?
       
  6239         // *but* fill can appear between any of the numbers
       
  6240         Matcher.many([true /* first element is required */],
       
  6241                      Matcher.cast("<nonnegative-number-or-percentage>"),
       
  6242                      Matcher.cast("<nonnegative-number-or-percentage>"),
       
  6243                      Matcher.cast("<nonnegative-number-or-percentage>"),
       
  6244                      Matcher.cast("<nonnegative-number-or-percentage>"),
       
  6245                      "fill"),
       
  6246 
       
  6247         "<border-radius>":
       
  6248             "<nonnegative-length-or-percentage>{1,4} " +
       
  6249             "[ / <nonnegative-length-or-percentage>{1,4} ]?",
       
  6250 
       
  6251         "<box-shadow>": "none | <shadow>#",
       
  6252 
       
  6253         "<clip-path>": "<basic-shape> || <geometry-box>",
       
  6254 
       
  6255         "<dasharray>":
       
  6256         // "list of comma and/or white space separated <length>s and
       
  6257         // <percentage>s".  There is a non-negative constraint.
       
  6258         Matcher.cast("<nonnegative-length-or-percentage>")
       
  6259             .braces(1, Infinity, "#", Matcher.cast(",").question()),
       
  6260 
       
  6261         "<family-name>":
       
  6262             // <string> | <IDENT>+
       
  6263             "<string> | <ident-not-generic-family> <ident>*",
       
  6264 
       
  6265         "<filter-function-list>": "[ <filter-function> | <uri> ]+",
       
  6266 
       
  6267         // https://www.w3.org/TR/2014/WD-css-flexbox-1-20140325/#flex-property
       
  6268         "<flex>":
       
  6269             "none | [ <flex-grow> <flex-shrink>? || <flex-basis> ]",
       
  6270 
       
  6271         "<font-family>": "[ <generic-family> | <family-name> ]#",
       
  6272 
       
  6273         "<font-shorthand>":
       
  6274             "[ <font-style> || <font-variant-css21> || " +
       
  6275             "<font-weight> || <font-stretch> ]? <font-size> " +
       
  6276             "[ / <line-height> ]? <font-family>",
       
  6277 
       
  6278         "<font-variant-alternates>":
       
  6279             // stylistic(<feature-value-name>)
       
  6280             "stylistic() || " +
       
  6281             "historical-forms || " +
       
  6282             // styleset(<feature-value-name> #)
       
  6283             "styleset() || " +
       
  6284             // character-variant(<feature-value-name> #)
       
  6285             "character-variant() || " +
       
  6286             // swash(<feature-value-name>)
       
  6287             "swash() || " +
       
  6288             // ornaments(<feature-value-name>)
       
  6289             "ornaments() || " +
       
  6290             // annotation(<feature-value-name>)
       
  6291             "annotation()",
       
  6292 
       
  6293         "<font-variant-ligatures>":
       
  6294             // <common-lig-values>
       
  6295             "[ common-ligatures | no-common-ligatures ] || " +
       
  6296             // <discretionary-lig-values>
       
  6297             "[ discretionary-ligatures | no-discretionary-ligatures ] || " +
       
  6298             // <historical-lig-values>
       
  6299             "[ historical-ligatures | no-historical-ligatures ] || " +
       
  6300             // <contextual-alt-values>
       
  6301             "[ contextual | no-contextual ]",
       
  6302 
       
  6303         "<font-variant-numeric>":
       
  6304             // <numeric-figure-values>
       
  6305             "[ lining-nums | oldstyle-nums ] || " +
       
  6306             // <numeric-spacing-values>
       
  6307             "[ proportional-nums | tabular-nums ] || " +
       
  6308             // <numeric-fraction-values>
       
  6309             "[ diagonal-fractions | stacked-fractions ] || " +
       
  6310             "ordinal || slashed-zero",
       
  6311 
       
  6312         "<font-variant-east-asian>":
       
  6313             // <east-asian-variant-values>
       
  6314             "[ jis78 | jis83 | jis90 | jis04 | simplified | traditional ] || " +
       
  6315             // <east-asian-width-values>
       
  6316             "[ full-width | proportional-width ] || " +
       
  6317             "ruby",
       
  6318 
       
  6319         // Note that <color> here is "as defined in the SVG spec", which
       
  6320         // is more restrictive that the <color> defined in the CSS spec.
       
  6321         // none | currentColor | <color> [<icccolor>]? |
       
  6322         // <funciri> [ none | currentColor | <color> [<icccolor>]? ]?
       
  6323         "<paint>": "<paint-basic> | <uri> <paint-basic>?",
       
  6324 
       
  6325         // Helper definition for <paint> above.
       
  6326         "<paint-basic>": "none | currentColor | <color-svg> <icccolor>?",
       
  6327 
       
  6328         "<position>":
       
  6329             // Because our `alt` combinator is ordered, we need to test these
       
  6330             // in order from longest possible match to shortest.
       
  6331             "[ center | [ left | right ] [ <percentage> | <length> ]? ] && " +
       
  6332             "[ center | [ top | bottom ] [ <percentage> | <length> ]? ]" +
       
  6333             " | " +
       
  6334             "[ left | center | right | <percentage> | <length> ] " +
       
  6335             "[ top | center | bottom | <percentage> | <length> ]" +
       
  6336             " | " +
       
  6337             "[ left | center | right | top | bottom | <percentage> | <length> ]",
       
  6338 
       
  6339         "<repeat-style>":
       
  6340             "repeat-x | repeat-y | [ repeat | space | round | no-repeat ]{1,2}",
       
  6341 
       
  6342         "<shadow>":
       
  6343         //inset? && [ <length>{2,4} && <color>? ]
       
  6344         Matcher.many([true /* length is required */],
       
  6345                      Matcher.cast("<length>").braces(2, 4), "inset", "<color>"),
       
  6346 
       
  6347         "<text-decoration-color>":
       
  6348            "<color>",
       
  6349 
       
  6350         "<text-decoration-line>":
       
  6351             "none | [ underline || overline || line-through || blink ]",
       
  6352 
       
  6353         "<text-decoration-style>":
       
  6354             "solid | double | dotted | dashed | wavy",
       
  6355 
       
  6356         "<will-change>":
       
  6357             "auto | <animateable-feature>#",
       
  6358 
       
  6359         "<x-one-radius>":
       
  6360             //[ <length> | <percentage> ] [ <length> | <percentage> ]?
       
  6361             "[ <length> | <percentage> ]{1,2}"
       
  6362     }
       
  6363 });
       
  6364 
       
  6365 Object.keys(ValidationTypes.simple).forEach(function(nt) {
       
  6366     var rule = ValidationTypes.simple[nt];
       
  6367     if (typeof rule === "string") {
       
  6368         ValidationTypes.simple[nt] = function(part) {
       
  6369             return ValidationTypes.isLiteral(part, rule);
       
  6370         };
       
  6371     }
       
  6372 });
       
  6373 
       
  6374 Object.keys(ValidationTypes.complex).forEach(function(nt) {
       
  6375     var rule = ValidationTypes.complex[nt];
       
  6376     if (typeof rule === "string") {
       
  6377         ValidationTypes.complex[nt] = Matcher.parse(rule);
       
  6378     }
       
  6379 });
       
  6380 
       
  6381 // Because this is defined relative to other complex validation types,
       
  6382 // we need to define it *after* the rest of the types are initialized.
       
  6383 ValidationTypes.complex["<font-variant>"] =
       
  6384     Matcher.oror({ expand: "<font-variant-ligatures>" },
       
  6385                  { expand: "<font-variant-alternates>" },
       
  6386                  "<font-variant-caps>",
       
  6387                  { expand: "<font-variant-numeric>" },
       
  6388                  { expand: "<font-variant-east-asian>" });
       
  6389 
       
  6390 },{"./Matcher":3}],22:[function(require,module,exports){
       
  6391 "use strict";
       
  6392 
       
  6393 module.exports = {
       
  6394     Colors            : require("./Colors"),
       
  6395     Combinator        : require("./Combinator"),
       
  6396     Parser            : require("./Parser"),
       
  6397     PropertyName      : require("./PropertyName"),
       
  6398     PropertyValue     : require("./PropertyValue"),
       
  6399     PropertyValuePart : require("./PropertyValuePart"),
       
  6400     Matcher           : require("./Matcher"),
       
  6401     MediaFeature      : require("./MediaFeature"),
       
  6402     MediaQuery        : require("./MediaQuery"),
       
  6403     Selector          : require("./Selector"),
       
  6404     SelectorPart      : require("./SelectorPart"),
       
  6405     SelectorSubPart   : require("./SelectorSubPart"),
       
  6406     Specificity       : require("./Specificity"),
       
  6407     TokenStream       : require("./TokenStream"),
       
  6408     Tokens            : require("./Tokens"),
       
  6409     ValidationError   : require("./ValidationError")
       
  6410 };
       
  6411 
       
  6412 },{"./Colors":1,"./Combinator":2,"./Matcher":3,"./MediaFeature":4,"./MediaQuery":5,"./Parser":6,"./PropertyName":8,"./PropertyValue":9,"./PropertyValuePart":11,"./Selector":13,"./SelectorPart":14,"./SelectorSubPart":15,"./Specificity":16,"./TokenStream":17,"./Tokens":18,"./ValidationError":20}],23:[function(require,module,exports){
       
  6413 "use strict";
       
  6414 
       
  6415 module.exports = EventTarget;
       
  6416 
       
  6417 /**
       
  6418  * A generic base to inherit from for any object
       
  6419  * that needs event handling.
       
  6420  * @class EventTarget
       
  6421  * @constructor
       
  6422  */
       
  6423 function EventTarget() {
       
  6424 
       
  6425     /**
       
  6426      * The array of listeners for various events.
       
  6427      * @type Object
       
  6428      * @property _listeners
       
  6429      * @private
       
  6430      */
       
  6431     this._listeners = Object.create(null);
       
  6432 }
       
  6433 
       
  6434 EventTarget.prototype = {
       
  6435 
       
  6436     //restore constructor
       
  6437     constructor: EventTarget,
       
  6438 
       
  6439     /**
       
  6440      * Adds a listener for a given event type.
       
  6441      * @param {String} type The type of event to add a listener for.
       
  6442      * @param {Function} listener The function to call when the event occurs.
       
  6443      * @return {void}
       
  6444      * @method addListener
       
  6445      */
       
  6446     addListener: function(type, listener) {
       
  6447         if (!this._listeners[type]) {
       
  6448             this._listeners[type] = [];
       
  6449         }
       
  6450 
       
  6451         this._listeners[type].push(listener);
       
  6452     },
       
  6453 
       
  6454     /**
       
  6455      * Fires an event based on the passed-in object.
       
  6456      * @param {Object|String} event An object with at least a 'type' attribute
       
  6457      *      or a string indicating the event name.
       
  6458      * @return {void}
       
  6459      * @method fire
       
  6460      */
       
  6461     fire: function(event) {
       
  6462         if (typeof event === "string") {
       
  6463             event = { type: event };
       
  6464         }
       
  6465         if (typeof event.target !== "undefined") {
       
  6466             event.target = this;
       
  6467         }
       
  6468 
       
  6469         if (typeof event.type === "undefined") {
       
  6470             throw new Error("Event object missing 'type' property.");
       
  6471         }
       
  6472 
       
  6473         if (this._listeners[event.type]) {
       
  6474 
       
  6475             //create a copy of the array and use that so listeners can't chane
       
  6476             var listeners = this._listeners[event.type].concat();
       
  6477             for (var i=0, len=listeners.length; i < len; i++) {
       
  6478                 listeners[i].call(this, event);
       
  6479             }
       
  6480         }
       
  6481     },
       
  6482 
       
  6483     /**
       
  6484      * Removes a listener for a given event type.
       
  6485      * @param {String} type The type of event to remove a listener from.
       
  6486      * @param {Function} listener The function to remove from the event.
       
  6487      * @return {void}
       
  6488      * @method removeListener
       
  6489      */
       
  6490     removeListener: function(type, listener) {
       
  6491         if (this._listeners[type]) {
       
  6492             var listeners = this._listeners[type];
       
  6493             for (var i=0, len=listeners.length; i < len; i++) {
       
  6494                 if (listeners[i] === listener) {
       
  6495                     listeners.splice(i, 1);
       
  6496                     break;
       
  6497                 }
       
  6498             }
       
  6499 
       
  6500 
       
  6501         }
       
  6502     }
       
  6503 };
       
  6504 
       
  6505 },{}],24:[function(require,module,exports){
       
  6506 "use strict";
       
  6507 
       
  6508 module.exports = StringReader;
       
  6509 
       
  6510 /**
       
  6511  * Convenient way to read through strings.
       
  6512  * @namespace parserlib.util
       
  6513  * @class StringReader
       
  6514  * @constructor
       
  6515  * @param {String} text The text to read.
       
  6516  */
       
  6517 function StringReader(text) {
       
  6518 
       
  6519     /**
       
  6520      * The input text with line endings normalized.
       
  6521      * @property _input
       
  6522      * @type String
       
  6523      * @private
       
  6524      */
       
  6525     this._input = text.replace(/(\r\n?|\n)/g, "\n");
       
  6526 
       
  6527 
       
  6528     /**
       
  6529      * The row for the character to be read next.
       
  6530      * @property _line
       
  6531      * @type int
       
  6532      * @private
       
  6533      */
       
  6534     this._line = 1;
       
  6535 
       
  6536 
       
  6537     /**
       
  6538      * The column for the character to be read next.
       
  6539      * @property _col
       
  6540      * @type int
       
  6541      * @private
       
  6542      */
       
  6543     this._col = 1;
       
  6544 
       
  6545     /**
       
  6546      * The index of the character in the input to be read next.
       
  6547      * @property _cursor
       
  6548      * @type int
       
  6549      * @private
       
  6550      */
       
  6551     this._cursor = 0;
       
  6552 }
       
  6553 
       
  6554 StringReader.prototype = {
       
  6555 
       
  6556     // restore constructor
       
  6557     constructor: StringReader,
       
  6558 
       
  6559     //-------------------------------------------------------------------------
       
  6560     // Position info
       
  6561     //-------------------------------------------------------------------------
       
  6562 
       
  6563     /**
       
  6564      * Returns the column of the character to be read next.
       
  6565      * @return {int} The column of the character to be read next.
       
  6566      * @method getCol
       
  6567      */
       
  6568     getCol: function() {
       
  6569         return this._col;
       
  6570     },
       
  6571 
       
  6572     /**
       
  6573      * Returns the row of the character to be read next.
       
  6574      * @return {int} The row of the character to be read next.
       
  6575      * @method getLine
       
  6576      */
       
  6577     getLine: function() {
       
  6578         return this._line;
       
  6579     },
       
  6580 
       
  6581     /**
       
  6582      * Determines if you're at the end of the input.
       
  6583      * @return {Boolean} True if there's no more input, false otherwise.
       
  6584      * @method eof
       
  6585      */
       
  6586     eof: function() {
       
  6587         return this._cursor === this._input.length;
       
  6588     },
       
  6589 
       
  6590     //-------------------------------------------------------------------------
       
  6591     // Basic reading
       
  6592     //-------------------------------------------------------------------------
       
  6593 
       
  6594     /**
       
  6595      * Reads the next character without advancing the cursor.
       
  6596      * @param {int} count How many characters to look ahead (default is 1).
       
  6597      * @return {String} The next character or null if there is no next character.
       
  6598      * @method peek
       
  6599      */
       
  6600     peek: function(count) {
       
  6601         var c = null;
       
  6602         count = typeof count === "undefined" ? 1 : count;
       
  6603 
       
  6604         // if we're not at the end of the input...
       
  6605         if (this._cursor < this._input.length) {
       
  6606 
       
  6607             // get character and increment cursor and column
       
  6608             c = this._input.charAt(this._cursor + count - 1);
       
  6609         }
       
  6610 
       
  6611         return c;
       
  6612     },
       
  6613 
       
  6614     /**
       
  6615      * Reads the next character from the input and adjusts the row and column
       
  6616      * accordingly.
       
  6617      * @return {String} The next character or null if there is no next character.
       
  6618      * @method read
       
  6619      */
       
  6620     read: function() {
       
  6621         var c = null;
       
  6622 
       
  6623         // if we're not at the end of the input...
       
  6624         if (this._cursor < this._input.length) {
       
  6625 
       
  6626             // if the last character was a newline, increment row count
       
  6627             // and reset column count
       
  6628             if (this._input.charAt(this._cursor) === "\n") {
       
  6629                 this._line++;
       
  6630                 this._col=1;
       
  6631             } else {
       
  6632                 this._col++;
       
  6633             }
       
  6634 
       
  6635             // get character and increment cursor and column
       
  6636             c = this._input.charAt(this._cursor++);
       
  6637         }
       
  6638 
       
  6639         return c;
       
  6640     },
       
  6641 
       
  6642     //-------------------------------------------------------------------------
       
  6643     // Misc
       
  6644     //-------------------------------------------------------------------------
       
  6645 
       
  6646     /**
       
  6647      * Saves the current location so it can be returned to later.
       
  6648      * @method mark
       
  6649      * @return {void}
       
  6650      */
       
  6651     mark: function() {
       
  6652         this._bookmark = {
       
  6653             cursor: this._cursor,
       
  6654             line:   this._line,
       
  6655             col:    this._col
       
  6656         };
       
  6657     },
       
  6658 
       
  6659     reset: function() {
       
  6660         if (this._bookmark) {
       
  6661             this._cursor = this._bookmark.cursor;
       
  6662             this._line = this._bookmark.line;
       
  6663             this._col = this._bookmark.col;
       
  6664             delete this._bookmark;
       
  6665         }
       
  6666     },
       
  6667 
       
  6668     //-------------------------------------------------------------------------
       
  6669     // Advanced reading
       
  6670     //-------------------------------------------------------------------------
       
  6671 
       
  6672     /**
       
  6673      * Reads up to and including the given string. Throws an error if that
       
  6674      * string is not found.
       
  6675      * @param {String} pattern The string to read.
       
  6676      * @return {String} The string when it is found.
       
  6677      * @throws Error when the string pattern is not found.
       
  6678      * @method readTo
       
  6679      */
       
  6680     readTo: function(pattern) {
       
  6681 
       
  6682         var buffer = "",
       
  6683             c;
       
  6684 
       
  6685         /*
       
  6686          * First, buffer must be the same length as the pattern.
       
  6687          * Then, buffer must end with the pattern or else reach the
       
  6688          * end of the input.
       
  6689          */
       
  6690         while (buffer.length < pattern.length || buffer.lastIndexOf(pattern) !== buffer.length - pattern.length) {
       
  6691             c = this.read();
       
  6692             if (c) {
       
  6693                 buffer += c;
       
  6694             } else {
       
  6695                 throw new Error("Expected \"" + pattern + "\" at line " + this._line  + ", col " + this._col + ".");
       
  6696             }
       
  6697         }
       
  6698 
       
  6699         return buffer;
       
  6700 
       
  6701     },
       
  6702 
       
  6703     /**
       
  6704      * Reads characters while each character causes the given
       
  6705      * filter function to return true. The function is passed
       
  6706      * in each character and either returns true to continue
       
  6707      * reading or false to stop.
       
  6708      * @param {Function} filter The function to read on each character.
       
  6709      * @return {String} The string made up of all characters that passed the
       
  6710      *      filter check.
       
  6711      * @method readWhile
       
  6712      */
       
  6713     readWhile: function(filter) {
       
  6714 
       
  6715         var buffer = "",
       
  6716             c = this.peek();
       
  6717 
       
  6718         while (c !== null && filter(c)) {
       
  6719             buffer += this.read();
       
  6720             c = this.peek();
       
  6721         }
       
  6722 
       
  6723         return buffer;
       
  6724 
       
  6725     },
       
  6726 
       
  6727     /**
       
  6728      * Reads characters that match either text or a regular expression and
       
  6729      * returns those characters. If a match is found, the row and column
       
  6730      * are adjusted; if no match is found, the reader's state is unchanged.
       
  6731      * reading or false to stop.
       
  6732      * @param {String|RegExp} matcher If a string, then the literal string
       
  6733      *      value is searched for. If a regular expression, then any string
       
  6734      *      matching the pattern is search for.
       
  6735      * @return {String} The string made up of all characters that matched or
       
  6736      *      null if there was no match.
       
  6737      * @method readMatch
       
  6738      */
       
  6739     readMatch: function(matcher) {
       
  6740 
       
  6741         var source = this._input.substring(this._cursor),
       
  6742             value = null;
       
  6743 
       
  6744         // if it's a string, just do a straight match
       
  6745         if (typeof matcher === "string") {
       
  6746             if (source.slice(0, matcher.length) === matcher) {
       
  6747                 value = this.readCount(matcher.length);
       
  6748             }
       
  6749         } else if (matcher instanceof RegExp) {
       
  6750             if (matcher.test(source)) {
       
  6751                 value = this.readCount(RegExp.lastMatch.length);
       
  6752             }
       
  6753         }
       
  6754 
       
  6755         return value;
       
  6756     },
       
  6757 
       
  6758 
       
  6759     /**
       
  6760      * Reads a given number of characters. If the end of the input is reached,
       
  6761      * it reads only the remaining characters and does not throw an error.
       
  6762      * @param {int} count The number of characters to read.
       
  6763      * @return {String} The string made up the read characters.
       
  6764      * @method readCount
       
  6765      */
       
  6766     readCount: function(count) {
       
  6767         var buffer = "";
       
  6768 
       
  6769         while (count--) {
       
  6770             buffer += this.read();
       
  6771         }
       
  6772 
       
  6773         return buffer;
       
  6774     }
       
  6775 
       
  6776 };
       
  6777 
       
  6778 },{}],25:[function(require,module,exports){
       
  6779 "use strict";
       
  6780 
       
  6781 module.exports = SyntaxError;
       
  6782 
       
  6783 /**
       
  6784  * Type to use when a syntax error occurs.
       
  6785  * @class SyntaxError
       
  6786  * @namespace parserlib.util
       
  6787  * @constructor
       
  6788  * @param {String} message The error message.
       
  6789  * @param {int} line The line at which the error occurred.
       
  6790  * @param {int} col The column at which the error occurred.
       
  6791  */
       
  6792 function SyntaxError(message, line, col) {
       
  6793     Error.call(this);
       
  6794     this.name = this.constructor.name;
       
  6795 
       
  6796     /**
       
  6797      * The column at which the error occurred.
       
  6798      * @type int
       
  6799      * @property col
       
  6800      */
       
  6801     this.col = col;
       
  6802 
       
  6803     /**
       
  6804      * The line at which the error occurred.
       
  6805      * @type int
       
  6806      * @property line
       
  6807      */
       
  6808     this.line = line;
       
  6809 
       
  6810     /**
       
  6811      * The text representation of the unit.
       
  6812      * @type String
       
  6813      * @property text
       
  6814      */
       
  6815     this.message = message;
       
  6816 
       
  6817 }
       
  6818 
       
  6819 //inherit from Error
       
  6820 SyntaxError.prototype = Object.create(Error.prototype); // jshint ignore:line
       
  6821 SyntaxError.prototype.constructor = SyntaxError; // jshint ignore:line
       
  6822 
       
  6823 },{}],26:[function(require,module,exports){
       
  6824 "use strict";
       
  6825 
       
  6826 module.exports = SyntaxUnit;
       
  6827 
       
  6828 /**
       
  6829  * Base type to represent a single syntactic unit.
       
  6830  * @class SyntaxUnit
       
  6831  * @namespace parserlib.util
       
  6832  * @constructor
       
  6833  * @param {String} text The text of the unit.
       
  6834  * @param {int} line The line of text on which the unit resides.
       
  6835  * @param {int} col The column of text on which the unit resides.
       
  6836  */
       
  6837 function SyntaxUnit(text, line, col, type) {
       
  6838 
       
  6839 
       
  6840     /**
       
  6841      * The column of text on which the unit resides.
       
  6842      * @type int
       
  6843      * @property col
       
  6844      */
       
  6845     this.col = col;
       
  6846 
       
  6847     /**
       
  6848      * The line of text on which the unit resides.
       
  6849      * @type int
       
  6850      * @property line
       
  6851      */
       
  6852     this.line = line;
       
  6853 
       
  6854     /**
       
  6855      * The text representation of the unit.
       
  6856      * @type String
       
  6857      * @property text
       
  6858      */
       
  6859     this.text = text;
       
  6860 
       
  6861     /**
       
  6862      * The type of syntax unit.
       
  6863      * @type int
       
  6864      * @property type
       
  6865      */
       
  6866     this.type = type;
       
  6867 }
       
  6868 
       
  6869 /**
       
  6870  * Create a new syntax unit based solely on the given token.
       
  6871  * Convenience method for creating a new syntax unit when
       
  6872  * it represents a single token instead of multiple.
       
  6873  * @param {Object} token The token object to represent.
       
  6874  * @return {parserlib.util.SyntaxUnit} The object representing the token.
       
  6875  * @static
       
  6876  * @method fromToken
       
  6877  */
       
  6878 SyntaxUnit.fromToken = function(token) {
       
  6879     return new SyntaxUnit(token.value, token.startLine, token.startCol);
       
  6880 };
       
  6881 
       
  6882 SyntaxUnit.prototype = {
       
  6883 
       
  6884     //restore constructor
       
  6885     constructor: SyntaxUnit,
       
  6886 
       
  6887     /**
       
  6888      * Returns the text representation of the unit.
       
  6889      * @return {String} The text representation of the unit.
       
  6890      * @method valueOf
       
  6891      */
       
  6892     valueOf: function() {
       
  6893         return this.toString();
       
  6894     },
       
  6895 
       
  6896     /**
       
  6897      * Returns the text representation of the unit.
       
  6898      * @return {String} The text representation of the unit.
       
  6899      * @method toString
       
  6900      */
       
  6901     toString: function() {
       
  6902         return this.text;
       
  6903     }
       
  6904 
       
  6905 };
       
  6906 
       
  6907 },{}],27:[function(require,module,exports){
       
  6908 "use strict";
       
  6909 
       
  6910 module.exports = TokenStreamBase;
       
  6911 
       
  6912 var StringReader = require("./StringReader");
       
  6913 var SyntaxError = require("./SyntaxError");
       
  6914 
       
  6915 /**
       
  6916  * Generic TokenStream providing base functionality.
       
  6917  * @class TokenStreamBase
       
  6918  * @namespace parserlib.util
       
  6919  * @constructor
       
  6920  * @param {String|StringReader} input The text to tokenize or a reader from
       
  6921  *      which to read the input.
       
  6922  */
       
  6923 function TokenStreamBase(input, tokenData) {
       
  6924 
       
  6925     /**
       
  6926      * The string reader for easy access to the text.
       
  6927      * @type StringReader
       
  6928      * @property _reader
       
  6929      * @private
       
  6930      */
       
  6931     this._reader = new StringReader(input ? input.toString() : "");
       
  6932 
       
  6933     /**
       
  6934      * Token object for the last consumed token.
       
  6935      * @type Token
       
  6936      * @property _token
       
  6937      * @private
       
  6938      */
       
  6939     this._token = null;
       
  6940 
       
  6941     /**
       
  6942      * The array of token information.
       
  6943      * @type Array
       
  6944      * @property _tokenData
       
  6945      * @private
       
  6946      */
       
  6947     this._tokenData = tokenData;
       
  6948 
       
  6949     /**
       
  6950      * Lookahead token buffer.
       
  6951      * @type Array
       
  6952      * @property _lt
       
  6953      * @private
       
  6954      */
       
  6955     this._lt = [];
       
  6956 
       
  6957     /**
       
  6958      * Lookahead token buffer index.
       
  6959      * @type int
       
  6960      * @property _ltIndex
       
  6961      * @private
       
  6962      */
       
  6963     this._ltIndex = 0;
       
  6964 
       
  6965     this._ltIndexCache = [];
       
  6966 }
       
  6967 
       
  6968 /**
       
  6969  * Accepts an array of token information and outputs
       
  6970  * an array of token data containing key-value mappings
       
  6971  * and matching functions that the TokenStream needs.
       
  6972  * @param {Array} tokens An array of token descriptors.
       
  6973  * @return {Array} An array of processed token data.
       
  6974  * @method createTokenData
       
  6975  * @static
       
  6976  */
       
  6977 TokenStreamBase.createTokenData = function(tokens) {
       
  6978 
       
  6979     var nameMap     = [],
       
  6980         typeMap     = Object.create(null),
       
  6981         tokenData     = tokens.concat([]),
       
  6982         i            = 0,
       
  6983         len            = tokenData.length+1;
       
  6984 
       
  6985     tokenData.UNKNOWN = -1;
       
  6986     tokenData.unshift({ name:"EOF" });
       
  6987 
       
  6988     for (; i < len; i++) {
       
  6989         nameMap.push(tokenData[i].name);
       
  6990         tokenData[tokenData[i].name] = i;
       
  6991         if (tokenData[i].text) {
       
  6992             typeMap[tokenData[i].text] = i;
       
  6993         }
       
  6994     }
       
  6995 
       
  6996     tokenData.name = function(tt) {
       
  6997         return nameMap[tt];
       
  6998     };
       
  6999 
       
  7000     tokenData.type = function(c) {
       
  7001         return typeMap[c];
       
  7002     };
       
  7003 
       
  7004     return tokenData;
       
  7005 };
       
  7006 
       
  7007 TokenStreamBase.prototype = {
       
  7008 
       
  7009     //restore constructor
       
  7010     constructor: TokenStreamBase,
       
  7011 
       
  7012     //-------------------------------------------------------------------------
       
  7013     // Matching methods
       
  7014     //-------------------------------------------------------------------------
       
  7015 
       
  7016     /**
       
  7017      * Determines if the next token matches the given token type.
       
  7018      * If so, that token is consumed; if not, the token is placed
       
  7019      * back onto the token stream. You can pass in any number of
       
  7020      * token types and this will return true if any of the token
       
  7021      * types is found.
       
  7022      * @param {int|int[]} tokenTypes Either a single token type or an array of
       
  7023      *      token types that the next token might be. If an array is passed,
       
  7024      *      it's assumed that the token can be any of these.
       
  7025      * @param {variant} channel (Optional) The channel to read from. If not
       
  7026      *      provided, reads from the default (unnamed) channel.
       
  7027      * @return {Boolean} True if the token type matches, false if not.
       
  7028      * @method match
       
  7029      */
       
  7030     match: function(tokenTypes, channel) {
       
  7031 
       
  7032         //always convert to an array, makes things easier
       
  7033         if (!(tokenTypes instanceof Array)) {
       
  7034             tokenTypes = [tokenTypes];
       
  7035         }
       
  7036 
       
  7037         var tt  = this.get(channel),
       
  7038             i   = 0,
       
  7039             len = tokenTypes.length;
       
  7040 
       
  7041         while (i < len) {
       
  7042             if (tt === tokenTypes[i++]) {
       
  7043                 return true;
       
  7044             }
       
  7045         }
       
  7046 
       
  7047         //no match found, put the token back
       
  7048         this.unget();
       
  7049         return false;
       
  7050     },
       
  7051 
       
  7052     /**
       
  7053      * Determines if the next token matches the given token type.
       
  7054      * If so, that token is consumed; if not, an error is thrown.
       
  7055      * @param {int|int[]} tokenTypes Either a single token type or an array of
       
  7056      *      token types that the next token should be. If an array is passed,
       
  7057      *      it's assumed that the token must be one of these.
       
  7058      * @return {void}
       
  7059      * @method mustMatch
       
  7060      */
       
  7061     mustMatch: function(tokenTypes) {
       
  7062 
       
  7063         var token;
       
  7064 
       
  7065         //always convert to an array, makes things easier
       
  7066         if (!(tokenTypes instanceof Array)) {
       
  7067             tokenTypes = [tokenTypes];
       
  7068         }
       
  7069 
       
  7070         if (!this.match.apply(this, arguments)) {
       
  7071             token = this.LT(1);
       
  7072             throw new SyntaxError("Expected " + this._tokenData[tokenTypes[0]].name +
       
  7073                 " at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
       
  7074         }
       
  7075     },
       
  7076 
       
  7077     //-------------------------------------------------------------------------
       
  7078     // Consuming methods
       
  7079     //-------------------------------------------------------------------------
       
  7080 
       
  7081     /**
       
  7082      * Keeps reading from the token stream until either one of the specified
       
  7083      * token types is found or until the end of the input is reached.
       
  7084      * @param {int|int[]} tokenTypes Either a single token type or an array of
       
  7085      *      token types that the next token should be. If an array is passed,
       
  7086      *      it's assumed that the token must be one of these.
       
  7087      * @param {variant} channel (Optional) The channel to read from. If not
       
  7088      *      provided, reads from the default (unnamed) channel.
       
  7089      * @return {void}
       
  7090      * @method advance
       
  7091      */
       
  7092     advance: function(tokenTypes, channel) {
       
  7093 
       
  7094         while (this.LA(0) !== 0 && !this.match(tokenTypes, channel)) {
       
  7095             this.get();
       
  7096         }
       
  7097 
       
  7098         return this.LA(0);
       
  7099     },
       
  7100 
       
  7101     /**
       
  7102      * Consumes the next token from the token stream.
       
  7103      * @return {int} The token type of the token that was just consumed.
       
  7104      * @method get
       
  7105      */
       
  7106     get: function(channel) {
       
  7107 
       
  7108         var tokenInfo   = this._tokenData,
       
  7109             i           =0,
       
  7110             token,
       
  7111             info;
       
  7112 
       
  7113         //check the lookahead buffer first
       
  7114         if (this._lt.length && this._ltIndex >= 0 && this._ltIndex < this._lt.length) {
       
  7115 
       
  7116             i++;
       
  7117             this._token = this._lt[this._ltIndex++];
       
  7118             info = tokenInfo[this._token.type];
       
  7119 
       
  7120             //obey channels logic
       
  7121             while ((info.channel !== undefined && channel !== info.channel) &&
       
  7122                     this._ltIndex < this._lt.length) {
       
  7123                 this._token = this._lt[this._ltIndex++];
       
  7124                 info = tokenInfo[this._token.type];
       
  7125                 i++;
       
  7126             }
       
  7127 
       
  7128             //here be dragons
       
  7129             if ((info.channel === undefined || channel === info.channel) &&
       
  7130                     this._ltIndex <= this._lt.length) {
       
  7131                 this._ltIndexCache.push(i);
       
  7132                 return this._token.type;
       
  7133             }
       
  7134         }
       
  7135 
       
  7136         //call token retriever method
       
  7137         token = this._getToken();
       
  7138 
       
  7139         //if it should be hidden, don't save a token
       
  7140         if (token.type > -1 && !tokenInfo[token.type].hide) {
       
  7141 
       
  7142             //apply token channel
       
  7143             token.channel = tokenInfo[token.type].channel;
       
  7144 
       
  7145             //save for later
       
  7146             this._token = token;
       
  7147             this._lt.push(token);
       
  7148 
       
  7149             //save space that will be moved (must be done before array is truncated)
       
  7150             this._ltIndexCache.push(this._lt.length - this._ltIndex + i);
       
  7151 
       
  7152             //keep the buffer under 5 items
       
  7153             if (this._lt.length > 5) {
       
  7154                 this._lt.shift();
       
  7155             }
       
  7156 
       
  7157             //also keep the shift buffer under 5 items
       
  7158             if (this._ltIndexCache.length > 5) {
       
  7159                 this._ltIndexCache.shift();
       
  7160             }
       
  7161 
       
  7162             //update lookahead index
       
  7163             this._ltIndex = this._lt.length;
       
  7164         }
       
  7165 
       
  7166         /*
       
  7167          * Skip to the next token if:
       
  7168          * 1. The token type is marked as hidden.
       
  7169          * 2. The token type has a channel specified and it isn't the current channel.
       
  7170          */
       
  7171         info = tokenInfo[token.type];
       
  7172         if (info &&
       
  7173                 (info.hide ||
       
  7174                 (info.channel !== undefined && channel !== info.channel))) {
       
  7175             return this.get(channel);
       
  7176         } else {
       
  7177             //return just the type
       
  7178             return token.type;
       
  7179         }
       
  7180     },
       
  7181 
       
  7182     /**
       
  7183      * Looks ahead a certain number of tokens and returns the token type at
       
  7184      * that position. This will throw an error if you lookahead past the
       
  7185      * end of input, past the size of the lookahead buffer, or back past
       
  7186      * the first token in the lookahead buffer.
       
  7187      * @param {int} The index of the token type to retrieve. 0 for the
       
  7188      *      current token, 1 for the next, -1 for the previous, etc.
       
  7189      * @return {int} The token type of the token in the given position.
       
  7190      * @method LA
       
  7191      */
       
  7192     LA: function(index) {
       
  7193         var total = index,
       
  7194             tt;
       
  7195         if (index > 0) {
       
  7196             //TODO: Store 5 somewhere
       
  7197             if (index > 5) {
       
  7198                 throw new Error("Too much lookahead.");
       
  7199             }
       
  7200 
       
  7201             //get all those tokens
       
  7202             while (total) {
       
  7203                 tt = this.get();
       
  7204                 total--;
       
  7205             }
       
  7206 
       
  7207             //unget all those tokens
       
  7208             while (total < index) {
       
  7209                 this.unget();
       
  7210                 total++;
       
  7211             }
       
  7212         } else if (index < 0) {
       
  7213 
       
  7214             if (this._lt[this._ltIndex+index]) {
       
  7215                 tt = this._lt[this._ltIndex+index].type;
       
  7216             } else {
       
  7217                 throw new Error("Too much lookbehind.");
       
  7218             }
       
  7219 
       
  7220         } else {
       
  7221             tt = this._token.type;
       
  7222         }
       
  7223 
       
  7224         return tt;
       
  7225 
       
  7226     },
       
  7227 
       
  7228     /**
       
  7229      * Looks ahead a certain number of tokens and returns the token at
       
  7230      * that position. This will throw an error if you lookahead past the
       
  7231      * end of input, past the size of the lookahead buffer, or back past
       
  7232      * the first token in the lookahead buffer.
       
  7233      * @param {int} The index of the token type to retrieve. 0 for the
       
  7234      *      current token, 1 for the next, -1 for the previous, etc.
       
  7235      * @return {Object} The token of the token in the given position.
       
  7236      * @method LA
       
  7237      */
       
  7238     LT: function(index) {
       
  7239 
       
  7240         //lookahead first to prime the token buffer
       
  7241         this.LA(index);
       
  7242 
       
  7243         //now find the token, subtract one because _ltIndex is already at the next index
       
  7244         return this._lt[this._ltIndex+index-1];
       
  7245     },
       
  7246 
       
  7247     /**
       
  7248      * Returns the token type for the next token in the stream without
       
  7249      * consuming it.
       
  7250      * @return {int} The token type of the next token in the stream.
       
  7251      * @method peek
       
  7252      */
       
  7253     peek: function() {
       
  7254         return this.LA(1);
       
  7255     },
       
  7256 
       
  7257     /**
       
  7258      * Returns the actual token object for the last consumed token.
       
  7259      * @return {Token} The token object for the last consumed token.
       
  7260      * @method token
       
  7261      */
       
  7262     token: function() {
       
  7263         return this._token;
       
  7264     },
       
  7265 
       
  7266     /**
       
  7267      * Returns the name of the token for the given token type.
       
  7268      * @param {int} tokenType The type of token to get the name of.
       
  7269      * @return {String} The name of the token or "UNKNOWN_TOKEN" for any
       
  7270      *      invalid token type.
       
  7271      * @method tokenName
       
  7272      */
       
  7273     tokenName: function(tokenType) {
       
  7274         if (tokenType < 0 || tokenType > this._tokenData.length) {
       
  7275             return "UNKNOWN_TOKEN";
       
  7276         } else {
       
  7277             return this._tokenData[tokenType].name;
       
  7278         }
       
  7279     },
       
  7280 
       
  7281     /**
       
  7282      * Returns the token type value for the given token name.
       
  7283      * @param {String} tokenName The name of the token whose value should be returned.
       
  7284      * @return {int} The token type value for the given token name or -1
       
  7285      *      for an unknown token.
       
  7286      * @method tokenName
       
  7287      */
       
  7288     tokenType: function(tokenName) {
       
  7289         return this._tokenData[tokenName] || -1;
       
  7290     },
       
  7291 
       
  7292     /**
       
  7293      * Returns the last consumed token to the token stream.
       
  7294      * @method unget
       
  7295      */
       
  7296     unget: function() {
       
  7297         //if (this._ltIndex > -1) {
       
  7298         if (this._ltIndexCache.length) {
       
  7299             this._ltIndex -= this._ltIndexCache.pop();//--;
       
  7300             this._token = this._lt[this._ltIndex - 1];
       
  7301         } else {
       
  7302             throw new Error("Too much lookahead.");
       
  7303         }
       
  7304     }
       
  7305 
       
  7306 };
       
  7307 
       
  7308 
       
  7309 },{"./StringReader":24,"./SyntaxError":25}],28:[function(require,module,exports){
       
  7310 "use strict";
       
  7311 
       
  7312 module.exports = {
       
  7313     StringReader    : require("./StringReader"),
       
  7314     SyntaxError     : require("./SyntaxError"),
       
  7315     SyntaxUnit      : require("./SyntaxUnit"),
       
  7316     EventTarget     : require("./EventTarget"),
       
  7317     TokenStreamBase : require("./TokenStreamBase")
       
  7318 };
       
  7319 
       
  7320 },{"./EventTarget":23,"./StringReader":24,"./SyntaxError":25,"./SyntaxUnit":26,"./TokenStreamBase":27}],"parserlib":[function(require,module,exports){
       
  7321 "use strict";
       
  7322 
       
  7323 module.exports = {
       
  7324     css  : require("./css"),
       
  7325     util : require("./util")
       
  7326 };
       
  7327 
       
  7328 },{"./css":22,"./util":28}]},{},[]);
       
  7329 
       
  7330 return require('parserlib');
       
  7331 })();
       
  7332 var clone = (function() {
       
  7333 'use strict';
       
  7334 
       
  7335 var nativeMap;
       
  7336 try {
       
  7337   nativeMap = Map;
       
  7338 } catch(_) {
       
  7339   // maybe a reference error because no `Map`. Give it a dummy value that no
       
  7340   // value will ever be an instanceof.
       
  7341   nativeMap = function() {};
       
  7342 }
       
  7343 
       
  7344 var nativeSet;
       
  7345 try {
       
  7346   nativeSet = Set;
       
  7347 } catch(_) {
       
  7348   nativeSet = function() {};
       
  7349 }
       
  7350 
       
  7351 var nativePromise;
       
  7352 try {
       
  7353   nativePromise = Promise;
       
  7354 } catch(_) {
       
  7355   nativePromise = function() {};
       
  7356 }
       
  7357 
       
  7358 /**
       
  7359  * Clones (copies) an Object using deep copying.
       
  7360  *
       
  7361  * This function supports circular references by default, but if you are certain
       
  7362  * there are no circular references in your object, you can save some CPU time
       
  7363  * by calling clone(obj, false).
       
  7364  *
       
  7365  * Caution: if `circular` is false and `parent` contains circular references,
       
  7366  * your program may enter an infinite loop and crash.
       
  7367  *
       
  7368  * @param `parent` - the object to be cloned
       
  7369  * @param `circular` - set to true if the object to be cloned may contain
       
  7370  *    circular references. (optional - true by default)
       
  7371  * @param `depth` - set to a number if the object is only to be cloned to
       
  7372  *    a particular depth. (optional - defaults to Infinity)
       
  7373  * @param `prototype` - sets the prototype to be used when cloning an object.
       
  7374  *    (optional - defaults to parent prototype).
       
  7375  * @param `includeNonEnumerable` - set to true if the non-enumerable properties
       
  7376  *    should be cloned as well. Non-enumerable properties on the prototype
       
  7377  *    chain will be ignored. (optional - false by default)
       
  7378 */
       
  7379 function clone(parent, circular, depth, prototype, includeNonEnumerable) {
       
  7380   if (typeof circular === 'object') {
       
  7381     depth = circular.depth;
       
  7382     prototype = circular.prototype;
       
  7383     includeNonEnumerable = circular.includeNonEnumerable;
       
  7384     circular = circular.circular;
       
  7385   }
       
  7386   // maintain two arrays for circular references, where corresponding parents
       
  7387   // and children have the same index
       
  7388   var allParents = [];
       
  7389   var allChildren = [];
       
  7390 
       
  7391   var useBuffer = typeof Buffer != 'undefined';
       
  7392 
       
  7393   if (typeof circular == 'undefined')
       
  7394     circular = true;
       
  7395 
       
  7396   if (typeof depth == 'undefined')
       
  7397     depth = Infinity;
       
  7398 
       
  7399   // recurse this function so we don't reset allParents and allChildren
       
  7400   function _clone(parent, depth) {
       
  7401     // cloning null always returns null
       
  7402     if (parent === null)
       
  7403       return null;
       
  7404 
       
  7405     if (depth === 0)
       
  7406       return parent;
       
  7407 
       
  7408     var child;
       
  7409     var proto;
       
  7410     if (typeof parent != 'object') {
       
  7411       return parent;
       
  7412     }
       
  7413 
       
  7414     if (parent instanceof nativeMap) {
       
  7415       child = new nativeMap();
       
  7416     } else if (parent instanceof nativeSet) {
       
  7417       child = new nativeSet();
       
  7418     } else if (parent instanceof nativePromise) {
       
  7419       child = new nativePromise(function (resolve, reject) {
       
  7420         parent.then(function(value) {
       
  7421           resolve(_clone(value, depth - 1));
       
  7422         }, function(err) {
       
  7423           reject(_clone(err, depth - 1));
       
  7424         });
       
  7425       });
       
  7426     } else if (clone.__isArray(parent)) {
       
  7427       child = [];
       
  7428     } else if (clone.__isRegExp(parent)) {
       
  7429       child = new RegExp(parent.source, __getRegExpFlags(parent));
       
  7430       if (parent.lastIndex) child.lastIndex = parent.lastIndex;
       
  7431     } else if (clone.__isDate(parent)) {
       
  7432       child = new Date(parent.getTime());
       
  7433     } else if (useBuffer && Buffer.isBuffer(parent)) {
       
  7434       child = new Buffer(parent.length);
       
  7435       parent.copy(child);
       
  7436       return child;
       
  7437     } else if (parent instanceof Error) {
       
  7438       child = Object.create(parent);
       
  7439     } else {
       
  7440       if (typeof prototype == 'undefined') {
       
  7441         proto = Object.getPrototypeOf(parent);
       
  7442         child = Object.create(proto);
       
  7443       }
       
  7444       else {
       
  7445         child = Object.create(prototype);
       
  7446         proto = prototype;
       
  7447       }
       
  7448     }
       
  7449 
       
  7450     if (circular) {
       
  7451       var index = allParents.indexOf(parent);
       
  7452 
       
  7453       if (index != -1) {
       
  7454         return allChildren[index];
       
  7455       }
       
  7456       allParents.push(parent);
       
  7457       allChildren.push(child);
       
  7458     }
       
  7459 
       
  7460     if (parent instanceof nativeMap) {
       
  7461       var keyIterator = parent.keys();
       
  7462       while(true) {
       
  7463         var next = keyIterator.next();
       
  7464         if (next.done) {
       
  7465           break;
       
  7466         }
       
  7467         var keyChild = _clone(next.value, depth - 1);
       
  7468         var valueChild = _clone(parent.get(next.value), depth - 1);
       
  7469         child.set(keyChild, valueChild);
       
  7470       }
       
  7471     }
       
  7472     if (parent instanceof nativeSet) {
       
  7473       var iterator = parent.keys();
       
  7474       while(true) {
       
  7475         var next = iterator.next();
       
  7476         if (next.done) {
       
  7477           break;
       
  7478         }
       
  7479         var entryChild = _clone(next.value, depth - 1);
       
  7480         child.add(entryChild);
       
  7481       }
       
  7482     }
       
  7483 
       
  7484     for (var i in parent) {
       
  7485       var attrs;
       
  7486       if (proto) {
       
  7487         attrs = Object.getOwnPropertyDescriptor(proto, i);
       
  7488       }
       
  7489 
       
  7490       if (attrs && attrs.set == null) {
       
  7491         continue;
       
  7492       }
       
  7493       child[i] = _clone(parent[i], depth - 1);
       
  7494     }
       
  7495 
       
  7496     if (Object.getOwnPropertySymbols) {
       
  7497       var symbols = Object.getOwnPropertySymbols(parent);
       
  7498       for (var i = 0; i < symbols.length; i++) {
       
  7499         // Don't need to worry about cloning a symbol because it is a primitive,
       
  7500         // like a number or string.
       
  7501         var symbol = symbols[i];
       
  7502         var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
       
  7503         if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
       
  7504           continue;
       
  7505         }
       
  7506         child[symbol] = _clone(parent[symbol], depth - 1);
       
  7507         if (!descriptor.enumerable) {
       
  7508           Object.defineProperty(child, symbol, {
       
  7509             enumerable: false
       
  7510           });
       
  7511         }
       
  7512       }
       
  7513     }
       
  7514 
       
  7515     if (includeNonEnumerable) {
       
  7516       var allPropertyNames = Object.getOwnPropertyNames(parent);
       
  7517       for (var i = 0; i < allPropertyNames.length; i++) {
       
  7518         var propertyName = allPropertyNames[i];
       
  7519         var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
       
  7520         if (descriptor && descriptor.enumerable) {
       
  7521           continue;
       
  7522         }
       
  7523         child[propertyName] = _clone(parent[propertyName], depth - 1);
       
  7524         Object.defineProperty(child, propertyName, {
       
  7525           enumerable: false
       
  7526         });
       
  7527       }
       
  7528     }
       
  7529 
       
  7530     return child;
       
  7531   }
       
  7532 
       
  7533   return _clone(parent, depth);
       
  7534 }
       
  7535 
       
  7536 /**
       
  7537  * Simple flat clone using prototype, accepts only objects, usefull for property
       
  7538  * override on FLAT configuration object (no nested props).
       
  7539  *
       
  7540  * USE WITH CAUTION! This may not behave as you wish if you do not know how this
       
  7541  * works.
       
  7542  */
       
  7543 clone.clonePrototype = function clonePrototype(parent) {
       
  7544   if (parent === null)
       
  7545     return null;
       
  7546 
       
  7547   var c = function () {};
       
  7548   c.prototype = parent;
       
  7549   return new c();
       
  7550 };
       
  7551 
       
  7552 // private utility functions
       
  7553 
       
  7554 function __objToStr(o) {
       
  7555   return Object.prototype.toString.call(o);
       
  7556 }
       
  7557 clone.__objToStr = __objToStr;
       
  7558 
       
  7559 function __isDate(o) {
       
  7560   return typeof o === 'object' && __objToStr(o) === '[object Date]';
       
  7561 }
       
  7562 clone.__isDate = __isDate;
       
  7563 
       
  7564 function __isArray(o) {
       
  7565   return typeof o === 'object' && __objToStr(o) === '[object Array]';
       
  7566 }
       
  7567 clone.__isArray = __isArray;
       
  7568 
       
  7569 function __isRegExp(o) {
       
  7570   return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
       
  7571 }
       
  7572 clone.__isRegExp = __isRegExp;
       
  7573 
       
  7574 function __getRegExpFlags(re) {
       
  7575   var flags = '';
       
  7576   if (re.global) flags += 'g';
       
  7577   if (re.ignoreCase) flags += 'i';
       
  7578   if (re.multiline) flags += 'm';
       
  7579   return flags;
       
  7580 }
       
  7581 clone.__getRegExpFlags = __getRegExpFlags;
       
  7582 
       
  7583 return clone;
       
  7584 })();
       
  7585 
       
  7586 if (typeof module === 'object' && module.exports) {
       
  7587   module.exports = clone;
       
  7588 }
       
  7589 
       
  7590 /**
       
  7591  * Main CSSLint object.
       
  7592  * @class CSSLint
       
  7593  * @static
       
  7594  * @extends parserlib.util.EventTarget
       
  7595  */
       
  7596 
       
  7597 /* global parserlib, clone, Reporter */
       
  7598 /* exported CSSLint */
       
  7599 
       
  7600 var CSSLint = (function() {
       
  7601     "use strict";
       
  7602 
       
  7603     var rules           = [],
       
  7604         formatters      = [],
       
  7605         embeddedRuleset = /\/\*\s*csslint([^\*]*)\*\//,
       
  7606         api             = new parserlib.util.EventTarget();
       
  7607 
       
  7608     api.version = "1.0.4";
       
  7609 
       
  7610     //-------------------------------------------------------------------------
       
  7611     // Rule Management
       
  7612     //-------------------------------------------------------------------------
       
  7613 
       
  7614     /**
       
  7615      * Adds a new rule to the engine.
       
  7616      * @param {Object} rule The rule to add.
       
  7617      * @method addRule
       
  7618      */
       
  7619     api.addRule = function(rule) {
       
  7620         rules.push(rule);
       
  7621         rules[rule.id] = rule;
       
  7622     };
       
  7623 
       
  7624     /**
       
  7625      * Clears all rule from the engine.
       
  7626      * @method clearRules
       
  7627      */
       
  7628     api.clearRules = function() {
       
  7629         rules = [];
       
  7630     };
       
  7631 
       
  7632     /**
       
  7633      * Returns the rule objects.
       
  7634      * @return An array of rule objects.
       
  7635      * @method getRules
       
  7636      */
       
  7637     api.getRules = function() {
       
  7638         return [].concat(rules).sort(function(a, b) {
       
  7639             return a.id > b.id ? 1 : 0;
       
  7640         });
       
  7641     };
       
  7642 
       
  7643     /**
       
  7644      * Returns a ruleset configuration object with all current rules.
       
  7645      * @return A ruleset object.
       
  7646      * @method getRuleset
       
  7647      */
       
  7648     api.getRuleset = function() {
       
  7649         var ruleset = {},
       
  7650             i = 0,
       
  7651             len = rules.length;
       
  7652 
       
  7653         while (i < len) {
       
  7654             ruleset[rules[i++].id] = 1;    // by default, everything is a warning
       
  7655         }
       
  7656 
       
  7657         return ruleset;
       
  7658     };
       
  7659 
       
  7660     /**
       
  7661      * Returns a ruleset object based on embedded rules.
       
  7662      * @param {String} text A string of css containing embedded rules.
       
  7663      * @param {Object} ruleset A ruleset object to modify.
       
  7664      * @return {Object} A ruleset object.
       
  7665      * @method getEmbeddedRuleset
       
  7666      */
       
  7667     function applyEmbeddedRuleset(text, ruleset) {
       
  7668         var valueMap,
       
  7669             embedded = text && text.match(embeddedRuleset),
       
  7670             rules = embedded && embedded[1];
       
  7671 
       
  7672         if (rules) {
       
  7673             valueMap = {
       
  7674                 "true": 2,  // true is error
       
  7675                 "": 1,      // blank is warning
       
  7676                 "false": 0, // false is ignore
       
  7677 
       
  7678                 "2": 2,     // explicit error
       
  7679                 "1": 1,     // explicit warning
       
  7680                 "0": 0      // explicit ignore
       
  7681             };
       
  7682 
       
  7683             rules.toLowerCase().split(",").forEach(function(rule) {
       
  7684                 var pair = rule.split(":"),
       
  7685                     property = pair[0] || "",
       
  7686                     value = pair[1] || "";
       
  7687 
       
  7688                 ruleset[property.trim()] = valueMap[value.trim()];
       
  7689             });
       
  7690         }
       
  7691 
       
  7692         return ruleset;
       
  7693     }
       
  7694 
       
  7695     //-------------------------------------------------------------------------
       
  7696     // Formatters
       
  7697     //-------------------------------------------------------------------------
       
  7698 
       
  7699     /**
       
  7700      * Adds a new formatter to the engine.
       
  7701      * @param {Object} formatter The formatter to add.
       
  7702      * @method addFormatter
       
  7703      */
       
  7704     api.addFormatter = function(formatter) {
       
  7705         // formatters.push(formatter);
       
  7706         formatters[formatter.id] = formatter;
       
  7707     };
       
  7708 
       
  7709     /**
       
  7710      * Retrieves a formatter for use.
       
  7711      * @param {String} formatId The name of the format to retrieve.
       
  7712      * @return {Object} The formatter or undefined.
       
  7713      * @method getFormatter
       
  7714      */
       
  7715     api.getFormatter = function(formatId) {
       
  7716         return formatters[formatId];
       
  7717     };
       
  7718 
       
  7719     /**
       
  7720      * Formats the results in a particular format for a single file.
       
  7721      * @param {Object} result The results returned from CSSLint.verify().
       
  7722      * @param {String} filename The filename for which the results apply.
       
  7723      * @param {String} formatId The name of the formatter to use.
       
  7724      * @param {Object} options (Optional) for special output handling.
       
  7725      * @return {String} A formatted string for the results.
       
  7726      * @method format
       
  7727      */
       
  7728     api.format = function(results, filename, formatId, options) {
       
  7729         var formatter = this.getFormatter(formatId),
       
  7730             result = null;
       
  7731 
       
  7732         if (formatter) {
       
  7733             result = formatter.startFormat();
       
  7734             result += formatter.formatResults(results, filename, options || {});
       
  7735             result += formatter.endFormat();
       
  7736         }
       
  7737 
       
  7738         return result;
       
  7739     };
       
  7740 
       
  7741     /**
       
  7742      * Indicates if the given format is supported.
       
  7743      * @param {String} formatId The ID of the format to check.
       
  7744      * @return {Boolean} True if the format exists, false if not.
       
  7745      * @method hasFormat
       
  7746      */
       
  7747     api.hasFormat = function(formatId) {
       
  7748         return formatters.hasOwnProperty(formatId);
       
  7749     };
       
  7750 
       
  7751     //-------------------------------------------------------------------------
       
  7752     // Verification
       
  7753     //-------------------------------------------------------------------------
       
  7754 
       
  7755     /**
       
  7756      * Starts the verification process for the given CSS text.
       
  7757      * @param {String} text The CSS text to verify.
       
  7758      * @param {Object} ruleset (Optional) List of rules to apply. If null, then
       
  7759      *      all rules are used. If a rule has a value of 1 then it's a warning,
       
  7760      *      a value of 2 means it's an error.
       
  7761      * @return {Object} Results of the verification.
       
  7762      * @method verify
       
  7763      */
       
  7764     api.verify = function(text, ruleset) {
       
  7765 
       
  7766         var i = 0,
       
  7767             reporter,
       
  7768             lines,
       
  7769             allow = {},
       
  7770             ignore = [],
       
  7771             report,
       
  7772             parser = new parserlib.css.Parser({
       
  7773                 starHack: true,
       
  7774                 ieFilters: true,
       
  7775                 underscoreHack: true,
       
  7776                 strict: false
       
  7777             });
       
  7778 
       
  7779         // normalize line endings
       
  7780         lines = text.replace(/\n\r?/g, "$split$").split("$split$");
       
  7781 
       
  7782         // find 'allow' comments
       
  7783         CSSLint.Util.forEach(lines, function (line, lineno) {
       
  7784             var allowLine = line && line.match(/\/\*[ \t]*csslint[ \t]+allow:[ \t]*([^\*]*)\*\//i),
       
  7785                 allowRules = allowLine && allowLine[1],
       
  7786                 allowRuleset = {};
       
  7787 
       
  7788             if (allowRules) {
       
  7789                 allowRules.toLowerCase().split(",").forEach(function(allowRule) {
       
  7790                     allowRuleset[allowRule.trim()] = true;
       
  7791                 });
       
  7792                 if (Object.keys(allowRuleset).length > 0) {
       
  7793                     allow[lineno + 1] = allowRuleset;
       
  7794                 }
       
  7795             }
       
  7796         });
       
  7797 
       
  7798         var ignoreStart = null,
       
  7799             ignoreEnd = null;
       
  7800         CSSLint.Util.forEach(lines, function (line, lineno) {
       
  7801             // Keep oldest, "unclosest" ignore:start
       
  7802             if (ignoreStart === null && line.match(/\/\*[ \t]*csslint[ \t]+ignore:start[ \t]*\*\//i)) {
       
  7803                 ignoreStart = lineno;
       
  7804             }
       
  7805 
       
  7806             if (line.match(/\/\*[ \t]*csslint[ \t]+ignore:end[ \t]*\*\//i)) {
       
  7807                 ignoreEnd = lineno;
       
  7808             }
       
  7809 
       
  7810             if (ignoreStart !== null && ignoreEnd !== null) {
       
  7811                 ignore.push([ignoreStart, ignoreEnd]);
       
  7812                 ignoreStart = ignoreEnd = null;
       
  7813             }
       
  7814         });
       
  7815 
       
  7816         // Close remaining ignore block, if any
       
  7817         if (ignoreStart !== null) {
       
  7818             ignore.push([ignoreStart, lines.length]);
       
  7819         }
       
  7820 
       
  7821         if (!ruleset) {
       
  7822             ruleset = this.getRuleset();
       
  7823         }
       
  7824 
       
  7825         if (embeddedRuleset.test(text)) {
       
  7826             // defensively copy so that caller's version does not get modified
       
  7827             ruleset = clone(ruleset);
       
  7828             ruleset = applyEmbeddedRuleset(text, ruleset);
       
  7829         }
       
  7830 
       
  7831         reporter = new Reporter(lines, ruleset, allow, ignore);
       
  7832 
       
  7833         ruleset.errors = 2;       // always report parsing errors as errors
       
  7834         for (i in ruleset) {
       
  7835             if (ruleset.hasOwnProperty(i) && ruleset[i]) {
       
  7836                 if (rules[i]) {
       
  7837                     rules[i].init(parser, reporter);
       
  7838                 }
       
  7839             }
       
  7840         }
       
  7841 
       
  7842 
       
  7843         // capture most horrible error type
       
  7844         try {
       
  7845             parser.parse(text);
       
  7846         } catch (ex) {
       
  7847             reporter.error("Fatal error, cannot continue: " + ex.message, ex.line, ex.col, {});
       
  7848         }
       
  7849 
       
  7850         report = {
       
  7851             messages    : reporter.messages,
       
  7852             stats       : reporter.stats,
       
  7853             ruleset     : reporter.ruleset,
       
  7854             allow       : reporter.allow,
       
  7855             ignore      : reporter.ignore
       
  7856         };
       
  7857 
       
  7858         // sort by line numbers, rollups at the bottom
       
  7859         report.messages.sort(function (a, b) {
       
  7860             if (a.rollup && !b.rollup) {
       
  7861                 return 1;
       
  7862             } else if (!a.rollup && b.rollup) {
       
  7863                 return -1;
       
  7864             } else {
       
  7865                 return a.line - b.line;
       
  7866             }
       
  7867         });
       
  7868 
       
  7869         return report;
       
  7870     };
       
  7871 
       
  7872     //-------------------------------------------------------------------------
       
  7873     // Publish the API
       
  7874     //-------------------------------------------------------------------------
       
  7875 
       
  7876     return api;
       
  7877 
       
  7878 })();
       
  7879 
       
  7880 /**
       
  7881  * An instance of Report is used to report results of the
       
  7882  * verification back to the main API.
       
  7883  * @class Reporter
       
  7884  * @constructor
       
  7885  * @param {String[]} lines The text lines of the source.
       
  7886  * @param {Object} ruleset The set of rules to work with, including if
       
  7887  *      they are errors or warnings.
       
  7888  * @param {Object} explicitly allowed lines
       
  7889  * @param {[][]} ingore list of line ranges to be ignored
       
  7890  */
       
  7891 function Reporter(lines, ruleset, allow, ignore) {
       
  7892     "use strict";
       
  7893 
       
  7894     /**
       
  7895      * List of messages being reported.
       
  7896      * @property messages
       
  7897      * @type String[]
       
  7898      */
       
  7899     this.messages = [];
       
  7900 
       
  7901     /**
       
  7902      * List of statistics being reported.
       
  7903      * @property stats
       
  7904      * @type String[]
       
  7905      */
       
  7906     this.stats = [];
       
  7907 
       
  7908     /**
       
  7909      * Lines of code being reported on. Used to provide contextual information
       
  7910      * for messages.
       
  7911      * @property lines
       
  7912      * @type String[]
       
  7913      */
       
  7914     this.lines = lines;
       
  7915 
       
  7916     /**
       
  7917      * Information about the rules. Used to determine whether an issue is an
       
  7918      * error or warning.
       
  7919      * @property ruleset
       
  7920      * @type Object
       
  7921      */
       
  7922     this.ruleset = ruleset;
       
  7923 
       
  7924     /**
       
  7925      * Lines with specific rule messages to leave out of the report.
       
  7926      * @property allow
       
  7927      * @type Object
       
  7928      */
       
  7929     this.allow = allow;
       
  7930     if (!this.allow) {
       
  7931         this.allow = {};
       
  7932     }
       
  7933 
       
  7934     /**
       
  7935      * Linesets not to include in the report.
       
  7936      * @property ignore
       
  7937      * @type [][]
       
  7938      */
       
  7939     this.ignore = ignore;
       
  7940     if (!this.ignore) {
       
  7941         this.ignore = [];
       
  7942     }
       
  7943 }
       
  7944 
       
  7945 Reporter.prototype = {
       
  7946 
       
  7947     // restore constructor
       
  7948     constructor: Reporter,
       
  7949 
       
  7950     /**
       
  7951      * Report an error.
       
  7952      * @param {String} message The message to store.
       
  7953      * @param {int} line The line number.
       
  7954      * @param {int} col The column number.
       
  7955      * @param {Object} rule The rule this message relates to.
       
  7956      * @method error
       
  7957      */
       
  7958     error: function(message, line, col, rule) {
       
  7959         "use strict";
       
  7960         this.messages.push({
       
  7961             type    : "error",
       
  7962             line    : line,
       
  7963             col     : col,
       
  7964             message : message,
       
  7965             evidence: this.lines[line-1],
       
  7966             rule    : rule || {}
       
  7967         });
       
  7968     },
       
  7969 
       
  7970     /**
       
  7971      * Report an warning.
       
  7972      * @param {String} message The message to store.
       
  7973      * @param {int} line The line number.
       
  7974      * @param {int} col The column number.
       
  7975      * @param {Object} rule The rule this message relates to.
       
  7976      * @method warn
       
  7977      * @deprecated Use report instead.
       
  7978      */
       
  7979     warn: function(message, line, col, rule) {
       
  7980         "use strict";
       
  7981         this.report(message, line, col, rule);
       
  7982     },
       
  7983 
       
  7984     /**
       
  7985      * Report an issue.
       
  7986      * @param {String} message The message to store.
       
  7987      * @param {int} line The line number.
       
  7988      * @param {int} col The column number.
       
  7989      * @param {Object} rule The rule this message relates to.
       
  7990      * @method report
       
  7991      */
       
  7992     report: function(message, line, col, rule) {
       
  7993         "use strict";
       
  7994 
       
  7995         // Check if rule violation should be allowed
       
  7996         if (this.allow.hasOwnProperty(line) && this.allow[line].hasOwnProperty(rule.id)) {
       
  7997             return;
       
  7998         }
       
  7999 
       
  8000         var ignore = false;
       
  8001         CSSLint.Util.forEach(this.ignore, function (range) {
       
  8002             if (range[0] <= line && line <= range[1]) {
       
  8003                 ignore = true;
       
  8004             }
       
  8005         });
       
  8006         if (ignore) {
       
  8007             return;
       
  8008         }
       
  8009 
       
  8010         this.messages.push({
       
  8011             type    : this.ruleset[rule.id] === 2 ? "error" : "warning",
       
  8012             line    : line,
       
  8013             col     : col,
       
  8014             message : message,
       
  8015             evidence: this.lines[line-1],
       
  8016             rule    : rule
       
  8017         });
       
  8018     },
       
  8019 
       
  8020     /**
       
  8021      * Report some informational text.
       
  8022      * @param {String} message The message to store.
       
  8023      * @param {int} line The line number.
       
  8024      * @param {int} col The column number.
       
  8025      * @param {Object} rule The rule this message relates to.
       
  8026      * @method info
       
  8027      */
       
  8028     info: function(message, line, col, rule) {
       
  8029         "use strict";
       
  8030         this.messages.push({
       
  8031             type    : "info",
       
  8032             line    : line,
       
  8033             col     : col,
       
  8034             message : message,
       
  8035             evidence: this.lines[line-1],
       
  8036             rule    : rule
       
  8037         });
       
  8038     },
       
  8039 
       
  8040     /**
       
  8041      * Report some rollup error information.
       
  8042      * @param {String} message The message to store.
       
  8043      * @param {Object} rule The rule this message relates to.
       
  8044      * @method rollupError
       
  8045      */
       
  8046     rollupError: function(message, rule) {
       
  8047         "use strict";
       
  8048         this.messages.push({
       
  8049             type    : "error",
       
  8050             rollup  : true,
       
  8051             message : message,
       
  8052             rule    : rule
       
  8053         });
       
  8054     },
       
  8055 
       
  8056     /**
       
  8057      * Report some rollup warning information.
       
  8058      * @param {String} message The message to store.
       
  8059      * @param {Object} rule The rule this message relates to.
       
  8060      * @method rollupWarn
       
  8061      */
       
  8062     rollupWarn: function(message, rule) {
       
  8063         "use strict";
       
  8064         this.messages.push({
       
  8065             type    : "warning",
       
  8066             rollup  : true,
       
  8067             message : message,
       
  8068             rule    : rule
       
  8069         });
       
  8070     },
       
  8071 
       
  8072     /**
       
  8073      * Report a statistic.
       
  8074      * @param {String} name The name of the stat to store.
       
  8075      * @param {Variant} value The value of the stat.
       
  8076      * @method stat
       
  8077      */
       
  8078     stat: function(name, value) {
       
  8079         "use strict";
       
  8080         this.stats[name] = value;
       
  8081     }
       
  8082 };
       
  8083 
       
  8084 // expose for testing purposes
       
  8085 CSSLint._Reporter = Reporter;
       
  8086 
       
  8087 /*
       
  8088  * Utility functions that make life easier.
       
  8089  */
       
  8090 CSSLint.Util = {
       
  8091     /*
       
  8092      * Adds all properties from supplier onto receiver,
       
  8093      * overwriting if the same name already exists on
       
  8094      * receiver.
       
  8095      * @param {Object} The object to receive the properties.
       
  8096      * @param {Object} The object to provide the properties.
       
  8097      * @return {Object} The receiver
       
  8098      */
       
  8099     mix: function(receiver, supplier) {
       
  8100         "use strict";
       
  8101         var prop;
       
  8102 
       
  8103         for (prop in supplier) {
       
  8104             if (supplier.hasOwnProperty(prop)) {
       
  8105                 receiver[prop] = supplier[prop];
       
  8106             }
       
  8107         }
       
  8108 
       
  8109         return prop;
       
  8110     },
       
  8111 
       
  8112     /*
       
  8113      * Polyfill for array indexOf() method.
       
  8114      * @param {Array} values The array to search.
       
  8115      * @param {Variant} value The value to search for.
       
  8116      * @return {int} The index of the value if found, -1 if not.
       
  8117      */
       
  8118     indexOf: function(values, value) {
       
  8119         "use strict";
       
  8120         if (values.indexOf) {
       
  8121             return values.indexOf(value);
       
  8122         } else {
       
  8123             for (var i=0, len=values.length; i < len; i++) {
       
  8124                 if (values[i] === value) {
       
  8125                     return i;
       
  8126                 }
       
  8127             }
       
  8128             return -1;
       
  8129         }
       
  8130     },
       
  8131 
       
  8132     /*
       
  8133      * Polyfill for array forEach() method.
       
  8134      * @param {Array} values The array to operate on.
       
  8135      * @param {Function} func The function to call on each item.
       
  8136      * @return {void}
       
  8137      */
       
  8138     forEach: function(values, func) {
       
  8139         "use strict";
       
  8140         if (values.forEach) {
       
  8141             return values.forEach(func);
       
  8142         } else {
       
  8143             for (var i=0, len=values.length; i < len; i++) {
       
  8144                 func(values[i], i, values);
       
  8145             }
       
  8146         }
       
  8147     }
       
  8148 };
       
  8149 
       
  8150 /*
       
  8151  * Rule: Don't use adjoining classes (.foo.bar).
       
  8152  */
       
  8153 
       
  8154 CSSLint.addRule({
       
  8155 
       
  8156     // rule information
       
  8157     id: "adjoining-classes",
       
  8158     name: "Disallow adjoining classes",
       
  8159     desc: "Don't use adjoining classes.",
       
  8160     url: "https://github.com/CSSLint/csslint/wiki/Disallow-adjoining-classes",
       
  8161     browsers: "IE6",
       
  8162 
       
  8163     // initialization
       
  8164     init: function(parser, reporter) {
       
  8165         "use strict";
       
  8166         var rule = this;
       
  8167         parser.addListener("startrule", function(event) {
       
  8168             var selectors = event.selectors,
       
  8169                 selector,
       
  8170                 part,
       
  8171                 modifier,
       
  8172                 classCount,
       
  8173                 i, j, k;
       
  8174 
       
  8175             for (i=0; i < selectors.length; i++) {
       
  8176                 selector = selectors[i];
       
  8177                 for (j=0; j < selector.parts.length; j++) {
       
  8178                     part = selector.parts[j];
       
  8179                     if (part.type === parser.SELECTOR_PART_TYPE) {
       
  8180                         classCount = 0;
       
  8181                         for (k=0; k < part.modifiers.length; k++) {
       
  8182                             modifier = part.modifiers[k];
       
  8183                             if (modifier.type === "class") {
       
  8184                                 classCount++;
       
  8185                             }
       
  8186                             if (classCount > 1){
       
  8187                                 reporter.report("Adjoining classes: "+selectors[i].text, part.line, part.col, rule);
       
  8188                             }
       
  8189                         }
       
  8190                     }
       
  8191                 }
       
  8192             }
       
  8193         });
       
  8194     }
       
  8195 
       
  8196 });
       
  8197 
       
  8198 /*
       
  8199  * Rule: Don't use width or height when using padding or border.
       
  8200  */
       
  8201 CSSLint.addRule({
       
  8202 
       
  8203     // rule information
       
  8204     id: "box-model",
       
  8205     name: "Beware of broken box size",
       
  8206     desc: "Don't use width or height when using padding or border.",
       
  8207     url: "https://github.com/CSSLint/csslint/wiki/Beware-of-box-model-size",
       
  8208     browsers: "All",
       
  8209 
       
  8210     // initialization
       
  8211     init: function(parser, reporter) {
       
  8212         "use strict";
       
  8213         var rule = this,
       
  8214             widthProperties = {
       
  8215                 border: 1,
       
  8216                 "border-left": 1,
       
  8217                 "border-right": 1,
       
  8218                 padding: 1,
       
  8219                 "padding-left": 1,
       
  8220                 "padding-right": 1
       
  8221             },
       
  8222             heightProperties = {
       
  8223                 border: 1,
       
  8224                 "border-bottom": 1,
       
  8225                 "border-top": 1,
       
  8226                 padding: 1,
       
  8227                 "padding-bottom": 1,
       
  8228                 "padding-top": 1
       
  8229             },
       
  8230             properties,
       
  8231             boxSizing = false;
       
  8232 
       
  8233         function startRule() {
       
  8234             properties = {};
       
  8235             boxSizing = false;
       
  8236         }
       
  8237 
       
  8238         function endRule() {
       
  8239             var prop, value;
       
  8240 
       
  8241             if (!boxSizing) {
       
  8242                 if (properties.height) {
       
  8243                     for (prop in heightProperties) {
       
  8244                         if (heightProperties.hasOwnProperty(prop) && properties[prop]) {
       
  8245                             value = properties[prop].value;
       
  8246                             // special case for padding
       
  8247                             if (!(prop === "padding" && value.parts.length === 2 && value.parts[0].value === 0)) {
       
  8248                                 reporter.report("Using height with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
       
  8249                             }
       
  8250                         }
       
  8251                     }
       
  8252                 }
       
  8253 
       
  8254                 if (properties.width) {
       
  8255                     for (prop in widthProperties) {
       
  8256                         if (widthProperties.hasOwnProperty(prop) && properties[prop]) {
       
  8257                             value = properties[prop].value;
       
  8258 
       
  8259                             if (!(prop === "padding" && value.parts.length === 2 && value.parts[1].value === 0)) {
       
  8260                                 reporter.report("Using width with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
       
  8261                             }
       
  8262                         }
       
  8263                     }
       
  8264                 }
       
  8265             }
       
  8266         }
       
  8267 
       
  8268         parser.addListener("startrule", startRule);
       
  8269         parser.addListener("startfontface", startRule);
       
  8270         parser.addListener("startpage", startRule);
       
  8271         parser.addListener("startpagemargin", startRule);
       
  8272         parser.addListener("startkeyframerule", startRule);
       
  8273         parser.addListener("startviewport", startRule);
       
  8274 
       
  8275         parser.addListener("property", function(event) {
       
  8276             var name = event.property.text.toLowerCase();
       
  8277 
       
  8278             if (heightProperties[name] || widthProperties[name]) {
       
  8279                 if (!/^0\S*$/.test(event.value) && !(name === "border" && event.value.toString() === "none")) {
       
  8280                     properties[name] = {
       
  8281                         line: event.property.line,
       
  8282                         col: event.property.col,
       
  8283                         value: event.value
       
  8284                     };
       
  8285                 }
       
  8286             } else {
       
  8287                 if (/^(width|height)/i.test(name) && /^(length|percentage)/.test(event.value.parts[0].type)) {
       
  8288                     properties[name] = 1;
       
  8289                 } else if (name === "box-sizing") {
       
  8290                     boxSizing = true;
       
  8291                 }
       
  8292             }
       
  8293 
       
  8294         });
       
  8295 
       
  8296         parser.addListener("endrule", endRule);
       
  8297         parser.addListener("endfontface", endRule);
       
  8298         parser.addListener("endpage", endRule);
       
  8299         parser.addListener("endpagemargin", endRule);
       
  8300         parser.addListener("endkeyframerule", endRule);
       
  8301         parser.addListener("endviewport", endRule);
       
  8302     }
       
  8303 
       
  8304 });
       
  8305 
       
  8306 /*
       
  8307  * Rule: box-sizing doesn't work in IE6 and IE7.
       
  8308  */
       
  8309 
       
  8310 CSSLint.addRule({
       
  8311 
       
  8312     // rule information
       
  8313     id: "box-sizing",
       
  8314     name: "Disallow use of box-sizing",
       
  8315     desc: "The box-sizing properties isn't supported in IE6 and IE7.",
       
  8316     url: "https://github.com/CSSLint/csslint/wiki/Disallow-box-sizing",
       
  8317     browsers: "IE6, IE7",
       
  8318     tags: ["Compatibility"],
       
  8319 
       
  8320     // initialization
       
  8321     init: function(parser, reporter) {
       
  8322         "use strict";
       
  8323         var rule = this;
       
  8324 
       
  8325         parser.addListener("property", function(event) {
       
  8326             var name = event.property.text.toLowerCase();
       
  8327 
       
  8328             if (name === "box-sizing") {
       
  8329                 reporter.report("The box-sizing property isn't supported in IE6 and IE7.", event.line, event.col, rule);
       
  8330             }
       
  8331         });
       
  8332     }
       
  8333 
       
  8334 });
       
  8335 
       
  8336 /*
       
  8337  * Rule: Use the bulletproof @font-face syntax to avoid 404's in old IE
       
  8338  * (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax)
       
  8339  */
       
  8340 
       
  8341 CSSLint.addRule({
       
  8342 
       
  8343     // rule information
       
  8344     id: "bulletproof-font-face",
       
  8345     name: "Use the bulletproof @font-face syntax",
       
  8346     desc: "Use the bulletproof @font-face syntax to avoid 404's in old IE (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax).",
       
  8347     url: "https://github.com/CSSLint/csslint/wiki/Bulletproof-font-face",
       
  8348     browsers: "All",
       
  8349 
       
  8350     // initialization
       
  8351     init: function(parser, reporter) {
       
  8352         "use strict";
       
  8353         var rule = this,
       
  8354             fontFaceRule = false,
       
  8355             firstSrc = true,
       
  8356             ruleFailed = false,
       
  8357             line, col;
       
  8358 
       
  8359         // Mark the start of a @font-face declaration so we only test properties inside it
       
  8360         parser.addListener("startfontface", function() {
       
  8361             fontFaceRule = true;
       
  8362         });
       
  8363 
       
  8364         parser.addListener("property", function(event) {
       
  8365             // If we aren't inside an @font-face declaration then just return
       
  8366             if (!fontFaceRule) {
       
  8367                 return;
       
  8368             }
       
  8369 
       
  8370             var propertyName = event.property.toString().toLowerCase(),
       
  8371                 value = event.value.toString();
       
  8372 
       
  8373             // Set the line and col numbers for use in the endfontface listener
       
  8374             line = event.line;
       
  8375             col = event.col;
       
  8376 
       
  8377             // This is the property that we care about, we can ignore the rest
       
  8378             if (propertyName === "src") {
       
  8379                 var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i;
       
  8380 
       
  8381                 // We need to handle the advanced syntax with two src properties
       
  8382                 if (!value.match(regex) && firstSrc) {
       
  8383                     ruleFailed = true;
       
  8384                     firstSrc = false;
       
  8385                 } else if (value.match(regex) && !firstSrc) {
       
  8386                     ruleFailed = false;
       
  8387                 }
       
  8388             }
       
  8389 
       
  8390 
       
  8391         });
       
  8392 
       
  8393         // Back to normal rules that we don't need to test
       
  8394         parser.addListener("endfontface", function() {
       
  8395             fontFaceRule = false;
       
  8396 
       
  8397             if (ruleFailed) {
       
  8398                 reporter.report("@font-face declaration doesn't follow the fontspring bulletproof syntax.", line, col, rule);
       
  8399             }
       
  8400         });
       
  8401     }
       
  8402 });
       
  8403 
       
  8404 /*
       
  8405  * Rule: Include all compatible vendor prefixes to reach a wider
       
  8406  * range of users.
       
  8407  */
       
  8408 
       
  8409 CSSLint.addRule({
       
  8410 
       
  8411     // rule information
       
  8412     id: "compatible-vendor-prefixes",
       
  8413     name: "Require compatible vendor prefixes",
       
  8414     desc: "Include all compatible vendor prefixes to reach a wider range of users.",
       
  8415     url: "https://github.com/CSSLint/csslint/wiki/Require-compatible-vendor-prefixes",
       
  8416     browsers: "All",
       
  8417 
       
  8418     // initialization
       
  8419     init: function (parser, reporter) {
       
  8420         "use strict";
       
  8421         var rule = this,
       
  8422             compatiblePrefixes,
       
  8423             properties,
       
  8424             prop,
       
  8425             variations,
       
  8426             prefixed,
       
  8427             i,
       
  8428             len,
       
  8429             inKeyFrame = false,
       
  8430             arrayPush = Array.prototype.push,
       
  8431             applyTo = [];
       
  8432 
       
  8433         // See http://peter.sh/experiments/vendor-prefixed-css-property-overview/ for details
       
  8434         compatiblePrefixes = {
       
  8435             "animation"                  : "webkit",
       
  8436             "animation-delay"            : "webkit",
       
  8437             "animation-direction"        : "webkit",
       
  8438             "animation-duration"         : "webkit",
       
  8439             "animation-fill-mode"        : "webkit",
       
  8440             "animation-iteration-count"  : "webkit",
       
  8441             "animation-name"             : "webkit",
       
  8442             "animation-play-state"       : "webkit",
       
  8443             "animation-timing-function"  : "webkit",
       
  8444             "appearance"                 : "webkit moz",
       
  8445             "border-end"                 : "webkit moz",
       
  8446             "border-end-color"           : "webkit moz",
       
  8447             "border-end-style"           : "webkit moz",
       
  8448             "border-end-width"           : "webkit moz",
       
  8449             "border-image"               : "webkit moz o",
       
  8450             "border-radius"              : "webkit",
       
  8451             "border-start"               : "webkit moz",
       
  8452             "border-start-color"         : "webkit moz",
       
  8453             "border-start-style"         : "webkit moz",
       
  8454             "border-start-width"         : "webkit moz",
       
  8455             "box-align"                  : "webkit moz ms",
       
  8456             "box-direction"              : "webkit moz ms",
       
  8457             "box-flex"                   : "webkit moz ms",
       
  8458             "box-lines"                  : "webkit ms",
       
  8459             "box-ordinal-group"          : "webkit moz ms",
       
  8460             "box-orient"                 : "webkit moz ms",
       
  8461             "box-pack"                   : "webkit moz ms",
       
  8462             "box-sizing"                 : "",
       
  8463             "box-shadow"                 : "",
       
  8464             "column-count"               : "webkit moz ms",
       
  8465             "column-gap"                 : "webkit moz ms",
       
  8466             "column-rule"                : "webkit moz ms",
       
  8467             "column-rule-color"          : "webkit moz ms",
       
  8468             "column-rule-style"          : "webkit moz ms",
       
  8469             "column-rule-width"          : "webkit moz ms",
       
  8470             "column-width"               : "webkit moz ms",
       
  8471             "hyphens"                    : "epub moz",
       
  8472             "line-break"                 : "webkit ms",
       
  8473             "margin-end"                 : "webkit moz",
       
  8474             "margin-start"               : "webkit moz",
       
  8475             "marquee-speed"              : "webkit wap",
       
  8476             "marquee-style"              : "webkit wap",
       
  8477             "padding-end"                : "webkit moz",
       
  8478             "padding-start"              : "webkit moz",
       
  8479             "tab-size"                   : "moz o",
       
  8480             "text-size-adjust"           : "webkit ms",
       
  8481             "transform"                  : "webkit ms",
       
  8482             "transform-origin"           : "webkit ms",
       
  8483             "transition"                 : "",
       
  8484             "transition-delay"           : "",
       
  8485             "transition-duration"        : "",
       
  8486             "transition-property"        : "",
       
  8487             "transition-timing-function" : "",
       
  8488             "user-modify"                : "webkit moz",
       
  8489             "user-select"                : "webkit moz ms",
       
  8490             "word-break"                 : "epub ms",
       
  8491             "writing-mode"               : "epub ms"
       
  8492         };
       
  8493 
       
  8494 
       
  8495         for (prop in compatiblePrefixes) {
       
  8496             if (compatiblePrefixes.hasOwnProperty(prop)) {
       
  8497                 variations = [];
       
  8498                 prefixed = compatiblePrefixes[prop].split(" ");
       
  8499                 for (i = 0, len = prefixed.length; i < len; i++) {
       
  8500                     variations.push("-" + prefixed[i] + "-" + prop);
       
  8501                 }
       
  8502                 compatiblePrefixes[prop] = variations;
       
  8503                 arrayPush.apply(applyTo, variations);
       
  8504             }
       
  8505         }
       
  8506 
       
  8507         parser.addListener("startrule", function () {
       
  8508             properties = [];
       
  8509         });
       
  8510 
       
  8511         parser.addListener("startkeyframes", function (event) {
       
  8512             inKeyFrame = event.prefix || true;
       
  8513         });
       
  8514 
       
  8515         parser.addListener("endkeyframes", function () {
       
  8516             inKeyFrame = false;
       
  8517         });
       
  8518 
       
  8519         parser.addListener("property", function (event) {
       
  8520             var name = event.property;
       
  8521             if (CSSLint.Util.indexOf(applyTo, name.text) > -1) {
       
  8522 
       
  8523                 // e.g., -moz-transform is okay to be alone in @-moz-keyframes
       
  8524                 if (!inKeyFrame || typeof inKeyFrame !== "string" ||
       
  8525                         name.text.indexOf("-" + inKeyFrame + "-") !== 0) {
       
  8526                     properties.push(name);
       
  8527                 }
       
  8528             }
       
  8529         });
       
  8530 
       
  8531         parser.addListener("endrule", function () {
       
  8532             if (!properties.length) {
       
  8533                 return;
       
  8534             }
       
  8535 
       
  8536             var propertyGroups = {},
       
  8537                 i,
       
  8538                 len,
       
  8539                 name,
       
  8540                 prop,
       
  8541                 variations,
       
  8542                 value,
       
  8543                 full,
       
  8544                 actual,
       
  8545                 item,
       
  8546                 propertiesSpecified;
       
  8547 
       
  8548             for (i = 0, len = properties.length; i < len; i++) {
       
  8549                 name = properties[i];
       
  8550 
       
  8551                 for (prop in compatiblePrefixes) {
       
  8552                     if (compatiblePrefixes.hasOwnProperty(prop)) {
       
  8553                         variations = compatiblePrefixes[prop];
       
  8554                         if (CSSLint.Util.indexOf(variations, name.text) > -1) {
       
  8555                             if (!propertyGroups[prop]) {
       
  8556                                 propertyGroups[prop] = {
       
  8557                                     full: variations.slice(0),
       
  8558                                     actual: [],
       
  8559                                     actualNodes: []
       
  8560                                 };
       
  8561                             }
       
  8562                             if (CSSLint.Util.indexOf(propertyGroups[prop].actual, name.text) === -1) {
       
  8563                                 propertyGroups[prop].actual.push(name.text);
       
  8564                                 propertyGroups[prop].actualNodes.push(name);
       
  8565                             }
       
  8566                         }
       
  8567                     }
       
  8568                 }
       
  8569             }
       
  8570 
       
  8571             for (prop in propertyGroups) {
       
  8572                 if (propertyGroups.hasOwnProperty(prop)) {
       
  8573                     value = propertyGroups[prop];
       
  8574                     full = value.full;
       
  8575                     actual = value.actual;
       
  8576 
       
  8577                     if (full.length > actual.length) {
       
  8578                         for (i = 0, len = full.length; i < len; i++) {
       
  8579                             item = full[i];
       
  8580                             if (CSSLint.Util.indexOf(actual, item) === -1) {
       
  8581                                 propertiesSpecified = (actual.length === 1) ? actual[0] : (actual.length === 2) ? actual.join(" and ") : actual.join(", ");
       
  8582                                 reporter.report("The property " + item + " is compatible with " + propertiesSpecified + " and should be included as well.", value.actualNodes[0].line, value.actualNodes[0].col, rule);
       
  8583                             }
       
  8584                         }
       
  8585 
       
  8586                     }
       
  8587                 }
       
  8588             }
       
  8589         });
       
  8590     }
       
  8591 });
       
  8592 
       
  8593 /*
       
  8594  * Rule: Certain properties don't play well with certain display values.
       
  8595  * - float should not be used with inline-block
       
  8596  * - height, width, margin-top, margin-bottom, float should not be used with inline
       
  8597  * - vertical-align should not be used with block
       
  8598  * - margin, float should not be used with table-*
       
  8599  */
       
  8600 
       
  8601 CSSLint.addRule({
       
  8602 
       
  8603     // rule information
       
  8604     id: "display-property-grouping",
       
  8605     name: "Require properties appropriate for display",
       
  8606     desc: "Certain properties shouldn't be used with certain display property values.",
       
  8607     url: "https://github.com/CSSLint/csslint/wiki/Require-properties-appropriate-for-display",
       
  8608     browsers: "All",
       
  8609 
       
  8610     // initialization
       
  8611     init: function(parser, reporter) {
       
  8612         "use strict";
       
  8613         var rule = this;
       
  8614 
       
  8615         var propertiesToCheck = {
       
  8616                 display: 1,
       
  8617                 "float": "none",
       
  8618                 height: 1,
       
  8619                 width: 1,
       
  8620                 margin: 1,
       
  8621                 "margin-left": 1,
       
  8622                 "margin-right": 1,
       
  8623                 "margin-bottom": 1,
       
  8624                 "margin-top": 1,
       
  8625                 padding: 1,
       
  8626                 "padding-left": 1,
       
  8627                 "padding-right": 1,
       
  8628                 "padding-bottom": 1,
       
  8629                 "padding-top": 1,
       
  8630                 "vertical-align": 1
       
  8631             },
       
  8632             properties;
       
  8633 
       
  8634         function reportProperty(name, display, msg) {
       
  8635             if (properties[name]) {
       
  8636                 if (typeof propertiesToCheck[name] !== "string" || properties[name].value.toLowerCase() !== propertiesToCheck[name]) {
       
  8637                     reporter.report(msg || name + " can't be used with display: " + display + ".", properties[name].line, properties[name].col, rule);
       
  8638                 }
       
  8639             }
       
  8640         }
       
  8641 
       
  8642         function startRule() {
       
  8643             properties = {};
       
  8644         }
       
  8645 
       
  8646         function endRule() {
       
  8647 
       
  8648             var display = properties.display ? properties.display.value : null;
       
  8649             if (display) {
       
  8650                 switch (display) {
       
  8651 
       
  8652                     case "inline":
       
  8653                         // height, width, margin-top, margin-bottom, float should not be used with inline
       
  8654                         reportProperty("height", display);
       
  8655                         reportProperty("width", display);
       
  8656                         reportProperty("margin", display);
       
  8657                         reportProperty("margin-top", display);
       
  8658                         reportProperty("margin-bottom", display);
       
  8659                         reportProperty("float", display, "display:inline has no effect on floated elements (but may be used to fix the IE6 double-margin bug).");
       
  8660                         break;
       
  8661 
       
  8662                     case "block":
       
  8663                         // vertical-align should not be used with block
       
  8664                         reportProperty("vertical-align", display);
       
  8665                         break;
       
  8666 
       
  8667                     case "inline-block":
       
  8668                         // float should not be used with inline-block
       
  8669                         reportProperty("float", display);
       
  8670                         break;
       
  8671 
       
  8672                     default:
       
  8673                         // margin, float should not be used with table
       
  8674                         if (display.indexOf("table-") === 0) {
       
  8675                             reportProperty("margin", display);
       
  8676                             reportProperty("margin-left", display);
       
  8677                             reportProperty("margin-right", display);
       
  8678                             reportProperty("margin-top", display);
       
  8679                             reportProperty("margin-bottom", display);
       
  8680                             reportProperty("float", display);
       
  8681                         }
       
  8682 
       
  8683                         // otherwise do nothing
       
  8684                 }
       
  8685             }
       
  8686 
       
  8687         }
       
  8688 
       
  8689         parser.addListener("startrule", startRule);
       
  8690         parser.addListener("startfontface", startRule);
       
  8691         parser.addListener("startkeyframerule", startRule);
       
  8692         parser.addListener("startpagemargin", startRule);
       
  8693         parser.addListener("startpage", startRule);
       
  8694         parser.addListener("startviewport", startRule);
       
  8695 
       
  8696         parser.addListener("property", function(event) {
       
  8697             var name = event.property.text.toLowerCase();
       
  8698 
       
  8699             if (propertiesToCheck[name]) {
       
  8700                 properties[name] = {
       
  8701                     value: event.value.text,
       
  8702                     line: event.property.line,
       
  8703                     col: event.property.col
       
  8704                 };
       
  8705             }
       
  8706         });
       
  8707 
       
  8708         parser.addListener("endrule", endRule);
       
  8709         parser.addListener("endfontface", endRule);
       
  8710         parser.addListener("endkeyframerule", endRule);
       
  8711         parser.addListener("endpagemargin", endRule);
       
  8712         parser.addListener("endpage", endRule);
       
  8713         parser.addListener("endviewport", endRule);
       
  8714 
       
  8715     }
       
  8716 
       
  8717 });
       
  8718 
       
  8719 /*
       
  8720  * Rule: Disallow duplicate background-images (using url).
       
  8721  */
       
  8722 
       
  8723 CSSLint.addRule({
       
  8724 
       
  8725     // rule information
       
  8726     id: "duplicate-background-images",
       
  8727     name: "Disallow duplicate background images",
       
  8728     desc: "Every background-image should be unique. Use a common class for e.g. sprites.",
       
  8729     url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-background-images",
       
  8730     browsers: "All",
       
  8731 
       
  8732     // initialization
       
  8733     init: function(parser, reporter) {
       
  8734         "use strict";
       
  8735         var rule = this,
       
  8736             stack = {};
       
  8737 
       
  8738         parser.addListener("property", function(event) {
       
  8739             var name = event.property.text,
       
  8740                 value = event.value,
       
  8741                 i, len;
       
  8742 
       
  8743             if (name.match(/background/i)) {
       
  8744                 for (i=0, len=value.parts.length; i < len; i++) {
       
  8745                     if (value.parts[i].type === "uri") {
       
  8746                         if (typeof stack[value.parts[i].uri] === "undefined") {
       
  8747                             stack[value.parts[i].uri] = event;
       
  8748                         } else {
       
  8749                             reporter.report("Background image '" + value.parts[i].uri + "' was used multiple times, first declared at line " + stack[value.parts[i].uri].line + ", col " + stack[value.parts[i].uri].col + ".", event.line, event.col, rule);
       
  8750                         }
       
  8751                     }
       
  8752                 }
       
  8753             }
       
  8754         });
       
  8755     }
       
  8756 });
       
  8757 
       
  8758 /*
       
  8759  * Rule: Duplicate properties must appear one after the other. If an already-defined
       
  8760  * property appears somewhere else in the rule, then it's likely an error.
       
  8761  */
       
  8762 
       
  8763 CSSLint.addRule({
       
  8764 
       
  8765     // rule information
       
  8766     id: "duplicate-properties",
       
  8767     name: "Disallow duplicate properties",
       
  8768     desc: "Duplicate properties must appear one after the other.",
       
  8769     url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-properties",
       
  8770     browsers: "All",
       
  8771 
       
  8772     // initialization
       
  8773     init: function(parser, reporter) {
       
  8774         "use strict";
       
  8775         var rule = this,
       
  8776             properties,
       
  8777             lastProperty;
       
  8778 
       
  8779         function startRule() {
       
  8780             properties = {};
       
  8781         }
       
  8782 
       
  8783         parser.addListener("startrule", startRule);
       
  8784         parser.addListener("startfontface", startRule);
       
  8785         parser.addListener("startpage", startRule);
       
  8786         parser.addListener("startpagemargin", startRule);
       
  8787         parser.addListener("startkeyframerule", startRule);
       
  8788         parser.addListener("startviewport", startRule);
       
  8789 
       
  8790         parser.addListener("property", function(event) {
       
  8791             var property = event.property,
       
  8792                 name = property.text.toLowerCase();
       
  8793 
       
  8794             if (properties[name] && (lastProperty !== name || properties[name] === event.value.text)) {
       
  8795                 reporter.report("Duplicate property '" + event.property + "' found.", event.line, event.col, rule);
       
  8796             }
       
  8797 
       
  8798             properties[name] = event.value.text;
       
  8799             lastProperty = name;
       
  8800 
       
  8801         });
       
  8802 
       
  8803 
       
  8804     }
       
  8805 
       
  8806 });
       
  8807 
       
  8808 /*
       
  8809  * Rule: Style rules without any properties defined should be removed.
       
  8810  */
       
  8811 
       
  8812 CSSLint.addRule({
       
  8813 
       
  8814     // rule information
       
  8815     id: "empty-rules",
       
  8816     name: "Disallow empty rules",
       
  8817     desc: "Rules without any properties specified should be removed.",
       
  8818     url: "https://github.com/CSSLint/csslint/wiki/Disallow-empty-rules",
       
  8819     browsers: "All",
       
  8820 
       
  8821     // initialization
       
  8822     init: function(parser, reporter) {
       
  8823         "use strict";
       
  8824         var rule = this,
       
  8825             count = 0;
       
  8826 
       
  8827         parser.addListener("startrule", function() {
       
  8828             count=0;
       
  8829         });
       
  8830 
       
  8831         parser.addListener("property", function() {
       
  8832             count++;
       
  8833         });
       
  8834 
       
  8835         parser.addListener("endrule", function(event) {
       
  8836             var selectors = event.selectors;
       
  8837             if (count === 0) {
       
  8838                 reporter.report("Rule is empty.", selectors[0].line, selectors[0].col, rule);
       
  8839             }
       
  8840         });
       
  8841     }
       
  8842 
       
  8843 });
       
  8844 
       
  8845 /*
       
  8846  * Rule: There should be no syntax errors. (Duh.)
       
  8847  */
       
  8848 
       
  8849 CSSLint.addRule({
       
  8850 
       
  8851     // rule information
       
  8852     id: "errors",
       
  8853     name: "Parsing Errors",
       
  8854     desc: "This rule looks for recoverable syntax errors.",
       
  8855     browsers: "All",
       
  8856 
       
  8857     // initialization
       
  8858     init: function(parser, reporter) {
       
  8859         "use strict";
       
  8860         var rule = this;
       
  8861 
       
  8862         parser.addListener("error", function(event) {
       
  8863             reporter.error(event.message, event.line, event.col, rule);
       
  8864         });
       
  8865 
       
  8866     }
       
  8867 
       
  8868 });
       
  8869 
       
  8870 CSSLint.addRule({
       
  8871 
       
  8872     // rule information
       
  8873     id: "fallback-colors",
       
  8874     name: "Require fallback colors",
       
  8875     desc: "For older browsers that don't support RGBA, HSL, or HSLA, provide a fallback color.",
       
  8876     url: "https://github.com/CSSLint/csslint/wiki/Require-fallback-colors",
       
  8877     browsers: "IE6,IE7,IE8",
       
  8878 
       
  8879     // initialization
       
  8880     init: function(parser, reporter) {
       
  8881         "use strict";
       
  8882         var rule = this,
       
  8883             lastProperty,
       
  8884             propertiesToCheck = {
       
  8885                 color: 1,
       
  8886                 background: 1,
       
  8887                 "border-color": 1,
       
  8888                 "border-top-color": 1,
       
  8889                 "border-right-color": 1,
       
  8890                 "border-bottom-color": 1,
       
  8891                 "border-left-color": 1,
       
  8892                 border: 1,
       
  8893                 "border-top": 1,
       
  8894                 "border-right": 1,
       
  8895                 "border-bottom": 1,
       
  8896                 "border-left": 1,
       
  8897                 "background-color": 1
       
  8898             };
       
  8899 
       
  8900         function startRule() {
       
  8901             lastProperty = null;
       
  8902         }
       
  8903 
       
  8904         parser.addListener("startrule", startRule);
       
  8905         parser.addListener("startfontface", startRule);
       
  8906         parser.addListener("startpage", startRule);
       
  8907         parser.addListener("startpagemargin", startRule);
       
  8908         parser.addListener("startkeyframerule", startRule);
       
  8909         parser.addListener("startviewport", startRule);
       
  8910 
       
  8911         parser.addListener("property", function(event) {
       
  8912             var property = event.property,
       
  8913                 name = property.text.toLowerCase(),
       
  8914                 parts = event.value.parts,
       
  8915                 i = 0,
       
  8916                 colorType = "",
       
  8917                 len = parts.length;
       
  8918 
       
  8919             if (propertiesToCheck[name]) {
       
  8920                 while (i < len) {
       
  8921                     if (parts[i].type === "color") {
       
  8922                         if ("alpha" in parts[i] || "hue" in parts[i]) {
       
  8923 
       
  8924                             if (/([^\)]+)\(/.test(parts[i])) {
       
  8925                                 colorType = RegExp.$1.toUpperCase();
       
  8926                             }
       
  8927 
       
  8928                             if (!lastProperty || (lastProperty.property.text.toLowerCase() !== name || lastProperty.colorType !== "compat")) {
       
  8929                                 reporter.report("Fallback " + name + " (hex or RGB) should precede " + colorType + " " + name + ".", event.line, event.col, rule);
       
  8930                             }
       
  8931                         } else {
       
  8932                             event.colorType = "compat";
       
  8933                         }
       
  8934                     }
       
  8935 
       
  8936                     i++;
       
  8937                 }
       
  8938             }
       
  8939 
       
  8940             lastProperty = event;
       
  8941         });
       
  8942 
       
  8943     }
       
  8944 
       
  8945 });
       
  8946 
       
  8947 /*
       
  8948  * Rule: You shouldn't use more than 10 floats. If you do, there's probably
       
  8949  * room for some abstraction.
       
  8950  */
       
  8951 
       
  8952 CSSLint.addRule({
       
  8953 
       
  8954     // rule information
       
  8955     id: "floats",
       
  8956     name: "Disallow too many floats",
       
  8957     desc: "This rule tests if the float property is used too many times",
       
  8958     url: "https://github.com/CSSLint/csslint/wiki/Disallow-too-many-floats",
       
  8959     browsers: "All",
       
  8960 
       
  8961     // initialization
       
  8962     init: function(parser, reporter) {
       
  8963         "use strict";
       
  8964         var rule = this;
       
  8965         var count = 0;
       
  8966 
       
  8967         // count how many times "float" is used
       
  8968         parser.addListener("property", function(event) {
       
  8969             if (event.property.text.toLowerCase() === "float" &&
       
  8970                     event.value.text.toLowerCase() !== "none") {
       
  8971                 count++;
       
  8972             }
       
  8973         });
       
  8974 
       
  8975         // report the results
       
  8976         parser.addListener("endstylesheet", function() {
       
  8977             reporter.stat("floats", count);
       
  8978             if (count >= 10) {
       
  8979                 reporter.rollupWarn("Too many floats (" + count + "), you're probably using them for layout. Consider using a grid system instead.", rule);
       
  8980             }
       
  8981         });
       
  8982     }
       
  8983 
       
  8984 });
       
  8985 
       
  8986 /*
       
  8987  * Rule: Avoid too many @font-face declarations in the same stylesheet.
       
  8988  */
       
  8989 
       
  8990 CSSLint.addRule({
       
  8991 
       
  8992     // rule information
       
  8993     id: "font-faces",
       
  8994     name: "Don't use too many web fonts",
       
  8995     desc: "Too many different web fonts in the same stylesheet.",
       
  8996     url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-web-fonts",
       
  8997     browsers: "All",
       
  8998 
       
  8999     // initialization
       
  9000     init: function(parser, reporter) {
       
  9001         "use strict";
       
  9002         var rule = this,
       
  9003             count = 0;
       
  9004 
       
  9005 
       
  9006         parser.addListener("startfontface", function() {
       
  9007             count++;
       
  9008         });
       
  9009 
       
  9010         parser.addListener("endstylesheet", function() {
       
  9011             if (count > 5) {
       
  9012                 reporter.rollupWarn("Too many @font-face declarations (" + count + ").", rule);
       
  9013             }
       
  9014         });
       
  9015     }
       
  9016 
       
  9017 });
       
  9018 
       
  9019 /*
       
  9020  * Rule: You shouldn't need more than 9 font-size declarations.
       
  9021  */
       
  9022 
       
  9023 CSSLint.addRule({
       
  9024 
       
  9025     // rule information
       
  9026     id: "font-sizes",
       
  9027     name: "Disallow too many font sizes",
       
  9028     desc: "Checks the number of font-size declarations.",
       
  9029     url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-font-size-declarations",
       
  9030     browsers: "All",
       
  9031 
       
  9032     // initialization
       
  9033     init: function(parser, reporter) {
       
  9034         "use strict";
       
  9035         var rule = this,
       
  9036             count = 0;
       
  9037 
       
  9038         // check for use of "font-size"
       
  9039         parser.addListener("property", function(event) {
       
  9040             if (event.property.toString() === "font-size") {
       
  9041                 count++;
       
  9042             }
       
  9043         });
       
  9044 
       
  9045         // report the results
       
  9046         parser.addListener("endstylesheet", function() {
       
  9047             reporter.stat("font-sizes", count);
       
  9048             if (count >= 10) {
       
  9049                 reporter.rollupWarn("Too many font-size declarations (" + count + "), abstraction needed.", rule);
       
  9050             }
       
  9051         });
       
  9052     }
       
  9053 
       
  9054 });
       
  9055 
       
  9056 /*
       
  9057  * Rule: When using a vendor-prefixed gradient, make sure to use them all.
       
  9058  */
       
  9059 
       
  9060 CSSLint.addRule({
       
  9061 
       
  9062     // rule information
       
  9063     id: "gradients",
       
  9064     name: "Require all gradient definitions",
       
  9065     desc: "When using a vendor-prefixed gradient, make sure to use them all.",
       
  9066     url: "https://github.com/CSSLint/csslint/wiki/Require-all-gradient-definitions",
       
  9067     browsers: "All",
       
  9068 
       
  9069     // initialization
       
  9070     init: function(parser, reporter) {
       
  9071         "use strict";
       
  9072         var rule = this,
       
  9073             gradients;
       
  9074 
       
  9075         parser.addListener("startrule", function() {
       
  9076             gradients = {
       
  9077                 moz: 0,
       
  9078                 webkit: 0,
       
  9079                 oldWebkit: 0,
       
  9080                 o: 0
       
  9081             };
       
  9082         });
       
  9083 
       
  9084         parser.addListener("property", function(event) {
       
  9085 
       
  9086             if (/\-(moz|o|webkit)(?:\-(?:linear|radial))\-gradient/i.test(event.value)) {
       
  9087                 gradients[RegExp.$1] = 1;
       
  9088             } else if (/\-webkit\-gradient/i.test(event.value)) {
       
  9089                 gradients.oldWebkit = 1;
       
  9090             }
       
  9091 
       
  9092         });
       
  9093 
       
  9094         parser.addListener("endrule", function(event) {
       
  9095             var missing = [];
       
  9096 
       
  9097             if (!gradients.moz) {
       
  9098                 missing.push("Firefox 3.6+");
       
  9099             }
       
  9100 
       
  9101             if (!gradients.webkit) {
       
  9102                 missing.push("Webkit (Safari 5+, Chrome)");
       
  9103             }
       
  9104 
       
  9105             if (!gradients.oldWebkit) {
       
  9106                 missing.push("Old Webkit (Safari 4+, Chrome)");
       
  9107             }
       
  9108 
       
  9109             if (!gradients.o) {
       
  9110                 missing.push("Opera 11.1+");
       
  9111             }
       
  9112 
       
  9113             if (missing.length && missing.length < 4) {
       
  9114                 reporter.report("Missing vendor-prefixed CSS gradients for " + missing.join(", ") + ".", event.selectors[0].line, event.selectors[0].col, rule);
       
  9115             }
       
  9116 
       
  9117         });
       
  9118 
       
  9119     }
       
  9120 
       
  9121 });
       
  9122 
       
  9123 /*
       
  9124  * Rule: Don't use IDs for selectors.
       
  9125  */
       
  9126 
       
  9127 CSSLint.addRule({
       
  9128 
       
  9129     // rule information
       
  9130     id: "ids",
       
  9131     name: "Disallow IDs in selectors",
       
  9132     desc: "Selectors should not contain IDs.",
       
  9133     url: "https://github.com/CSSLint/csslint/wiki/Disallow-IDs-in-selectors",
       
  9134     browsers: "All",
       
  9135 
       
  9136     // initialization
       
  9137     init: function(parser, reporter) {
       
  9138         "use strict";
       
  9139         var rule = this;
       
  9140         parser.addListener("startrule", function(event) {
       
  9141             var selectors = event.selectors,
       
  9142                 selector,
       
  9143                 part,
       
  9144                 modifier,
       
  9145                 idCount,
       
  9146                 i, j, k;
       
  9147 
       
  9148             for (i=0; i < selectors.length; i++) {
       
  9149                 selector = selectors[i];
       
  9150                 idCount = 0;
       
  9151 
       
  9152                 for (j=0; j < selector.parts.length; j++) {
       
  9153                     part = selector.parts[j];
       
  9154                     if (part.type === parser.SELECTOR_PART_TYPE) {
       
  9155                         for (k=0; k < part.modifiers.length; k++) {
       
  9156                             modifier = part.modifiers[k];
       
  9157                             if (modifier.type === "id") {
       
  9158                                 idCount++;
       
  9159                             }
       
  9160                         }
       
  9161                     }
       
  9162                 }
       
  9163 
       
  9164                 if (idCount === 1) {
       
  9165                     reporter.report("Don't use IDs in selectors.", selector.line, selector.col, rule);
       
  9166                 } else if (idCount > 1) {
       
  9167                     reporter.report(idCount + " IDs in the selector, really?", selector.line, selector.col, rule);
       
  9168                 }
       
  9169             }
       
  9170 
       
  9171         });
       
  9172     }
       
  9173 
       
  9174 });
       
  9175 
       
  9176 /*
       
  9177  * Rule: IE6-9 supports up to 31 stylesheet import.
       
  9178  * Reference:
       
  9179  * http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/internet-explorer-stylesheet-rule-selector-import-sheet-limit-maximum.aspx
       
  9180  */
       
  9181 
       
  9182 CSSLint.addRule({
       
  9183 
       
  9184     // rule information
       
  9185     id: "import-ie-limit",
       
  9186     name: "@import limit on IE6-IE9",
       
  9187     desc: "IE6-9 supports up to 31 @import per stylesheet",
       
  9188     browsers: "IE6, IE7, IE8, IE9",
       
  9189 
       
  9190     // initialization
       
  9191     init: function(parser, reporter) {
       
  9192         "use strict";
       
  9193         var rule = this,
       
  9194             MAX_IMPORT_COUNT = 31,
       
  9195             count = 0;
       
  9196 
       
  9197         function startPage() {
       
  9198             count = 0;
       
  9199         }
       
  9200 
       
  9201         parser.addListener("startpage", startPage);
       
  9202 
       
  9203         parser.addListener("import", function() {
       
  9204             count++;
       
  9205         });
       
  9206 
       
  9207         parser.addListener("endstylesheet", function() {
       
  9208             if (count > MAX_IMPORT_COUNT) {
       
  9209                 reporter.rollupError(
       
  9210                     "Too many @import rules (" + count + "). IE6-9 supports up to 31 import per stylesheet.",
       
  9211                     rule
       
  9212                 );
       
  9213             }
       
  9214         });
       
  9215     }
       
  9216 
       
  9217 });
       
  9218 
       
  9219 /*
       
  9220  * Rule: Don't use @import, use <link> instead.
       
  9221  */
       
  9222 
       
  9223 CSSLint.addRule({
       
  9224 
       
  9225     // rule information
       
  9226     id: "import",
       
  9227     name: "Disallow @import",
       
  9228     desc: "Don't use @import, use <link> instead.",
       
  9229     url: "https://github.com/CSSLint/csslint/wiki/Disallow-%40import",
       
  9230     browsers: "All",
       
  9231 
       
  9232     // initialization
       
  9233     init: function(parser, reporter) {
       
  9234         "use strict";
       
  9235         var rule = this;
       
  9236 
       
  9237         parser.addListener("import", function(event) {
       
  9238             reporter.report("@import prevents parallel downloads, use <link> instead.", event.line, event.col, rule);
       
  9239         });
       
  9240 
       
  9241     }
       
  9242 
       
  9243 });
       
  9244 
       
  9245 /*
       
  9246  * Rule: Make sure !important is not overused, this could lead to specificity
       
  9247  * war. Display a warning on !important declarations, an error if it's
       
  9248  * used more at least 10 times.
       
  9249  */
       
  9250 
       
  9251 CSSLint.addRule({
       
  9252 
       
  9253     // rule information
       
  9254     id: "important",
       
  9255     name: "Disallow !important",
       
  9256     desc: "Be careful when using !important declaration",
       
  9257     url: "https://github.com/CSSLint/csslint/wiki/Disallow-%21important",
       
  9258     browsers: "All",
       
  9259 
       
  9260     // initialization
       
  9261     init: function(parser, reporter) {
       
  9262         "use strict";
       
  9263         var rule = this,
       
  9264             count = 0;
       
  9265 
       
  9266         // warn that important is used and increment the declaration counter
       
  9267         parser.addListener("property", function(event) {
       
  9268             if (event.important === true) {
       
  9269                 count++;
       
  9270                 reporter.report("Use of !important", event.line, event.col, rule);
       
  9271             }
       
  9272         });
       
  9273 
       
  9274         // if there are more than 10, show an error
       
  9275         parser.addListener("endstylesheet", function() {
       
  9276             reporter.stat("important", count);
       
  9277             if (count >= 10) {
       
  9278                 reporter.rollupWarn("Too many !important declarations (" + count + "), try to use less than 10 to avoid specificity issues.", rule);
       
  9279             }
       
  9280         });
       
  9281     }
       
  9282 
       
  9283 });
       
  9284 
       
  9285 /*
       
  9286  * Rule: Properties should be known (listed in CSS3 specification) or
       
  9287  * be a vendor-prefixed property.
       
  9288  */
       
  9289 
       
  9290 CSSLint.addRule({
       
  9291 
       
  9292     // rule information
       
  9293     id: "known-properties",
       
  9294     name: "Require use of known properties",
       
  9295     desc: "Properties should be known (listed in CSS3 specification) or be a vendor-prefixed property.",
       
  9296     url: "https://github.com/CSSLint/csslint/wiki/Require-use-of-known-properties",
       
  9297     browsers: "All",
       
  9298 
       
  9299     // initialization
       
  9300     init: function(parser, reporter) {
       
  9301         "use strict";
       
  9302         var rule = this;
       
  9303 
       
  9304         parser.addListener("property", function(event) {
       
  9305 
       
  9306             // the check is handled entirely by the parser-lib (https://github.com/nzakas/parser-lib)
       
  9307             if (event.invalid) {
       
  9308                 reporter.report(event.invalid.message, event.line, event.col, rule);
       
  9309             }
       
  9310 
       
  9311         });
       
  9312     }
       
  9313 
       
  9314 });
       
  9315 
       
  9316 /*
       
  9317  * Rule: All properties should be in alphabetical order.
       
  9318  */
       
  9319 
       
  9320 CSSLint.addRule({
       
  9321 
       
  9322     // rule information
       
  9323     id: "order-alphabetical",
       
  9324     name: "Alphabetical order",
       
  9325     desc: "Assure properties are in alphabetical order",
       
  9326     browsers: "All",
       
  9327 
       
  9328     // initialization
       
  9329     init: function(parser, reporter) {
       
  9330         "use strict";
       
  9331         var rule = this,
       
  9332             properties;
       
  9333 
       
  9334         var startRule = function () {
       
  9335             properties = [];
       
  9336         };
       
  9337 
       
  9338         var endRule = function(event) {
       
  9339             var currentProperties = properties.join(","),
       
  9340                 expectedProperties = properties.sort().join(",");
       
  9341 
       
  9342             if (currentProperties !== expectedProperties) {
       
  9343                 reporter.report("Rule doesn't have all its properties in alphabetical order.", event.line, event.col, rule);
       
  9344             }
       
  9345         };
       
  9346 
       
  9347         parser.addListener("startrule", startRule);
       
  9348         parser.addListener("startfontface", startRule);
       
  9349         parser.addListener("startpage", startRule);
       
  9350         parser.addListener("startpagemargin", startRule);
       
  9351         parser.addListener("startkeyframerule", startRule);
       
  9352         parser.addListener("startviewport", startRule);
       
  9353 
       
  9354         parser.addListener("property", function(event) {
       
  9355             var name = event.property.text,
       
  9356                 lowerCasePrefixLessName = name.toLowerCase().replace(/^-.*?-/, "");
       
  9357 
       
  9358             properties.push(lowerCasePrefixLessName);
       
  9359         });
       
  9360 
       
  9361         parser.addListener("endrule", endRule);
       
  9362         parser.addListener("endfontface", endRule);
       
  9363         parser.addListener("endpage", endRule);
       
  9364         parser.addListener("endpagemargin", endRule);
       
  9365         parser.addListener("endkeyframerule", endRule);
       
  9366         parser.addListener("endviewport", endRule);
       
  9367     }
       
  9368 
       
  9369 });
       
  9370 
       
  9371 /*
       
  9372  * Rule: outline: none or outline: 0 should only be used in a :focus rule
       
  9373  *       and only if there are other properties in the same rule.
       
  9374  */
       
  9375 
       
  9376 CSSLint.addRule({
       
  9377 
       
  9378     // rule information
       
  9379     id: "outline-none",
       
  9380     name: "Disallow outline: none",
       
  9381     desc: "Use of outline: none or outline: 0 should be limited to :focus rules.",
       
  9382     url: "https://github.com/CSSLint/csslint/wiki/Disallow-outline%3Anone",
       
  9383     browsers: "All",
       
  9384     tags: ["Accessibility"],
       
  9385 
       
  9386     // initialization
       
  9387     init: function(parser, reporter) {
       
  9388         "use strict";
       
  9389         var rule = this,
       
  9390             lastRule;
       
  9391 
       
  9392         function startRule(event) {
       
  9393             if (event.selectors) {
       
  9394                 lastRule = {
       
  9395                     line: event.line,
       
  9396                     col: event.col,
       
  9397                     selectors: event.selectors,
       
  9398                     propCount: 0,
       
  9399                     outline: false
       
  9400                 };
       
  9401             } else {
       
  9402                 lastRule = null;
       
  9403             }
       
  9404         }
       
  9405 
       
  9406         function endRule() {
       
  9407             if (lastRule) {
       
  9408                 if (lastRule.outline) {
       
  9409                     if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") === -1) {
       
  9410                         reporter.report("Outlines should only be modified using :focus.", lastRule.line, lastRule.col, rule);
       
  9411                     } else if (lastRule.propCount === 1) {
       
  9412                         reporter.report("Outlines shouldn't be hidden unless other visual changes are made.", lastRule.line, lastRule.col, rule);
       
  9413                     }
       
  9414                 }
       
  9415             }
       
  9416         }
       
  9417 
       
  9418         parser.addListener("startrule", startRule);
       
  9419         parser.addListener("startfontface", startRule);
       
  9420         parser.addListener("startpage", startRule);
       
  9421         parser.addListener("startpagemargin", startRule);
       
  9422         parser.addListener("startkeyframerule", startRule);
       
  9423         parser.addListener("startviewport", startRule);
       
  9424 
       
  9425         parser.addListener("property", function(event) {
       
  9426             var name = event.property.text.toLowerCase(),
       
  9427                 value = event.value;
       
  9428 
       
  9429             if (lastRule) {
       
  9430                 lastRule.propCount++;
       
  9431                 if (name === "outline" && (value.toString() === "none" || value.toString() === "0")) {
       
  9432                     lastRule.outline = true;
       
  9433                 }
       
  9434             }
       
  9435 
       
  9436         });
       
  9437 
       
  9438         parser.addListener("endrule", endRule);
       
  9439         parser.addListener("endfontface", endRule);
       
  9440         parser.addListener("endpage", endRule);
       
  9441         parser.addListener("endpagemargin", endRule);
       
  9442         parser.addListener("endkeyframerule", endRule);
       
  9443         parser.addListener("endviewport", endRule);
       
  9444 
       
  9445     }
       
  9446 
       
  9447 });
       
  9448 
       
  9449 /*
       
  9450  * Rule: Don't use classes or IDs with elements (a.foo or a#foo).
       
  9451  */
       
  9452 
       
  9453 CSSLint.addRule({
       
  9454 
       
  9455     // rule information
       
  9456     id: "overqualified-elements",
       
  9457     name: "Disallow overqualified elements",
       
  9458     desc: "Don't use classes or IDs with elements (a.foo or a#foo).",
       
  9459     url: "https://github.com/CSSLint/csslint/wiki/Disallow-overqualified-elements",
       
  9460     browsers: "All",
       
  9461 
       
  9462     // initialization
       
  9463     init: function(parser, reporter) {
       
  9464         "use strict";
       
  9465         var rule = this,
       
  9466             classes = {};
       
  9467 
       
  9468         parser.addListener("startrule", function(event) {
       
  9469             var selectors = event.selectors,
       
  9470                 selector,
       
  9471                 part,
       
  9472                 modifier,
       
  9473                 i, j, k;
       
  9474 
       
  9475             for (i=0; i < selectors.length; i++) {
       
  9476                 selector = selectors[i];
       
  9477 
       
  9478                 for (j=0; j < selector.parts.length; j++) {
       
  9479                     part = selector.parts[j];
       
  9480                     if (part.type === parser.SELECTOR_PART_TYPE) {
       
  9481                         for (k=0; k < part.modifiers.length; k++) {
       
  9482                             modifier = part.modifiers[k];
       
  9483                             if (part.elementName && modifier.type === "id") {
       
  9484                                 reporter.report("Element (" + part + ") is overqualified, just use " + modifier + " without element name.", part.line, part.col, rule);
       
  9485                             } else if (modifier.type === "class") {
       
  9486 
       
  9487                                 if (!classes[modifier]) {
       
  9488                                     classes[modifier] = [];
       
  9489                                 }
       
  9490                                 classes[modifier].push({
       
  9491                                     modifier: modifier,
       
  9492                                     part: part
       
  9493                                 });
       
  9494                             }
       
  9495                         }
       
  9496                     }
       
  9497                 }
       
  9498             }
       
  9499         });
       
  9500 
       
  9501         parser.addListener("endstylesheet", function() {
       
  9502 
       
  9503             var prop;
       
  9504             for (prop in classes) {
       
  9505                 if (classes.hasOwnProperty(prop)) {
       
  9506 
       
  9507                     // one use means that this is overqualified
       
  9508                     if (classes[prop].length === 1 && classes[prop][0].part.elementName) {
       
  9509                         reporter.report("Element (" + classes[prop][0].part + ") is overqualified, just use " + classes[prop][0].modifier + " without element name.", classes[prop][0].part.line, classes[prop][0].part.col, rule);
       
  9510                     }
       
  9511                 }
       
  9512             }
       
  9513         });
       
  9514     }
       
  9515 
       
  9516 });
       
  9517 
       
  9518 /*
       
  9519  * Rule: Headings (h1-h6) should not be qualified (namespaced).
       
  9520  */
       
  9521 
       
  9522 CSSLint.addRule({
       
  9523 
       
  9524     // rule information
       
  9525     id: "qualified-headings",
       
  9526     name: "Disallow qualified headings",
       
  9527     desc: "Headings should not be qualified (namespaced).",
       
  9528     url: "https://github.com/CSSLint/csslint/wiki/Disallow-qualified-headings",
       
  9529     browsers: "All",
       
  9530 
       
  9531     // initialization
       
  9532     init: function(parser, reporter) {
       
  9533         "use strict";
       
  9534         var rule = this;
       
  9535 
       
  9536         parser.addListener("startrule", function(event) {
       
  9537             var selectors = event.selectors,
       
  9538                 selector,
       
  9539                 part,
       
  9540                 i, j;
       
  9541 
       
  9542             for (i=0; i < selectors.length; i++) {
       
  9543                 selector = selectors[i];
       
  9544 
       
  9545                 for (j=0; j < selector.parts.length; j++) {
       
  9546                     part = selector.parts[j];
       
  9547                     if (part.type === parser.SELECTOR_PART_TYPE) {
       
  9548                         if (part.elementName && /h[1-6]/.test(part.elementName.toString()) && j > 0) {
       
  9549                             reporter.report("Heading (" + part.elementName + ") should not be qualified.", part.line, part.col, rule);
       
  9550                         }
       
  9551                     }
       
  9552                 }
       
  9553             }
       
  9554         });
       
  9555     }
       
  9556 
       
  9557 });
       
  9558 
       
  9559 /*
       
  9560  * Rule: Selectors that look like regular expressions are slow and should be avoided.
       
  9561  */
       
  9562 
       
  9563 CSSLint.addRule({
       
  9564 
       
  9565     // rule information
       
  9566     id: "regex-selectors",
       
  9567     name: "Disallow selectors that look like regexs",
       
  9568     desc: "Selectors that look like regular expressions are slow and should be avoided.",
       
  9569     url: "https://github.com/CSSLint/csslint/wiki/Disallow-selectors-that-look-like-regular-expressions",
       
  9570     browsers: "All",
       
  9571 
       
  9572     // initialization
       
  9573     init: function(parser, reporter) {
       
  9574         "use strict";
       
  9575         var rule = this;
       
  9576 
       
  9577         parser.addListener("startrule", function(event) {
       
  9578             var selectors = event.selectors,
       
  9579                 selector,
       
  9580                 part,
       
  9581                 modifier,
       
  9582                 i, j, k;
       
  9583 
       
  9584             for (i=0; i < selectors.length; i++) {
       
  9585                 selector = selectors[i];
       
  9586                 for (j=0; j < selector.parts.length; j++) {
       
  9587                     part = selector.parts[j];
       
  9588                     if (part.type === parser.SELECTOR_PART_TYPE) {
       
  9589                         for (k=0; k < part.modifiers.length; k++) {
       
  9590                             modifier = part.modifiers[k];
       
  9591                             if (modifier.type === "attribute") {
       
  9592                                 if (/([~\|\^\$\*]=)/.test(modifier)) {
       
  9593                                     reporter.report("Attribute selectors with " + RegExp.$1 + " are slow!", modifier.line, modifier.col, rule);
       
  9594                                 }
       
  9595                             }
       
  9596 
       
  9597                         }
       
  9598                     }
       
  9599                 }
       
  9600             }
       
  9601         });
       
  9602     }
       
  9603 
       
  9604 });
       
  9605 
       
  9606 /*
       
  9607  * Rule: Total number of rules should not exceed x.
       
  9608  */
       
  9609 
       
  9610 CSSLint.addRule({
       
  9611 
       
  9612     // rule information
       
  9613     id: "rules-count",
       
  9614     name: "Rules Count",
       
  9615     desc: "Track how many rules there are.",
       
  9616     browsers: "All",
       
  9617 
       
  9618     // initialization
       
  9619     init: function(parser, reporter) {
       
  9620         "use strict";
       
  9621         var count = 0;
       
  9622 
       
  9623         // count each rule
       
  9624         parser.addListener("startrule", function() {
       
  9625             count++;
       
  9626         });
       
  9627 
       
  9628         parser.addListener("endstylesheet", function() {
       
  9629             reporter.stat("rule-count", count);
       
  9630         });
       
  9631     }
       
  9632 
       
  9633 });
       
  9634 
       
  9635 /*
       
  9636  * Rule: Warn people with approaching the IE 4095 limit
       
  9637  */
       
  9638 
       
  9639 CSSLint.addRule({
       
  9640 
       
  9641     // rule information
       
  9642     id: "selector-max-approaching",
       
  9643     name: "Warn when approaching the 4095 selector limit for IE",
       
  9644     desc: "Will warn when selector count is >= 3800 selectors.",
       
  9645     browsers: "IE",
       
  9646 
       
  9647     // initialization
       
  9648     init: function(parser, reporter) {
       
  9649         "use strict";
       
  9650         var rule = this, count = 0;
       
  9651 
       
  9652         parser.addListener("startrule", function(event) {
       
  9653             count += event.selectors.length;
       
  9654         });
       
  9655 
       
  9656         parser.addListener("endstylesheet", function() {
       
  9657             if (count >= 3800) {
       
  9658                 reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule);
       
  9659             }
       
  9660         });
       
  9661     }
       
  9662 
       
  9663 });
       
  9664 
       
  9665 /*
       
  9666  * Rule: Warn people past the IE 4095 limit
       
  9667  */
       
  9668 
       
  9669 CSSLint.addRule({
       
  9670 
       
  9671     // rule information
       
  9672     id: "selector-max",
       
  9673     name: "Error when past the 4095 selector limit for IE",
       
  9674     desc: "Will error when selector count is > 4095.",
       
  9675     browsers: "IE",
       
  9676 
       
  9677     // initialization
       
  9678     init: function(parser, reporter) {
       
  9679         "use strict";
       
  9680         var rule = this, count = 0;
       
  9681 
       
  9682         parser.addListener("startrule", function(event) {
       
  9683             count += event.selectors.length;
       
  9684         });
       
  9685 
       
  9686         parser.addListener("endstylesheet", function() {
       
  9687             if (count > 4095) {
       
  9688                 reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule);
       
  9689             }
       
  9690         });
       
  9691     }
       
  9692 
       
  9693 });
       
  9694 
       
  9695 /*
       
  9696  * Rule: Avoid new-line characters in selectors.
       
  9697  */
       
  9698 
       
  9699 CSSLint.addRule({
       
  9700 
       
  9701     // rule information
       
  9702     id: "selector-newline",
       
  9703     name: "Disallow new-line characters in selectors",
       
  9704     desc: "New-line characters in selectors are usually a forgotten comma and not a descendant combinator.",
       
  9705     browsers: "All",
       
  9706 
       
  9707     // initialization
       
  9708     init: function(parser, reporter) {
       
  9709         "use strict";
       
  9710         var rule = this;
       
  9711 
       
  9712         function startRule(event) {
       
  9713             var i, len, selector, p, n, pLen, part, part2, type, currentLine, nextLine,
       
  9714                 selectors = event.selectors;
       
  9715 
       
  9716             for (i = 0, len = selectors.length; i < len; i++) {
       
  9717                 selector = selectors[i];
       
  9718                 for (p = 0, pLen = selector.parts.length; p < pLen; p++) {
       
  9719                     for (n = p + 1; n < pLen; n++) {
       
  9720                         part = selector.parts[p];
       
  9721                         part2 = selector.parts[n];
       
  9722                         type = part.type;
       
  9723                         currentLine = part.line;
       
  9724                         nextLine = part2.line;
       
  9725 
       
  9726                         if (type === "descendant" && nextLine > currentLine) {
       
  9727                             reporter.report("newline character found in selector (forgot a comma?)", currentLine, selectors[i].parts[0].col, rule);
       
  9728                         }
       
  9729                     }
       
  9730                 }
       
  9731 
       
  9732             }
       
  9733         }
       
  9734 
       
  9735         parser.addListener("startrule", startRule);
       
  9736 
       
  9737     }
       
  9738 });
       
  9739 
       
  9740 /*
       
  9741  * Rule: Use shorthand properties where possible.
       
  9742  *
       
  9743  */
       
  9744 
       
  9745 CSSLint.addRule({
       
  9746 
       
  9747     // rule information
       
  9748     id: "shorthand",
       
  9749     name: "Require shorthand properties",
       
  9750     desc: "Use shorthand properties where possible.",
       
  9751     url: "https://github.com/CSSLint/csslint/wiki/Require-shorthand-properties",
       
  9752     browsers: "All",
       
  9753 
       
  9754     // initialization
       
  9755     init: function(parser, reporter) {
       
  9756         "use strict";
       
  9757         var rule = this,
       
  9758             prop, i, len,
       
  9759             propertiesToCheck = {},
       
  9760             properties,
       
  9761             mapping = {
       
  9762                 "margin": [
       
  9763                     "margin-top",
       
  9764                     "margin-bottom",
       
  9765                     "margin-left",
       
  9766                     "margin-right"
       
  9767                 ],
       
  9768                 "padding": [
       
  9769                     "padding-top",
       
  9770                     "padding-bottom",
       
  9771                     "padding-left",
       
  9772                     "padding-right"
       
  9773                 ]
       
  9774             };
       
  9775 
       
  9776         // initialize propertiesToCheck
       
  9777         for (prop in mapping) {
       
  9778             if (mapping.hasOwnProperty(prop)) {
       
  9779                 for (i=0, len=mapping[prop].length; i < len; i++) {
       
  9780                     propertiesToCheck[mapping[prop][i]] = prop;
       
  9781                 }
       
  9782             }
       
  9783         }
       
  9784 
       
  9785         function startRule() {
       
  9786             properties = {};
       
  9787         }
       
  9788 
       
  9789         // event handler for end of rules
       
  9790         function endRule(event) {
       
  9791 
       
  9792             var prop, i, len, total;
       
  9793 
       
  9794             // check which properties this rule has
       
  9795             for (prop in mapping) {
       
  9796                 if (mapping.hasOwnProperty(prop)) {
       
  9797                     total=0;
       
  9798 
       
  9799                     for (i=0, len=mapping[prop].length; i < len; i++) {
       
  9800                         total += properties[mapping[prop][i]] ? 1 : 0;
       
  9801                     }
       
  9802 
       
  9803                     if (total === mapping[prop].length) {
       
  9804                         reporter.report("The properties " + mapping[prop].join(", ") + " can be replaced by " + prop + ".", event.line, event.col, rule);
       
  9805                     }
       
  9806                 }
       
  9807             }
       
  9808         }
       
  9809 
       
  9810         parser.addListener("startrule", startRule);
       
  9811         parser.addListener("startfontface", startRule);
       
  9812 
       
  9813         // check for use of "font-size"
       
  9814         parser.addListener("property", function(event) {
       
  9815             var name = event.property.toString().toLowerCase();
       
  9816 
       
  9817             if (propertiesToCheck[name]) {
       
  9818                 properties[name] = 1;
       
  9819             }
       
  9820         });
       
  9821 
       
  9822         parser.addListener("endrule", endRule);
       
  9823         parser.addListener("endfontface", endRule);
       
  9824 
       
  9825     }
       
  9826 
       
  9827 });
       
  9828 
       
  9829 /*
       
  9830  * Rule: Don't use properties with a star prefix.
       
  9831  *
       
  9832  */
       
  9833 
       
  9834 CSSLint.addRule({
       
  9835 
       
  9836     // rule information
       
  9837     id: "star-property-hack",
       
  9838     name: "Disallow properties with a star prefix",
       
  9839     desc: "Checks for the star property hack (targets IE6/7)",
       
  9840     url: "https://github.com/CSSLint/csslint/wiki/Disallow-star-hack",
       
  9841     browsers: "All",
       
  9842 
       
  9843     // initialization
       
  9844     init: function(parser, reporter) {
       
  9845         "use strict";
       
  9846         var rule = this;
       
  9847 
       
  9848         // check if property name starts with "*"
       
  9849         parser.addListener("property", function(event) {
       
  9850             var property = event.property;
       
  9851 
       
  9852             if (property.hack === "*") {
       
  9853                 reporter.report("Property with star prefix found.", event.property.line, event.property.col, rule);
       
  9854             }
       
  9855         });
       
  9856     }
       
  9857 });
       
  9858 
       
  9859 /*
       
  9860  * Rule: Don't use text-indent for image replacement if you need to support rtl.
       
  9861  *
       
  9862  */
       
  9863 
       
  9864 CSSLint.addRule({
       
  9865 
       
  9866     // rule information
       
  9867     id: "text-indent",
       
  9868     name: "Disallow negative text-indent",
       
  9869     desc: "Checks for text indent less than -99px",
       
  9870     url: "https://github.com/CSSLint/csslint/wiki/Disallow-negative-text-indent",
       
  9871     browsers: "All",
       
  9872 
       
  9873     // initialization
       
  9874     init: function(parser, reporter) {
       
  9875         "use strict";
       
  9876         var rule = this,
       
  9877             textIndent,
       
  9878             direction;
       
  9879 
       
  9880 
       
  9881         function startRule() {
       
  9882             textIndent = false;
       
  9883             direction = "inherit";
       
  9884         }
       
  9885 
       
  9886         // event handler for end of rules
       
  9887         function endRule() {
       
  9888             if (textIndent && direction !== "ltr") {
       
  9889                 reporter.report("Negative text-indent doesn't work well with RTL. If you use text-indent for image replacement explicitly set direction for that item to ltr.", textIndent.line, textIndent.col, rule);
       
  9890             }
       
  9891         }
       
  9892 
       
  9893         parser.addListener("startrule", startRule);
       
  9894         parser.addListener("startfontface", startRule);
       
  9895 
       
  9896         // check for use of "font-size"
       
  9897         parser.addListener("property", function(event) {
       
  9898             var name = event.property.toString().toLowerCase(),
       
  9899                 value = event.value;
       
  9900 
       
  9901             if (name === "text-indent" && value.parts[0].value < -99) {
       
  9902                 textIndent = event.property;
       
  9903             } else if (name === "direction" && value.toString() === "ltr") {
       
  9904                 direction = "ltr";
       
  9905             }
       
  9906         });
       
  9907 
       
  9908         parser.addListener("endrule", endRule);
       
  9909         parser.addListener("endfontface", endRule);
       
  9910 
       
  9911     }
       
  9912 
       
  9913 });
       
  9914 
       
  9915 /*
       
  9916  * Rule: Don't use properties with a underscore prefix.
       
  9917  *
       
  9918  */
       
  9919 
       
  9920 CSSLint.addRule({
       
  9921 
       
  9922     // rule information
       
  9923     id: "underscore-property-hack",
       
  9924     name: "Disallow properties with an underscore prefix",
       
  9925     desc: "Checks for the underscore property hack (targets IE6)",
       
  9926     url: "https://github.com/CSSLint/csslint/wiki/Disallow-underscore-hack",
       
  9927     browsers: "All",
       
  9928 
       
  9929     // initialization
       
  9930     init: function(parser, reporter) {
       
  9931         "use strict";
       
  9932         var rule = this;
       
  9933 
       
  9934         // check if property name starts with "_"
       
  9935         parser.addListener("property", function(event) {
       
  9936             var property = event.property;
       
  9937 
       
  9938             if (property.hack === "_") {
       
  9939                 reporter.report("Property with underscore prefix found.", event.property.line, event.property.col, rule);
       
  9940             }
       
  9941         });
       
  9942     }
       
  9943 });
       
  9944 
       
  9945 /*
       
  9946  * Rule: Headings (h1-h6) should be defined only once.
       
  9947  */
       
  9948 
       
  9949 CSSLint.addRule({
       
  9950 
       
  9951     // rule information
       
  9952     id: "unique-headings",
       
  9953     name: "Headings should only be defined once",
       
  9954     desc: "Headings should be defined only once.",
       
  9955     url: "https://github.com/CSSLint/csslint/wiki/Headings-should-only-be-defined-once",
       
  9956     browsers: "All",
       
  9957 
       
  9958     // initialization
       
  9959     init: function(parser, reporter) {
       
  9960         "use strict";
       
  9961         var rule = this;
       
  9962 
       
  9963         var headings = {
       
  9964             h1: 0,
       
  9965             h2: 0,
       
  9966             h3: 0,
       
  9967             h4: 0,
       
  9968             h5: 0,
       
  9969             h6: 0
       
  9970         };
       
  9971 
       
  9972         parser.addListener("startrule", function(event) {
       
  9973             var selectors = event.selectors,
       
  9974                 selector,
       
  9975                 part,
       
  9976                 pseudo,
       
  9977                 i, j;
       
  9978 
       
  9979             for (i=0; i < selectors.length; i++) {
       
  9980                 selector = selectors[i];
       
  9981                 part = selector.parts[selector.parts.length-1];
       
  9982 
       
  9983                 if (part.elementName && /(h[1-6])/i.test(part.elementName.toString())) {
       
  9984 
       
  9985                     for (j=0; j < part.modifiers.length; j++) {
       
  9986                         if (part.modifiers[j].type === "pseudo") {
       
  9987                             pseudo = true;
       
  9988                             break;
       
  9989                         }
       
  9990                     }
       
  9991 
       
  9992                     if (!pseudo) {
       
  9993                         headings[RegExp.$1]++;
       
  9994                         if (headings[RegExp.$1] > 1) {
       
  9995                             reporter.report("Heading (" + part.elementName + ") has already been defined.", part.line, part.col, rule);
       
  9996                         }
       
  9997                     }
       
  9998                 }
       
  9999             }
       
 10000         });
       
 10001 
       
 10002         parser.addListener("endstylesheet", function() {
       
 10003             var prop,
       
 10004                 messages = [];
       
 10005 
       
 10006             for (prop in headings) {
       
 10007                 if (headings.hasOwnProperty(prop)) {
       
 10008                     if (headings[prop] > 1) {
       
 10009                         messages.push(headings[prop] + " " + prop + "s");
       
 10010                     }
       
 10011                 }
       
 10012             }
       
 10013 
       
 10014             if (messages.length) {
       
 10015                 reporter.rollupWarn("You have " + messages.join(", ") + " defined in this stylesheet.", rule);
       
 10016             }
       
 10017         });
       
 10018     }
       
 10019 
       
 10020 });
       
 10021 
       
 10022 /*
       
 10023  * Rule: Don't use universal selector because it's slow.
       
 10024  */
       
 10025 
       
 10026 CSSLint.addRule({
       
 10027 
       
 10028     // rule information
       
 10029     id: "universal-selector",
       
 10030     name: "Disallow universal selector",
       
 10031     desc: "The universal selector (*) is known to be slow.",
       
 10032     url: "https://github.com/CSSLint/csslint/wiki/Disallow-universal-selector",
       
 10033     browsers: "All",
       
 10034 
       
 10035     // initialization
       
 10036     init: function(parser, reporter) {
       
 10037         "use strict";
       
 10038         var rule = this;
       
 10039 
       
 10040         parser.addListener("startrule", function(event) {
       
 10041             var selectors = event.selectors,
       
 10042                 selector,
       
 10043                 part,
       
 10044                 i;
       
 10045 
       
 10046             for (i=0; i < selectors.length; i++) {
       
 10047                 selector = selectors[i];
       
 10048 
       
 10049                 part = selector.parts[selector.parts.length-1];
       
 10050                 if (part.elementName === "*") {
       
 10051                     reporter.report(rule.desc, part.line, part.col, rule);
       
 10052                 }
       
 10053             }
       
 10054         });
       
 10055     }
       
 10056 
       
 10057 });
       
 10058 
       
 10059 /*
       
 10060  * Rule: Don't use unqualified attribute selectors because they're just like universal selectors.
       
 10061  */
       
 10062 
       
 10063 CSSLint.addRule({
       
 10064 
       
 10065     // rule information
       
 10066     id: "unqualified-attributes",
       
 10067     name: "Disallow unqualified attribute selectors",
       
 10068     desc: "Unqualified attribute selectors are known to be slow.",
       
 10069     url: "https://github.com/CSSLint/csslint/wiki/Disallow-unqualified-attribute-selectors",
       
 10070     browsers: "All",
       
 10071 
       
 10072     // initialization
       
 10073     init: function(parser, reporter) {
       
 10074         "use strict";
       
 10075 
       
 10076         var rule = this;
       
 10077 
       
 10078         parser.addListener("startrule", function(event) {
       
 10079 
       
 10080             var selectors = event.selectors,
       
 10081                 selectorContainsClassOrId = false,
       
 10082                 selector,
       
 10083                 part,
       
 10084                 modifier,
       
 10085                 i, k;
       
 10086 
       
 10087             for (i=0; i < selectors.length; i++) {
       
 10088                 selector = selectors[i];
       
 10089 
       
 10090                 part = selector.parts[selector.parts.length-1];
       
 10091                 if (part.type === parser.SELECTOR_PART_TYPE) {
       
 10092                     for (k=0; k < part.modifiers.length; k++) {
       
 10093                         modifier = part.modifiers[k];
       
 10094 
       
 10095                         if (modifier.type === "class" || modifier.type === "id") {
       
 10096                             selectorContainsClassOrId = true;
       
 10097                             break;
       
 10098                         }
       
 10099                     }
       
 10100 
       
 10101                     if (!selectorContainsClassOrId) {
       
 10102                         for (k=0; k < part.modifiers.length; k++) {
       
 10103                             modifier = part.modifiers[k];
       
 10104                             if (modifier.type === "attribute" && (!part.elementName || part.elementName === "*")) {
       
 10105                                 reporter.report(rule.desc, part.line, part.col, rule);
       
 10106                             }
       
 10107                         }
       
 10108                     }
       
 10109                 }
       
 10110 
       
 10111             }
       
 10112         });
       
 10113     }
       
 10114 
       
 10115 });
       
 10116 
       
 10117 /*
       
 10118  * Rule: When using a vendor-prefixed property, make sure to
       
 10119  * include the standard one.
       
 10120  */
       
 10121 
       
 10122 CSSLint.addRule({
       
 10123 
       
 10124     // rule information
       
 10125     id: "vendor-prefix",
       
 10126     name: "Require standard property with vendor prefix",
       
 10127     desc: "When using a vendor-prefixed property, make sure to include the standard one.",
       
 10128     url: "https://github.com/CSSLint/csslint/wiki/Require-standard-property-with-vendor-prefix",
       
 10129     browsers: "All",
       
 10130 
       
 10131     // initialization
       
 10132     init: function(parser, reporter) {
       
 10133         "use strict";
       
 10134         var rule = this,
       
 10135             properties,
       
 10136             num,
       
 10137             propertiesToCheck = {
       
 10138                 "-webkit-border-radius": "border-radius",
       
 10139                 "-webkit-border-top-left-radius": "border-top-left-radius",
       
 10140                 "-webkit-border-top-right-radius": "border-top-right-radius",
       
 10141                 "-webkit-border-bottom-left-radius": "border-bottom-left-radius",
       
 10142                 "-webkit-border-bottom-right-radius": "border-bottom-right-radius",
       
 10143 
       
 10144                 "-o-border-radius": "border-radius",
       
 10145                 "-o-border-top-left-radius": "border-top-left-radius",
       
 10146                 "-o-border-top-right-radius": "border-top-right-radius",
       
 10147                 "-o-border-bottom-left-radius": "border-bottom-left-radius",
       
 10148                 "-o-border-bottom-right-radius": "border-bottom-right-radius",
       
 10149 
       
 10150                 "-moz-border-radius": "border-radius",
       
 10151                 "-moz-border-radius-topleft": "border-top-left-radius",
       
 10152                 "-moz-border-radius-topright": "border-top-right-radius",
       
 10153                 "-moz-border-radius-bottomleft": "border-bottom-left-radius",
       
 10154                 "-moz-border-radius-bottomright": "border-bottom-right-radius",
       
 10155 
       
 10156                 "-moz-column-count": "column-count",
       
 10157                 "-webkit-column-count": "column-count",
       
 10158 
       
 10159                 "-moz-column-gap": "column-gap",
       
 10160                 "-webkit-column-gap": "column-gap",
       
 10161 
       
 10162                 "-moz-column-rule": "column-rule",
       
 10163                 "-webkit-column-rule": "column-rule",
       
 10164 
       
 10165                 "-moz-column-rule-style": "column-rule-style",
       
 10166                 "-webkit-column-rule-style": "column-rule-style",
       
 10167 
       
 10168                 "-moz-column-rule-color": "column-rule-color",
       
 10169                 "-webkit-column-rule-color": "column-rule-color",
       
 10170 
       
 10171                 "-moz-column-rule-width": "column-rule-width",
       
 10172                 "-webkit-column-rule-width": "column-rule-width",
       
 10173 
       
 10174                 "-moz-column-width": "column-width",
       
 10175                 "-webkit-column-width": "column-width",
       
 10176 
       
 10177                 "-webkit-column-span": "column-span",
       
 10178                 "-webkit-columns": "columns",
       
 10179 
       
 10180                 "-moz-box-shadow": "box-shadow",
       
 10181                 "-webkit-box-shadow": "box-shadow",
       
 10182 
       
 10183                 "-moz-transform": "transform",
       
 10184                 "-webkit-transform": "transform",
       
 10185                 "-o-transform": "transform",
       
 10186                 "-ms-transform": "transform",
       
 10187 
       
 10188                 "-moz-transform-origin": "transform-origin",
       
 10189                 "-webkit-transform-origin": "transform-origin",
       
 10190                 "-o-transform-origin": "transform-origin",
       
 10191                 "-ms-transform-origin": "transform-origin",
       
 10192 
       
 10193                 "-moz-box-sizing": "box-sizing",
       
 10194                 "-webkit-box-sizing": "box-sizing"
       
 10195             };
       
 10196 
       
 10197         // event handler for beginning of rules
       
 10198         function startRule() {
       
 10199             properties = {};
       
 10200             num = 1;
       
 10201         }
       
 10202 
       
 10203         // event handler for end of rules
       
 10204         function endRule() {
       
 10205             var prop,
       
 10206                 i,
       
 10207                 len,
       
 10208                 needed,
       
 10209                 actual,
       
 10210                 needsStandard = [];
       
 10211 
       
 10212             for (prop in properties) {
       
 10213                 if (propertiesToCheck[prop]) {
       
 10214                     needsStandard.push({
       
 10215                         actual: prop,
       
 10216                         needed: propertiesToCheck[prop]
       
 10217                     });
       
 10218                 }
       
 10219             }
       
 10220 
       
 10221             for (i=0, len=needsStandard.length; i < len; i++) {
       
 10222                 needed = needsStandard[i].needed;
       
 10223                 actual = needsStandard[i].actual;
       
 10224 
       
 10225                 if (!properties[needed]) {
       
 10226                     reporter.report("Missing standard property '" + needed + "' to go along with '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
       
 10227                 } else {
       
 10228                     // make sure standard property is last
       
 10229                     if (properties[needed][0].pos < properties[actual][0].pos) {
       
 10230                         reporter.report("Standard property '" + needed + "' should come after vendor-prefixed property '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
       
 10231                     }
       
 10232                 }
       
 10233             }
       
 10234 
       
 10235         }
       
 10236 
       
 10237         parser.addListener("startrule", startRule);
       
 10238         parser.addListener("startfontface", startRule);
       
 10239         parser.addListener("startpage", startRule);
       
 10240         parser.addListener("startpagemargin", startRule);
       
 10241         parser.addListener("startkeyframerule", startRule);
       
 10242         parser.addListener("startviewport", startRule);
       
 10243 
       
 10244         parser.addListener("property", function(event) {
       
 10245             var name = event.property.text.toLowerCase();
       
 10246 
       
 10247             if (!properties[name]) {
       
 10248                 properties[name] = [];
       
 10249             }
       
 10250 
       
 10251             properties[name].push({
       
 10252                 name: event.property,
       
 10253                 value: event.value,
       
 10254                 pos: num++
       
 10255             });
       
 10256         });
       
 10257 
       
 10258         parser.addListener("endrule", endRule);
       
 10259         parser.addListener("endfontface", endRule);
       
 10260         parser.addListener("endpage", endRule);
       
 10261         parser.addListener("endpagemargin", endRule);
       
 10262         parser.addListener("endkeyframerule", endRule);
       
 10263         parser.addListener("endviewport", endRule);
       
 10264     }
       
 10265 
       
 10266 });
       
 10267 
       
 10268 /*
       
 10269  * Rule: You don't need to specify units when a value is 0.
       
 10270  */
       
 10271 
       
 10272 CSSLint.addRule({
       
 10273 
       
 10274     // rule information
       
 10275     id: "zero-units",
       
 10276     name: "Disallow units for 0 values",
       
 10277     desc: "You don't need to specify units when a value is 0.",
       
 10278     url: "https://github.com/CSSLint/csslint/wiki/Disallow-units-for-zero-values",
       
 10279     browsers: "All",
       
 10280 
       
 10281     // initialization
       
 10282     init: function(parser, reporter) {
       
 10283         "use strict";
       
 10284         var rule = this;
       
 10285 
       
 10286         // count how many times "float" is used
       
 10287         parser.addListener("property", function(event) {
       
 10288             var parts = event.value.parts,
       
 10289                 i = 0,
       
 10290                 len = parts.length;
       
 10291 
       
 10292             while (i < len) {
       
 10293                 if ((parts[i].units || parts[i].type === "percentage") && parts[i].value === 0 && parts[i].type !== "time") {
       
 10294                     reporter.report("Values of 0 shouldn't have units specified.", parts[i].line, parts[i].col, rule);
       
 10295                 }
       
 10296                 i++;
       
 10297             }
       
 10298 
       
 10299         });
       
 10300 
       
 10301     }
       
 10302 
       
 10303 });
       
 10304 
       
 10305 (function() {
       
 10306     "use strict";
       
 10307 
       
 10308     /**
       
 10309      * Replace special characters before write to output.
       
 10310      *
       
 10311      * Rules:
       
 10312      *  - single quotes is the escape sequence for double-quotes
       
 10313      *  - &amp; is the escape sequence for &
       
 10314      *  - &lt; is the escape sequence for <
       
 10315      *  - &gt; is the escape sequence for >
       
 10316      *
       
 10317      * @param {String} message to escape
       
 10318      * @return escaped message as {String}
       
 10319      */
       
 10320     var xmlEscape = function(str) {
       
 10321         if (!str || str.constructor !== String) {
       
 10322             return "";
       
 10323         }
       
 10324 
       
 10325         return str.replace(/["&><]/g, function(match) {
       
 10326             switch (match) {
       
 10327                 case "\"":
       
 10328                     return "&quot;";
       
 10329                 case "&":
       
 10330                     return "&amp;";
       
 10331                 case "<":
       
 10332                     return "&lt;";
       
 10333                 case ">":
       
 10334                     return "&gt;";
       
 10335             }
       
 10336         });
       
 10337     };
       
 10338 
       
 10339     CSSLint.addFormatter({
       
 10340         // format information
       
 10341         id: "checkstyle-xml",
       
 10342         name: "Checkstyle XML format",
       
 10343 
       
 10344         /**
       
 10345          * Return opening root XML tag.
       
 10346          * @return {String} to prepend before all results
       
 10347          */
       
 10348         startFormat: function() {
       
 10349             return "<?xml version=\"1.0\" encoding=\"utf-8\"?><checkstyle>";
       
 10350         },
       
 10351 
       
 10352         /**
       
 10353          * Return closing root XML tag.
       
 10354          * @return {String} to append after all results
       
 10355          */
       
 10356         endFormat: function() {
       
 10357             return "</checkstyle>";
       
 10358         },
       
 10359 
       
 10360         /**
       
 10361          * Returns message when there is a file read error.
       
 10362          * @param {String} filename The name of the file that caused the error.
       
 10363          * @param {String} message The error message
       
 10364          * @return {String} The error message.
       
 10365          */
       
 10366         readError: function(filename, message) {
       
 10367             return "<file name=\"" + xmlEscape(filename) + "\"><error line=\"0\" column=\"0\" severty=\"error\" message=\"" + xmlEscape(message) + "\"></error></file>";
       
 10368         },
       
 10369 
       
 10370         /**
       
 10371          * Given CSS Lint results for a file, return output for this format.
       
 10372          * @param results {Object} with error and warning messages
       
 10373          * @param filename {String} relative file path
       
 10374          * @param options {Object} (UNUSED for now) specifies special handling of output
       
 10375          * @return {String} output for results
       
 10376          */
       
 10377         formatResults: function(results, filename/*, options*/) {
       
 10378             var messages = results.messages,
       
 10379                 output = [];
       
 10380 
       
 10381             /**
       
 10382              * Generate a source string for a rule.
       
 10383              * Checkstyle source strings usually resemble Java class names e.g
       
 10384              * net.csslint.SomeRuleName
       
 10385              * @param {Object} rule
       
 10386              * @return rule source as {String}
       
 10387              */
       
 10388             var generateSource = function(rule) {
       
 10389                 if (!rule || !("name" in rule)) {
       
 10390                     return "";
       
 10391                 }
       
 10392                 return "net.csslint." + rule.name.replace(/\s/g, "");
       
 10393             };
       
 10394 
       
 10395 
       
 10396             if (messages.length > 0) {
       
 10397                 output.push("<file name=\""+filename+"\">");
       
 10398                 CSSLint.Util.forEach(messages, function (message) {
       
 10399                     // ignore rollups for now
       
 10400                     if (!message.rollup) {
       
 10401                         output.push("<error line=\"" + message.line + "\" column=\"" + message.col + "\" severity=\"" + message.type + "\"" +
       
 10402                           " message=\"" + xmlEscape(message.message) + "\" source=\"" + generateSource(message.rule) +"\"/>");
       
 10403                     }
       
 10404                 });
       
 10405                 output.push("</file>");
       
 10406             }
       
 10407 
       
 10408             return output.join("");
       
 10409         }
       
 10410     });
       
 10411 
       
 10412 }());
       
 10413 
       
 10414 CSSLint.addFormatter({
       
 10415     // format information
       
 10416     id: "compact",
       
 10417     name: "Compact, 'porcelain' format",
       
 10418 
       
 10419     /**
       
 10420      * Return content to be printed before all file results.
       
 10421      * @return {String} to prepend before all results
       
 10422      */
       
 10423     startFormat: function() {
       
 10424         "use strict";
       
 10425         return "";
       
 10426     },
       
 10427 
       
 10428     /**
       
 10429      * Return content to be printed after all file results.
       
 10430      * @return {String} to append after all results
       
 10431      */
       
 10432     endFormat: function() {
       
 10433         "use strict";
       
 10434         return "";
       
 10435     },
       
 10436 
       
 10437     /**
       
 10438      * Given CSS Lint results for a file, return output for this format.
       
 10439      * @param results {Object} with error and warning messages
       
 10440      * @param filename {String} relative file path
       
 10441      * @param options {Object} (Optional) specifies special handling of output
       
 10442      * @return {String} output for results
       
 10443      */
       
 10444     formatResults: function(results, filename, options) {
       
 10445         "use strict";
       
 10446         var messages = results.messages,
       
 10447             output = "";
       
 10448         options = options || {};
       
 10449 
       
 10450         /**
       
 10451          * Capitalize and return given string.
       
 10452          * @param str {String} to capitalize
       
 10453          * @return {String} capitalized
       
 10454          */
       
 10455         var capitalize = function(str) {
       
 10456             return str.charAt(0).toUpperCase() + str.slice(1);
       
 10457         };
       
 10458 
       
 10459         if (messages.length === 0) {
       
 10460             return options.quiet ? "" : filename + ": Lint Free!";
       
 10461         }
       
 10462 
       
 10463         CSSLint.Util.forEach(messages, function(message) {
       
 10464             if (message.rollup) {
       
 10465                 output += filename + ": " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
       
 10466             } else {
       
 10467                 output += filename + ": line " + message.line +
       
 10468                     ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
       
 10469             }
       
 10470         });
       
 10471 
       
 10472         return output;
       
 10473     }
       
 10474 });
       
 10475 
       
 10476 CSSLint.addFormatter({
       
 10477     // format information
       
 10478     id: "csslint-xml",
       
 10479     name: "CSSLint XML format",
       
 10480 
       
 10481     /**
       
 10482      * Return opening root XML tag.
       
 10483      * @return {String} to prepend before all results
       
 10484      */
       
 10485     startFormat: function() {
       
 10486         "use strict";
       
 10487         return "<?xml version=\"1.0\" encoding=\"utf-8\"?><csslint>";
       
 10488     },
       
 10489 
       
 10490     /**
       
 10491      * Return closing root XML tag.
       
 10492      * @return {String} to append after all results
       
 10493      */
       
 10494     endFormat: function() {
       
 10495         "use strict";
       
 10496         return "</csslint>";
       
 10497     },
       
 10498 
       
 10499     /**
       
 10500      * Given CSS Lint results for a file, return output for this format.
       
 10501      * @param results {Object} with error and warning messages
       
 10502      * @param filename {String} relative file path
       
 10503      * @param options {Object} (UNUSED for now) specifies special handling of output
       
 10504      * @return {String} output for results
       
 10505      */
       
 10506     formatResults: function(results, filename/*, options*/) {
       
 10507         "use strict";
       
 10508         var messages = results.messages,
       
 10509             output = [];
       
 10510 
       
 10511         /**
       
 10512          * Replace special characters before write to output.
       
 10513          *
       
 10514          * Rules:
       
 10515          *  - single quotes is the escape sequence for double-quotes
       
 10516          *  - &amp; is the escape sequence for &
       
 10517          *  - &lt; is the escape sequence for <
       
 10518          *  - &gt; is the escape sequence for >
       
 10519          *
       
 10520          * @param {String} message to escape
       
 10521          * @return escaped message as {String}
       
 10522          */
       
 10523         var escapeSpecialCharacters = function(str) {
       
 10524             if (!str || str.constructor !== String) {
       
 10525                 return "";
       
 10526             }
       
 10527             return str.replace(/"/g, "'").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
       
 10528         };
       
 10529 
       
 10530         if (messages.length > 0) {
       
 10531             output.push("<file name=\""+filename+"\">");
       
 10532             CSSLint.Util.forEach(messages, function (message) {
       
 10533                 if (message.rollup) {
       
 10534                     output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
       
 10535                 } else {
       
 10536                     output.push("<issue line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
       
 10537                         " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
       
 10538                 }
       
 10539             });
       
 10540             output.push("</file>");
       
 10541         }
       
 10542 
       
 10543         return output.join("");
       
 10544     }
       
 10545 });
       
 10546 
       
 10547 /* globals JSON: true */
       
 10548 
       
 10549 CSSLint.addFormatter({
       
 10550     // format information
       
 10551     id: "json",
       
 10552     name: "JSON",
       
 10553 
       
 10554     /**
       
 10555      * Return content to be printed before all file results.
       
 10556      * @return {String} to prepend before all results
       
 10557      */
       
 10558     startFormat: function() {
       
 10559         "use strict";
       
 10560         this.json = [];
       
 10561         return "";
       
 10562     },
       
 10563 
       
 10564     /**
       
 10565      * Return content to be printed after all file results.
       
 10566      * @return {String} to append after all results
       
 10567      */
       
 10568     endFormat: function() {
       
 10569         "use strict";
       
 10570         var ret = "";
       
 10571         if (this.json.length > 0) {
       
 10572             if (this.json.length === 1) {
       
 10573                 ret = JSON.stringify(this.json[0]);
       
 10574             } else {
       
 10575                 ret = JSON.stringify(this.json);
       
 10576             }
       
 10577         }
       
 10578         return ret;
       
 10579     },
       
 10580 
       
 10581     /**
       
 10582      * Given CSS Lint results for a file, return output for this format.
       
 10583      * @param results {Object} with error and warning messages
       
 10584      * @param filename {String} relative file path (Unused)
       
 10585      * @return {String} output for results
       
 10586      */
       
 10587     formatResults: function(results, filename, options) {
       
 10588         "use strict";
       
 10589         if (results.messages.length > 0 || !options.quiet) {
       
 10590             this.json.push({
       
 10591                 filename: filename,
       
 10592                 messages: results.messages,
       
 10593                 stats: results.stats
       
 10594             });
       
 10595         }
       
 10596         return "";
       
 10597     }
       
 10598 });
       
 10599 
       
 10600 CSSLint.addFormatter({
       
 10601     // format information
       
 10602     id: "junit-xml",
       
 10603     name: "JUNIT XML format",
       
 10604 
       
 10605     /**
       
 10606      * Return opening root XML tag.
       
 10607      * @return {String} to prepend before all results
       
 10608      */
       
 10609     startFormat: function() {
       
 10610         "use strict";
       
 10611         return "<?xml version=\"1.0\" encoding=\"utf-8\"?><testsuites>";
       
 10612     },
       
 10613 
       
 10614     /**
       
 10615      * Return closing root XML tag.
       
 10616      * @return {String} to append after all results
       
 10617      */
       
 10618     endFormat: function() {
       
 10619         "use strict";
       
 10620         return "</testsuites>";
       
 10621     },
       
 10622 
       
 10623     /**
       
 10624      * Given CSS Lint results for a file, return output for this format.
       
 10625      * @param results {Object} with error and warning messages
       
 10626      * @param filename {String} relative file path
       
 10627      * @param options {Object} (UNUSED for now) specifies special handling of output
       
 10628      * @return {String} output for results
       
 10629      */
       
 10630     formatResults: function(results, filename/*, options*/) {
       
 10631         "use strict";
       
 10632 
       
 10633         var messages = results.messages,
       
 10634             output = [],
       
 10635             tests = {
       
 10636                 "error": 0,
       
 10637                 "failure": 0
       
 10638             };
       
 10639 
       
 10640         /**
       
 10641          * Generate a source string for a rule.
       
 10642          * JUNIT source strings usually resemble Java class names e.g
       
 10643          * net.csslint.SomeRuleName
       
 10644          * @param {Object} rule
       
 10645          * @return rule source as {String}
       
 10646          */
       
 10647         var generateSource = function(rule) {
       
 10648             if (!rule || !("name" in rule)) {
       
 10649                 return "";
       
 10650             }
       
 10651             return "net.csslint." + rule.name.replace(/\s/g, "");
       
 10652         };
       
 10653 
       
 10654         /**
       
 10655          * Replace special characters before write to output.
       
 10656          *
       
 10657          * Rules:
       
 10658          *  - single quotes is the escape sequence for double-quotes
       
 10659          *  - &lt; is the escape sequence for <
       
 10660          *  - &gt; is the escape sequence for >
       
 10661          *
       
 10662          * @param {String} message to escape
       
 10663          * @return escaped message as {String}
       
 10664          */
       
 10665         var escapeSpecialCharacters = function(str) {
       
 10666 
       
 10667             if (!str || str.constructor !== String) {
       
 10668                 return "";
       
 10669             }
       
 10670 
       
 10671             return str.replace(/"/g, "'").replace(/</g, "&lt;").replace(/>/g, "&gt;");
       
 10672 
       
 10673         };
       
 10674 
       
 10675         if (messages.length > 0) {
       
 10676 
       
 10677             messages.forEach(function (message) {
       
 10678 
       
 10679                 // since junit has no warning class
       
 10680                 // all issues as errors
       
 10681                 var type = message.type === "warning" ? "error" : message.type;
       
 10682 
       
 10683                 // ignore rollups for now
       
 10684                 if (!message.rollup) {
       
 10685 
       
 10686                     // build the test case separately, once joined
       
 10687                     // we'll add it to a custom array filtered by type
       
 10688                     output.push("<testcase time=\"0\" name=\"" + generateSource(message.rule) + "\">");
       
 10689                     output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\"><![CDATA[" + message.line + ":" + message.col + ":" + escapeSpecialCharacters(message.evidence) + "]]></" + type + ">");
       
 10690                     output.push("</testcase>");
       
 10691 
       
 10692                     tests[type] += 1;
       
 10693 
       
 10694                 }
       
 10695 
       
 10696             });
       
 10697 
       
 10698             output.unshift("<testsuite time=\"0\" tests=\"" + messages.length + "\" skipped=\"0\" errors=\"" + tests.error + "\" failures=\"" + tests.failure + "\" package=\"net.csslint\" name=\"" + filename + "\">");
       
 10699             output.push("</testsuite>");
       
 10700 
       
 10701         }
       
 10702 
       
 10703         return output.join("");
       
 10704 
       
 10705     }
       
 10706 });
       
 10707 
       
 10708 CSSLint.addFormatter({
       
 10709     // format information
       
 10710     id: "lint-xml",
       
 10711     name: "Lint XML format",
       
 10712 
       
 10713     /**
       
 10714      * Return opening root XML tag.
       
 10715      * @return {String} to prepend before all results
       
 10716      */
       
 10717     startFormat: function() {
       
 10718         "use strict";
       
 10719         return "<?xml version=\"1.0\" encoding=\"utf-8\"?><lint>";
       
 10720     },
       
 10721 
       
 10722     /**
       
 10723      * Return closing root XML tag.
       
 10724      * @return {String} to append after all results
       
 10725      */
       
 10726     endFormat: function() {
       
 10727         "use strict";
       
 10728         return "</lint>";
       
 10729     },
       
 10730 
       
 10731     /**
       
 10732      * Given CSS Lint results for a file, return output for this format.
       
 10733      * @param results {Object} with error and warning messages
       
 10734      * @param filename {String} relative file path
       
 10735      * @param options {Object} (UNUSED for now) specifies special handling of output
       
 10736      * @return {String} output for results
       
 10737      */
       
 10738     formatResults: function(results, filename/*, options*/) {
       
 10739         "use strict";
       
 10740         var messages = results.messages,
       
 10741             output = [];
       
 10742 
       
 10743         /**
       
 10744          * Replace special characters before write to output.
       
 10745          *
       
 10746          * Rules:
       
 10747          *  - single quotes is the escape sequence for double-quotes
       
 10748          *  - &amp; is the escape sequence for &
       
 10749          *  - &lt; is the escape sequence for <
       
 10750          *  - &gt; is the escape sequence for >
       
 10751          *
       
 10752          * @param {String} message to escape
       
 10753          * @return escaped message as {String}
       
 10754          */
       
 10755         var escapeSpecialCharacters = function(str) {
       
 10756             if (!str || str.constructor !== String) {
       
 10757                 return "";
       
 10758             }
       
 10759             return str.replace(/"/g, "'").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
       
 10760         };
       
 10761 
       
 10762         if (messages.length > 0) {
       
 10763 
       
 10764             output.push("<file name=\""+filename+"\">");
       
 10765             CSSLint.Util.forEach(messages, function (message) {
       
 10766                 if (message.rollup) {
       
 10767                     output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
       
 10768                 } else {
       
 10769                     var rule = "";
       
 10770                     if (message.rule && message.rule.id) {
       
 10771                         rule = "rule=\"" + escapeSpecialCharacters(message.rule.id) + "\" ";
       
 10772                     }
       
 10773                     output.push("<issue " + rule + "line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
       
 10774                         " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
       
 10775                 }
       
 10776             });
       
 10777             output.push("</file>");
       
 10778         }
       
 10779 
       
 10780         return output.join("");
       
 10781     }
       
 10782 });
       
 10783 
       
 10784 CSSLint.addFormatter({
       
 10785     // format information
       
 10786     id: "text",
       
 10787     name: "Plain Text",
       
 10788 
       
 10789     /**
       
 10790      * Return content to be printed before all file results.
       
 10791      * @return {String} to prepend before all results
       
 10792      */
       
 10793     startFormat: function() {
       
 10794         "use strict";
       
 10795         return "";
       
 10796     },
       
 10797 
       
 10798     /**
       
 10799      * Return content to be printed after all file results.
       
 10800      * @return {String} to append after all results
       
 10801      */
       
 10802     endFormat: function() {
       
 10803         "use strict";
       
 10804         return "";
       
 10805     },
       
 10806 
       
 10807     /**
       
 10808      * Given CSS Lint results for a file, return output for this format.
       
 10809      * @param results {Object} with error and warning messages
       
 10810      * @param filename {String} relative file path
       
 10811      * @param options {Object} (Optional) specifies special handling of output
       
 10812      * @return {String} output for results
       
 10813      */
       
 10814     formatResults: function(results, filename, options) {
       
 10815         "use strict";
       
 10816         var messages = results.messages,
       
 10817             output = "";
       
 10818         options = options || {};
       
 10819 
       
 10820         if (messages.length === 0) {
       
 10821             return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + ".";
       
 10822         }
       
 10823 
       
 10824         output = "\n\ncsslint: There ";
       
 10825         if (messages.length === 1) {
       
 10826             output += "is 1 problem";
       
 10827         } else {
       
 10828             output += "are " + messages.length + " problems";
       
 10829         }
       
 10830         output += " in " + filename + ".";
       
 10831 
       
 10832         var pos = filename.lastIndexOf("/"),
       
 10833             shortFilename = filename;
       
 10834 
       
 10835         if (pos === -1) {
       
 10836             pos = filename.lastIndexOf("\\");
       
 10837         }
       
 10838         if (pos > -1) {
       
 10839             shortFilename = filename.substring(pos+1);
       
 10840         }
       
 10841 
       
 10842         CSSLint.Util.forEach(messages, function (message, i) {
       
 10843             output = output + "\n\n" + shortFilename;
       
 10844             if (message.rollup) {
       
 10845                 output += "\n" + (i+1) + ": " + message.type;
       
 10846                 output += "\n" + message.message;
       
 10847             } else {
       
 10848                 output += "\n" + (i+1) + ": " + message.type + " at line " + message.line + ", col " + message.col;
       
 10849                 output += "\n" + message.message;
       
 10850                 output += "\n" + message.evidence;
       
 10851             }
       
 10852         });
       
 10853 
       
 10854         return output;
       
 10855     }
       
 10856 });
       
 10857 
       
 10858 return CSSLint;
       
 10859 })();