balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

csslint.js (366769B)


      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 })();