PageSourceSearch

https://www.vip-stage-alternance.com/wp-includes/js/codemirror/csslint.js?ver=1.0.5

js vip-stage-alternance.com collected 2026-10-06 06:19:41 UTC 374,004 bytes, 10,859 lines download raw bytes

vendor: 374,004 bytes, lines 1-10859
1/*!
2CSSLint v1.0.4
3Copyright (c) 2016 Nicole Sullivan and Nicholas C. Zakas. All rights reserved.
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the 'Software'), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21THE SOFTWARE.
22
23*/
24
25var CSSLint = (function(){
26  var module = module || {},
27      exports = exports || {};
28
29/*!
30Parser-Lib
31Copyright (c) 2009-2016 Nicholas C. Zakas. All rights reserved.
32
33Permission is hereby granted, free of charge, to any person obtaining a copy
34of this software and associated documentation files (the "Software"), to deal
35in the Software without restriction, including without limitation the rights
36to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
37copies of the Software, and to permit persons to whom the Software is
38furnished to do so, subject to the following conditions:
39
40The above copyright notice and this permission notice shall be included in
41all copies or substantial portions of the Software.
42
43THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
44IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
45FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
46AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
47LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
48OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
49THE SOFTWARE.
50*/
51/* Version v1.1.0, Build time: 6-December-2016 10:31:29 */
52var parserlib = (function () {
53var require;
54require=(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
59var 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
245module.exports = Combinator;
246
247var SyntaxUnit = require("../util/SyntaxUnit");
248
249var 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 */
261function 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
285Combinator.prototype = new SyntaxUnit();
286Combinator.prototype.constructor = Combinator;
287
288
289},{"../util/SyntaxUnit":26,"./Parser":6}],3:[function(require,module,exports){
290"use strict";
291
292module.exports = Matcher;
293
294var StringReader = require("../util/StringReader");
295var 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 */
302function 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. */
322Matcher.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. */
331Matcher.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 */
415Matcher.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 */
425Matcher.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 */
437Matcher.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 */
464Matcher.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 */
494Matcher.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 */
585Matcher.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 */
596Matcher.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. */
603Matcher.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
649module.exports = MediaFeature;
650
651var SyntaxUnit = require("../util/SyntaxUnit");
652
653var 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 */
664function 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
683MediaFeature.prototype = new SyntaxUnit();
684MediaFeature.prototype.constructor = MediaFeature;
685
686
687},{"../util/SyntaxUnit":26,"./Parser":6}],5:[function(require,module,exports){
688"use strict";
689
690module.exports = MediaQuery;
691
692var SyntaxUnit = require("../util/SyntaxUnit");
693
694var 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 */
708function 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
735MediaQuery.prototype = new SyntaxUnit();
736MediaQuery.prototype.constructor = MediaQuery;
737
738
739},{"../util/SyntaxUnit":26,"./Parser":6}],6:[function(require,module,exports){
740"use strict";
741
742module.exports = Parser;
743
744var EventTarget = require("../util/EventTarget");
745var SyntaxError = require("../util/SyntaxError");
746var SyntaxUnit = require("../util/SyntaxUnit");
747
748var Combinator = require("./Combinator");
749var MediaFeature = require("./MediaFeature");
750var MediaQuery = require("./MediaQuery");
751var PropertyName = require("./PropertyName");
752var PropertyValue = require("./PropertyValue");
753var PropertyValuePart = require("./PropertyValuePart");
754var Selector = require("./Selector");
755var SelectorPart = require("./SelectorPart");
756var SelectorSubPart = require("./SelectorSubPart");
757var TokenStream = require("./TokenStream");
758var Tokens = require("./Tokens");
759var 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 */
773function 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
785Parser.DEFAULT_TYPE = 0;
786Parser.COMBINATOR_TYPE = 1;
787Parser.MEDIA_FEATURE_TYPE = 2;
788Parser.MEDIA_QUERY_TYPE = 3;
789Parser.PROPERTY_NAME_TYPE = 4;
790Parser.PROPERTY_VALUE_TYPE = 5;
791Parser.PROPERTY_VALUE_PART_TYPE = 6;
792Parser.SELECTOR_TYPE = 7;
793Parser.SELECTOR_PART_TYPE = 8;
794Parser.SELECTOR_SUB_PART_TYPE = 9;
795
796Parser.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/*
3287nth
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
3298var 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
3790module.exports = PropertyName;
3791
3792var SyntaxUnit = require("../util/SyntaxUnit");
3793
3794var 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 */
3807function 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
3820PropertyName.prototype = new SyntaxUnit();
3821PropertyName.prototype.constructor = PropertyName;
3822PropertyName.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
3829module.exports = PropertyValue;
3830
3831var SyntaxUnit = require("../util/SyntaxUnit");
3832
3833var 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 */
3847function 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
3860PropertyValue.prototype = new SyntaxUnit();
3861PropertyValue.prototype.constructor = PropertyValue;
3862
3863
3864},{"../util/SyntaxUnit":26,"./Parser":6}],10:[function(require,module,exports){
3865"use strict";
3866
3867module.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 */
3877function 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 */
3917PropertyValueIterator.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 */
3926PropertyValueIterator.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 */
3935PropertyValueIterator.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 */
3945PropertyValueIterator.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 */
3956PropertyValueIterator.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 */
3967PropertyValueIterator.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 */
3978PropertyValueIterator.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 */
3987PropertyValueIterator.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 */
3998PropertyValueIterator.prototype.drop = function() {
3999    this._marks.pop();
4000};
4001
4002},{}],11:[function(require,module,exports){
4003"use strict";
4004
4005module.exports = PropertyValuePart;
4006
4007var SyntaxUnit = require("../util/SyntaxUnit");
4008
4009var Colors = require("./Colors");
4010var Parser = require("./Parser");
4011var 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 */
4024function 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
4187PropertyValuePart.prototype = new SyntaxUnit();
4188PropertyValuePart.prototype.constructor = PropertyValuePart;
4189
4190/**
4191 * Helper method to parse a CSS string.
4192 */
4193PropertyValuePart.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 */
4218PropertyValuePart.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 */
4241PropertyValuePart.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
4254var Pseudos = module.exports = {
4255    __proto__:       null,
4256    ":first-letter": 1,
4257    ":first-line":   1,
4258    ":before":       1,
4259    ":after":        1
4260};
4261
4262Pseudos.ELEMENT = 1;
4263Pseudos.CLASS = 2;
4264
4265Pseudos.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
4272module.exports = Selector;
4273
4274var SyntaxUnit = require("../util/SyntaxUnit");
4275
4276var Parser = require("./Parser");
4277var 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 */
4290function 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
4310Selector.prototype = new SyntaxUnit();
4311Selector.prototype.constructor = Selector;
4312
4313
4314},{"../util/SyntaxUnit":26,"./Parser":6,"./Specificity":16}],14:[function(require,module,exports){
4315"use strict";
4316
4317module.exports = SelectorPart;
4318
4319var SyntaxUnit = require("../util/SyntaxUnit");
4320
4321var 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 */
4339function 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
4361SelectorPart.prototype = new SyntaxUnit();
4362SelectorPart.prototype.constructor = SelectorPart;
4363
4364
4365},{"../util/SyntaxUnit":26,"./Parser":6}],15:[function(require,module,exports){
4366"use strict";
4367
4368module.exports = SelectorSubPart;
4369
4370var SyntaxUnit = require("../util/SyntaxUnit");
4371
4372var 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 */
4386function 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
4406SelectorSubPart.prototype = new SyntaxUnit();
4407SelectorSubPart.prototype.constructor = SelectorSubPart;
4408
4409
4410},{"../util/SyntaxUnit":26,"./Parser":6}],16:[function(require,module,exports){
4411"use strict";
4412
4413module.exports = Specificity;
4414
4415var Pseudos = require("./Pseudos");
4416var 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 */
4428function Specificity(a, b, c, d) {
4429    this.a = a;
4430    this.b = b;
4431    this.c = c;
4432    this.d = d;
4433}
4434
4435Specificity.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 */
4486Specificity.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
4544module.exports = TokenStream;
4545
4546var TokenStreamBase = require("../util/TokenStreamBase");
4547
4548var PropertyValuePart = require("./PropertyValuePart");
4549var Tokens = require("./Tokens");
4550
4551var 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
4561function isHexDigit(c) {
4562    return c !== null && h.test(c);
4563}
4564
4565function isDigit(c) {
4566    return c !== null && /\d/.test(c);
4567}
4568
4569function isWhitespace(c) {
4570    return c !== null && whitespace.test(c);
4571}
4572
4573function isNewLine(c) {
4574    return c !== null && nl.test(c);
4575}
4576
4577function isNameStart(c) {
4578    return c !== null && /[a-z_\u00A0-\uFFFF\\]/i.test(c);
4579}
4580
4581function isNameChar(c) {
4582    return c !== null && (isNameStart(c) || /[0-9\-\\]/.test(c));
4583}
4584
4585function isIdentStart(c) {
4586    return c !== null && (isNameStart(c) || /\-\\/.test(c));
4587}
4588
4589function 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 */
4610function TokenStream(input) {
4611    TokenStreamBase.call(this, input, Tokens);
4612}
4613
4614TokenStream.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
5593var 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
5806var Matcher = require("./Matcher");
5807var Properties = require("./Properties");
5808var ValidationTypes = require("./ValidationTypes");
5809var ValidationError = require("./ValidationError");
5810var PropertyValueIterator = require("./PropertyValueIterator");
5811
5812var 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
5872module.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 */
5883function 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
5909ValidationError.prototype = new Error();
5910
5911},{}],21:[function(require,module,exports){
5912"use strict";
5913
5914var ValidationTypes = module.exports;
5915
5916var Matcher = require("./Matcher");
5917
5918function copy(to, from) {
5919    Object.keys(from).forEach(function(prop) {
5920        to[prop] = from[prop];
5921    });
5922}
5923copy(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
6365Object.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
6374Object.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.
6383ValidationTypes.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
6393module.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
6415module.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 */
6423function 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
6434EventTarget.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
6508module.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 */
6517function 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
6554StringReader.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
6781module.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 */
6792function 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
6820SyntaxError.prototype = Object.create(Error.prototype); // jshint ignore:line
6821SyntaxError.prototype.constructor = SyntaxError; // jshint ignore:line
6822
6823},{}],26:[function(require,module,exports){
6824"use strict";
6825
6826module.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 */
6837function 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 */
6878SyntaxUnit.fromToken = function(token) {
6879    return new SyntaxUnit(token.value, token.startLine, token.startCol);
6880};
6881
6882SyntaxUnit.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
6910module.exports = TokenStreamBase;
6911
6912var StringReader = require("./StringReader");
6913var 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 */
6923function 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 */
6977TokenStreamBase.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
7007TokenStreamBase.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
7312module.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
7323module.exports = {
7324    css  : require("./css"),
7325    util : require("./util")
7326};
7327
7328},{"./css":22,"./util":28}]},{},[]);
7329
7330return require('parserlib');
7331})();
7332var clone = (function() {
7333'use strict';
7334
7335var nativeMap;
7336try {
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
7344var nativeSet;
7345try {
7346  nativeSet = Set;
7347} catch(_) {
7348  nativeSet = function() {};
7349}
7350
7351var nativePromise;
7352try {
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*/
7379function 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 */
7543clone.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
7554function __objToStr(o) {
7555  return Object.prototype.toString.call(o);
7556}
7557clone.__objToStr = __objToStr;
7558
7559function __isDate(o) {
7560  return typeof o === 'object' && __objToStr(o) === '[object Date]';
7561}
7562clone.__isDate = __isDate;
7563
7564function __isArray(o) {
7565  return typeof o === 'object' && __objToStr(o) === '[object Array]';
7566}
7567clone.__isArray = __isArray;
7568
7569function __isRegExp(o) {
7570  return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
7571}
7572clone.__isRegExp = __isRegExp;
7573
7574function __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}
7581clone.__getRegExpFlags = __getRegExpFlags;
7582
7583return clone;
7584})();
7585
7586if (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
7600var 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 */
7891function 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
7945Reporter.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
8085CSSLint._Reporter = Reporter;
8086
8087/*
8088 * Utility functions that make life easier.
8089 */
8090CSSLint.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
8154CSSLint.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 */
8201CSSLint.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
8310CSSLint.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
8341CSSLint.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
8409CSSLint.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
8601CSSLint.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
8723CSSLint.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
8763CSSLint.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
8812CSSLint.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
8849CSSLint.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
8870CSSLint.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
8952CSSLint.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
8990CSSLint.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
9023CSSLint.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
9060CSSLint.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
9127CSSLint.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
9182CSSLint.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
9223CSSLint.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
9251CSSLint.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
9290CSSLint.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
9320CSSLint.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
9376CSSLint.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
9453CSSLint.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
9522CSSLint.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
9563CSSLint.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
9610CSSLint.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
9639CSSLint.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
9669CSSLint.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
9699CSSLint.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
9745CSSLint.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
9834CSSLint.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
9864CSSLint.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
9920CSSLint.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
9949CSSLint.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
10026CSSLint.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
10063CSSLint.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
10122CSSLint.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
10272CSSLint.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
10414CSSLint.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
10476CSSLint.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
10549CSSLint.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
10600CSSLint.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
10708CSSLint.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
10784CSSLint.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
10858return CSSLint;
10859})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.