[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/js/codemirror/ -> csslint.js (source)

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


Generated : Wed Dec 25 08:20:01 2024 Cross-referenced by PHPXref