mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-04 03:27:00 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			10898 lines
		
	
	
		
			360 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			10898 lines
		
	
	
		
			360 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/*!
 | 
						|
CSSLint v1.0.5
 | 
						|
Copyright (c) 2017 Nicole Sullivan and Nicholas C. Zakas. All rights reserved.
 | 
						|
 | 
						|
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
						|
of this software and associated documentation files (the 'Software'), to deal
 | 
						|
in the Software without restriction, including without limitation the rights
 | 
						|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
						|
copies of the Software, and to permit persons to whom the Software is
 | 
						|
furnished to do so, subject to the following conditions:
 | 
						|
 | 
						|
The above copyright notice and this permission notice shall be included in
 | 
						|
all copies or substantial portions of the Software.
 | 
						|
 | 
						|
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
						|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
						|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
						|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
						|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
						|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
						|
THE SOFTWARE.
 | 
						|
 | 
						|
*/
 | 
						|
 | 
						|
var CSSLint = (function(){
 | 
						|
  var module = module || {},
 | 
						|
      exports = exports || {};
 | 
						|
 | 
						|
/*!
 | 
						|
Parser-Lib
 | 
						|
Copyright (c) 2009-2016 Nicholas C. Zakas. All rights reserved.
 | 
						|
 | 
						|
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
						|
of this software and associated documentation files (the "Software"), to deal
 | 
						|
in the Software without restriction, including without limitation the rights
 | 
						|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
						|
copies of the Software, and to permit persons to whom the Software is
 | 
						|
furnished to do so, subject to the following conditions:
 | 
						|
 | 
						|
The above copyright notice and this permission notice shall be included in
 | 
						|
all copies or substantial portions of the Software.
 | 
						|
 | 
						|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
						|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
						|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
						|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
						|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
						|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
						|
THE SOFTWARE.
 | 
						|
*/
 | 
						|
/* Version v1.1.0, Build time: 6-December-2016 10:31:29 */
 | 
						|
var parserlib = (function () {
 | 
						|
var require;
 | 
						|
require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
/* exported Colors */
 | 
						|
 | 
						|
var Colors = module.exports = {
 | 
						|
    __proto__       :null,
 | 
						|
    aliceblue       :"#f0f8ff",
 | 
						|
    antiquewhite    :"#faebd7",
 | 
						|
    aqua            :"#00ffff",
 | 
						|
    aquamarine      :"#7fffd4",
 | 
						|
    azure           :"#f0ffff",
 | 
						|
    beige           :"#f5f5dc",
 | 
						|
    bisque          :"#ffe4c4",
 | 
						|
    black           :"#000000",
 | 
						|
    blanchedalmond  :"#ffebcd",
 | 
						|
    blue            :"#0000ff",
 | 
						|
    blueviolet      :"#8a2be2",
 | 
						|
    brown           :"#a52a2a",
 | 
						|
    burlywood       :"#deb887",
 | 
						|
    cadetblue       :"#5f9ea0",
 | 
						|
    chartreuse      :"#7fff00",
 | 
						|
    chocolate       :"#d2691e",
 | 
						|
    coral           :"#ff7f50",
 | 
						|
    cornflowerblue  :"#6495ed",
 | 
						|
    cornsilk        :"#fff8dc",
 | 
						|
    crimson         :"#dc143c",
 | 
						|
    cyan            :"#00ffff",
 | 
						|
    darkblue        :"#00008b",
 | 
						|
    darkcyan        :"#008b8b",
 | 
						|
    darkgoldenrod   :"#b8860b",
 | 
						|
    darkgray        :"#a9a9a9",
 | 
						|
    darkgrey        :"#a9a9a9",
 | 
						|
    darkgreen       :"#006400",
 | 
						|
    darkkhaki       :"#bdb76b",
 | 
						|
    darkmagenta     :"#8b008b",
 | 
						|
    darkolivegreen  :"#556b2f",
 | 
						|
    darkorange      :"#ff8c00",
 | 
						|
    darkorchid      :"#9932cc",
 | 
						|
    darkred         :"#8b0000",
 | 
						|
    darksalmon      :"#e9967a",
 | 
						|
    darkseagreen    :"#8fbc8f",
 | 
						|
    darkslateblue   :"#483d8b",
 | 
						|
    darkslategray   :"#2f4f4f",
 | 
						|
    darkslategrey   :"#2f4f4f",
 | 
						|
    darkturquoise   :"#00ced1",
 | 
						|
    darkviolet      :"#9400d3",
 | 
						|
    deeppink        :"#ff1493",
 | 
						|
    deepskyblue     :"#00bfff",
 | 
						|
    dimgray         :"#696969",
 | 
						|
    dimgrey         :"#696969",
 | 
						|
    dodgerblue      :"#1e90ff",
 | 
						|
    firebrick       :"#b22222",
 | 
						|
    floralwhite     :"#fffaf0",
 | 
						|
    forestgreen     :"#228b22",
 | 
						|
    fuchsia         :"#ff00ff",
 | 
						|
    gainsboro       :"#dcdcdc",
 | 
						|
    ghostwhite      :"#f8f8ff",
 | 
						|
    gold            :"#ffd700",
 | 
						|
    goldenrod       :"#daa520",
 | 
						|
    gray            :"#808080",
 | 
						|
    grey            :"#808080",
 | 
						|
    green           :"#008000",
 | 
						|
    greenyellow     :"#adff2f",
 | 
						|
    honeydew        :"#f0fff0",
 | 
						|
    hotpink         :"#ff69b4",
 | 
						|
    indianred       :"#cd5c5c",
 | 
						|
    indigo          :"#4b0082",
 | 
						|
    ivory           :"#fffff0",
 | 
						|
    khaki           :"#f0e68c",
 | 
						|
    lavender        :"#e6e6fa",
 | 
						|
    lavenderblush   :"#fff0f5",
 | 
						|
    lawngreen       :"#7cfc00",
 | 
						|
    lemonchiffon    :"#fffacd",
 | 
						|
    lightblue       :"#add8e6",
 | 
						|
    lightcoral      :"#f08080",
 | 
						|
    lightcyan       :"#e0ffff",
 | 
						|
    lightgoldenrodyellow  :"#fafad2",
 | 
						|
    lightgray       :"#d3d3d3",
 | 
						|
    lightgrey       :"#d3d3d3",
 | 
						|
    lightgreen      :"#90ee90",
 | 
						|
    lightpink       :"#ffb6c1",
 | 
						|
    lightsalmon     :"#ffa07a",
 | 
						|
    lightseagreen   :"#20b2aa",
 | 
						|
    lightskyblue    :"#87cefa",
 | 
						|
    lightslategray  :"#778899",
 | 
						|
    lightslategrey  :"#778899",
 | 
						|
    lightsteelblue  :"#b0c4de",
 | 
						|
    lightyellow     :"#ffffe0",
 | 
						|
    lime            :"#00ff00",
 | 
						|
    limegreen       :"#32cd32",
 | 
						|
    linen           :"#faf0e6",
 | 
						|
    magenta         :"#ff00ff",
 | 
						|
    maroon          :"#800000",
 | 
						|
    mediumaquamarine:"#66cdaa",
 | 
						|
    mediumblue      :"#0000cd",
 | 
						|
    mediumorchid    :"#ba55d3",
 | 
						|
    mediumpurple    :"#9370d8",
 | 
						|
    mediumseagreen  :"#3cb371",
 | 
						|
    mediumslateblue :"#7b68ee",
 | 
						|
    mediumspringgreen   :"#00fa9a",
 | 
						|
    mediumturquoise :"#48d1cc",
 | 
						|
    mediumvioletred :"#c71585",
 | 
						|
    midnightblue    :"#191970",
 | 
						|
    mintcream       :"#f5fffa",
 | 
						|
    mistyrose       :"#ffe4e1",
 | 
						|
    moccasin        :"#ffe4b5",
 | 
						|
    navajowhite     :"#ffdead",
 | 
						|
    navy            :"#000080",
 | 
						|
    oldlace         :"#fdf5e6",
 | 
						|
    olive           :"#808000",
 | 
						|
    olivedrab       :"#6b8e23",
 | 
						|
    orange          :"#ffa500",
 | 
						|
    orangered       :"#ff4500",
 | 
						|
    orchid          :"#da70d6",
 | 
						|
    palegoldenrod   :"#eee8aa",
 | 
						|
    palegreen       :"#98fb98",
 | 
						|
    paleturquoise   :"#afeeee",
 | 
						|
    palevioletred   :"#d87093",
 | 
						|
    papayawhip      :"#ffefd5",
 | 
						|
    peachpuff       :"#ffdab9",
 | 
						|
    peru            :"#cd853f",
 | 
						|
    pink            :"#ffc0cb",
 | 
						|
    plum            :"#dda0dd",
 | 
						|
    powderblue      :"#b0e0e6",
 | 
						|
    purple          :"#800080",
 | 
						|
    red             :"#ff0000",
 | 
						|
    rosybrown       :"#bc8f8f",
 | 
						|
    royalblue       :"#4169e1",
 | 
						|
    saddlebrown     :"#8b4513",
 | 
						|
    salmon          :"#fa8072",
 | 
						|
    sandybrown      :"#f4a460",
 | 
						|
    seagreen        :"#2e8b57",
 | 
						|
    seashell        :"#fff5ee",
 | 
						|
    sienna          :"#a0522d",
 | 
						|
    silver          :"#c0c0c0",
 | 
						|
    skyblue         :"#87ceeb",
 | 
						|
    slateblue       :"#6a5acd",
 | 
						|
    slategray       :"#708090",
 | 
						|
    slategrey       :"#708090",
 | 
						|
    snow            :"#fffafa",
 | 
						|
    springgreen     :"#00ff7f",
 | 
						|
    steelblue       :"#4682b4",
 | 
						|
    tan             :"#d2b48c",
 | 
						|
    teal            :"#008080",
 | 
						|
    thistle         :"#d8bfd8",
 | 
						|
    tomato          :"#ff6347",
 | 
						|
    turquoise       :"#40e0d0",
 | 
						|
    violet          :"#ee82ee",
 | 
						|
    wheat           :"#f5deb3",
 | 
						|
    white           :"#ffffff",
 | 
						|
    whitesmoke      :"#f5f5f5",
 | 
						|
    yellow          :"#ffff00",
 | 
						|
    yellowgreen     :"#9acd32",
 | 
						|
    //'currentColor' color keyword https://www.w3.org/TR/css3-color/#currentcolor
 | 
						|
    currentColor        :"The value of the 'color' property.",
 | 
						|
    //CSS2 system colors https://www.w3.org/TR/css3-color/#css2-system
 | 
						|
    activeBorder        :"Active window border.",
 | 
						|
    activecaption       :"Active window caption.",
 | 
						|
    appworkspace        :"Background color of multiple document interface.",
 | 
						|
    background          :"Desktop background.",
 | 
						|
    buttonface          :"The face background color for 3-D elements that appear 3-D due to one layer of surrounding border.",
 | 
						|
    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.",
 | 
						|
    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.",
 | 
						|
    buttontext          :"Text on push buttons.",
 | 
						|
    captiontext         :"Text in caption, size box, and scrollbar arrow box.",
 | 
						|
    graytext            :"Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.",
 | 
						|
    greytext            :"Greyed (disabled) text. This color is set to #000 if the current display driver does not support a solid grey color.",
 | 
						|
    highlight           :"Item(s) selected in a control.",
 | 
						|
    highlighttext       :"Text of item(s) selected in a control.",
 | 
						|
    inactiveborder      :"Inactive window border.",
 | 
						|
    inactivecaption     :"Inactive window caption.",
 | 
						|
    inactivecaptiontext :"Color of text in an inactive caption.",
 | 
						|
    infobackground      :"Background color for tooltip controls.",
 | 
						|
    infotext            :"Text color for tooltip controls.",
 | 
						|
    menu                :"Menu background.",
 | 
						|
    menutext            :"Text in menus.",
 | 
						|
    scrollbar           :"Scroll bar gray area.",
 | 
						|
    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.",
 | 
						|
    threedface          :"The face background color for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
 | 
						|
    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.",
 | 
						|
    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.",
 | 
						|
    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.",
 | 
						|
    window              :"Window background.",
 | 
						|
    windowframe         :"Window frame.",
 | 
						|
    windowtext          :"Text in windows."
 | 
						|
};
 | 
						|
 | 
						|
},{}],2:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = Combinator;
 | 
						|
 | 
						|
var SyntaxUnit = require("../util/SyntaxUnit");
 | 
						|
 | 
						|
var Parser = require("./Parser");
 | 
						|
 | 
						|
/**
 | 
						|
 * Represents a selector combinator (whitespace, +, >).
 | 
						|
 * @namespace parserlib.css
 | 
						|
 * @class Combinator
 | 
						|
 * @extends parserlib.util.SyntaxUnit
 | 
						|
 * @constructor
 | 
						|
 * @param {String} text The text representation of the unit.
 | 
						|
 * @param {int} line The line of text on which the unit resides.
 | 
						|
 * @param {int} col The column of text on which the unit resides.
 | 
						|
 */
 | 
						|
function Combinator(text, line, col) {
 | 
						|
 | 
						|
    SyntaxUnit.call(this, text, line, col, Parser.COMBINATOR_TYPE);
 | 
						|
 | 
						|
    /**
 | 
						|
     * The type of modifier.
 | 
						|
     * @type String
 | 
						|
     * @property type
 | 
						|
     */
 | 
						|
    this.type = "unknown";
 | 
						|
 | 
						|
    //pretty simple
 | 
						|
    if (/^\s+$/.test(text)) {
 | 
						|
        this.type = "descendant";
 | 
						|
    } else if (text === ">") {
 | 
						|
        this.type = "child";
 | 
						|
    } else if (text === "+") {
 | 
						|
        this.type = "adjacent-sibling";
 | 
						|
    } else if (text === "~") {
 | 
						|
        this.type = "sibling";
 | 
						|
    }
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
Combinator.prototype = new SyntaxUnit();
 | 
						|
Combinator.prototype.constructor = Combinator;
 | 
						|
 | 
						|
 | 
						|
},{"../util/SyntaxUnit":26,"./Parser":6}],3:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = Matcher;
 | 
						|
 | 
						|
var StringReader = require("../util/StringReader");
 | 
						|
var SyntaxError = require("../util/SyntaxError");
 | 
						|
 | 
						|
/**
 | 
						|
 * This class implements a combinator library for matcher functions.
 | 
						|
 * The combinators are described at:
 | 
						|
 * https://developer.mozilla.org/en-US/docs/Web/CSS/Value_definition_syntax#Component_value_combinators
 | 
						|
 */
 | 
						|
function Matcher(matchFunc, toString) {
 | 
						|
    this.match = function(expression) {
 | 
						|
        // Save/restore marks to ensure that failed matches always restore
 | 
						|
        // the original location in the expression.
 | 
						|
        var result;
 | 
						|
        expression.mark();
 | 
						|
        result = matchFunc(expression);
 | 
						|
        if (result) {
 | 
						|
            expression.drop();
 | 
						|
        } else {
 | 
						|
            expression.restore();
 | 
						|
        }
 | 
						|
        return result;
 | 
						|
    };
 | 
						|
    this.toString = typeof toString === "function" ? toString : function() {
 | 
						|
        return toString;
 | 
						|
    };
 | 
						|
}
 | 
						|
 | 
						|
/** Precedence table of combinators. */
 | 
						|
Matcher.prec = {
 | 
						|
    MOD:    5,
 | 
						|
    SEQ:    4,
 | 
						|
    ANDAND: 3,
 | 
						|
    OROR:   2,
 | 
						|
    ALT:    1
 | 
						|
};
 | 
						|
 | 
						|
/** Simple recursive-descent grammar to build matchers from strings. */
 | 
						|
Matcher.parse = function(str) {
 | 
						|
    var reader, eat, expr, oror, andand, seq, mod, term, result;
 | 
						|
    reader = new StringReader(str);
 | 
						|
    eat = function(matcher) {
 | 
						|
        var result = reader.readMatch(matcher);
 | 
						|
        if (result === null) {
 | 
						|
            throw new SyntaxError(
 | 
						|
                "Expected "+matcher, reader.getLine(), reader.getCol());
 | 
						|
        }
 | 
						|
        return result;
 | 
						|
    };
 | 
						|
    expr = function() {
 | 
						|
        // expr = oror (" | " oror)*
 | 
						|
        var m = [ oror() ];
 | 
						|
        while (reader.readMatch(" | ") !== null) {
 | 
						|
            m.push(oror());
 | 
						|
        }
 | 
						|
        return m.length === 1 ? m[0] : Matcher.alt.apply(Matcher, m);
 | 
						|
    };
 | 
						|
    oror = function() {
 | 
						|
        // oror = andand ( " || " andand)*
 | 
						|
        var m = [ andand() ];
 | 
						|
        while (reader.readMatch(" || ") !== null) {
 | 
						|
            m.push(andand());
 | 
						|
        }
 | 
						|
        return m.length === 1 ? m[0] : Matcher.oror.apply(Matcher, m);
 | 
						|
    };
 | 
						|
    andand = function() {
 | 
						|
        // andand = seq ( " && " seq)*
 | 
						|
        var m = [ seq() ];
 | 
						|
        while (reader.readMatch(" && ") !== null) {
 | 
						|
            m.push(seq());
 | 
						|
        }
 | 
						|
        return m.length === 1 ? m[0] : Matcher.andand.apply(Matcher, m);
 | 
						|
    };
 | 
						|
    seq = function() {
 | 
						|
        // seq = mod ( " " mod)*
 | 
						|
        var m = [ mod() ];
 | 
						|
        while (reader.readMatch(/^ (?![&|\]])/) !== null) {
 | 
						|
            m.push(mod());
 | 
						|
        }
 | 
						|
        return m.length === 1 ? m[0] : Matcher.seq.apply(Matcher, m);
 | 
						|
    };
 | 
						|
    mod = function() {
 | 
						|
        // mod = term ( "?" | "*" | "+" | "#" | "{<num>,<num>}" )?
 | 
						|
        var m = term();
 | 
						|
        if (reader.readMatch("?") !== null) {
 | 
						|
            return m.question();
 | 
						|
        } else if (reader.readMatch("*") !== null) {
 | 
						|
            return m.star();
 | 
						|
        } else if (reader.readMatch("+") !== null) {
 | 
						|
            return m.plus();
 | 
						|
        } else if (reader.readMatch("#") !== null) {
 | 
						|
            return m.hash();
 | 
						|
        } else if (reader.readMatch(/^\{\s*/) !== null) {
 | 
						|
            var min = eat(/^\d+/);
 | 
						|
            eat(/^\s*,\s*/);
 | 
						|
            var max = eat(/^\d+/);
 | 
						|
            eat(/^\s*\}/);
 | 
						|
            return m.braces(+min, +max);
 | 
						|
        }
 | 
						|
        return m;
 | 
						|
    };
 | 
						|
    term = function() {
 | 
						|
        // term = <nt> | literal | "[ " expression " ]"
 | 
						|
        if (reader.readMatch("[ ") !== null) {
 | 
						|
            var m = expr();
 | 
						|
            eat(" ]");
 | 
						|
            return m;
 | 
						|
        }
 | 
						|
        return Matcher.fromType(eat(/^[^ ?*+#{]+/));
 | 
						|
    };
 | 
						|
    result = expr();
 | 
						|
    if (!reader.eof()) {
 | 
						|
        throw new SyntaxError(
 | 
						|
            "Expected end of string", reader.getLine(), reader.getCol());
 | 
						|
    }
 | 
						|
    return result;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Convert a string to a matcher (parsing simple alternations),
 | 
						|
 * or do nothing if the argument is already a matcher.
 | 
						|
 */
 | 
						|
Matcher.cast = function(m) {
 | 
						|
    if (m instanceof Matcher) {
 | 
						|
        return m;
 | 
						|
    }
 | 
						|
    return Matcher.parse(m);
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Create a matcher for a single type.
 | 
						|
 */
 | 
						|
Matcher.fromType = function(type) {
 | 
						|
    // Late require of ValidationTypes to break a dependency cycle.
 | 
						|
    var ValidationTypes = require("./ValidationTypes");
 | 
						|
    return new Matcher(function(expression) {
 | 
						|
        return expression.hasNext() && ValidationTypes.isType(expression, type);
 | 
						|
    }, type);
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Create a matcher for one or more juxtaposed words, which all must
 | 
						|
 * occur, in the given order.
 | 
						|
 */
 | 
						|
Matcher.seq = function() {
 | 
						|
    var ms = Array.prototype.slice.call(arguments).map(Matcher.cast);
 | 
						|
    if (ms.length === 1) {
 | 
						|
        return ms[0];
 | 
						|
    }
 | 
						|
    return new Matcher(function(expression) {
 | 
						|
        var i, result = true;
 | 
						|
        for (i = 0; result && i < ms.length; i++) {
 | 
						|
            result = ms[i].match(expression);
 | 
						|
        }
 | 
						|
        return result;
 | 
						|
    }, function(prec) {
 | 
						|
        var p = Matcher.prec.SEQ;
 | 
						|
        var s = ms.map(function(m) {
 | 
						|
            return m.toString(p);
 | 
						|
        }).join(" ");
 | 
						|
        if (prec > p) {
 | 
						|
            s = "[ " + s + " ]";
 | 
						|
        }
 | 
						|
        return s;
 | 
						|
    });
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Create a matcher for one or more alternatives, where exactly one
 | 
						|
 * must occur.
 | 
						|
 */
 | 
						|
Matcher.alt = function() {
 | 
						|
    var ms = Array.prototype.slice.call(arguments).map(Matcher.cast);
 | 
						|
    if (ms.length === 1) {
 | 
						|
        return ms[0];
 | 
						|
    }
 | 
						|
    return new Matcher(function(expression) {
 | 
						|
        var i, result = false;
 | 
						|
        for (i = 0; !result && i < ms.length; i++) {
 | 
						|
            result = ms[i].match(expression);
 | 
						|
        }
 | 
						|
        return result;
 | 
						|
    }, function(prec) {
 | 
						|
        var p = Matcher.prec.ALT;
 | 
						|
        var s = ms.map(function(m) {
 | 
						|
            return m.toString(p);
 | 
						|
        }).join(" | ");
 | 
						|
        if (prec > p) {
 | 
						|
            s = "[ " + s + " ]";
 | 
						|
        }
 | 
						|
        return s;
 | 
						|
    });
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Create a matcher for two or more options.  This implements the
 | 
						|
 * double bar (||) and double ampersand (&&) operators, as well as
 | 
						|
 * variants of && where some of the alternatives are optional.
 | 
						|
 * This will backtrack through even successful matches to try to
 | 
						|
 * maximize the number of items matched.
 | 
						|
 */
 | 
						|
Matcher.many = function(required) {
 | 
						|
    var ms = Array.prototype.slice.call(arguments, 1).reduce(function(acc, v) {
 | 
						|
        if (v.expand) {
 | 
						|
            // Insert all of the options for the given complex rule as
 | 
						|
            // individual options.
 | 
						|
            var ValidationTypes = require("./ValidationTypes");
 | 
						|
            acc.push.apply(acc, ValidationTypes.complex[v.expand].options);
 | 
						|
        } else {
 | 
						|
            acc.push(Matcher.cast(v));
 | 
						|
        }
 | 
						|
        return acc;
 | 
						|
    }, []);
 | 
						|
 | 
						|
    if (required === true) {
 | 
						|
        required = ms.map(function() {
 | 
						|
            return true;
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
    var result = new Matcher(function(expression) {
 | 
						|
        var seen = [], max = 0, pass = 0;
 | 
						|
        var success = function(matchCount) {
 | 
						|
            if (pass === 0) {
 | 
						|
                max = Math.max(matchCount, max);
 | 
						|
                return matchCount === ms.length;
 | 
						|
            } else {
 | 
						|
                return matchCount === max;
 | 
						|
            }
 | 
						|
        };
 | 
						|
        var tryMatch = function(matchCount) {
 | 
						|
            for (var i = 0; i < ms.length; i++) {
 | 
						|
                if (seen[i]) {
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
                expression.mark();
 | 
						|
                if (ms[i].match(expression)) {
 | 
						|
                    seen[i] = true;
 | 
						|
                    // Increase matchCount iff this was a required element
 | 
						|
                    // (or if all the elements are optional)
 | 
						|
                    if (tryMatch(matchCount + ((required === false || required[i]) ? 1 : 0))) {
 | 
						|
                        expression.drop();
 | 
						|
                        return true;
 | 
						|
                    }
 | 
						|
                    // Backtrack: try *not* matching using this rule, and
 | 
						|
                    // let's see if it leads to a better overall match.
 | 
						|
                    expression.restore();
 | 
						|
                    seen[i] = false;
 | 
						|
                } else {
 | 
						|
                    expression.drop();
 | 
						|
                }
 | 
						|
            }
 | 
						|
            return success(matchCount);
 | 
						|
        };
 | 
						|
        if (!tryMatch(0)) {
 | 
						|
            // Couldn't get a complete match, retrace our steps to make the
 | 
						|
            // match with the maximum # of required elements.
 | 
						|
            pass++;
 | 
						|
            tryMatch(0);
 | 
						|
        }
 | 
						|
 | 
						|
        if (required === false) {
 | 
						|
            return max > 0;
 | 
						|
        }
 | 
						|
        // Use finer-grained specification of which matchers are required.
 | 
						|
        for (var i = 0; i < ms.length; i++) {
 | 
						|
            if (required[i] && !seen[i]) {
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return true;
 | 
						|
    }, function(prec) {
 | 
						|
        var p = required === false ? Matcher.prec.OROR : Matcher.prec.ANDAND;
 | 
						|
        var s = ms.map(function(m, i) {
 | 
						|
            if (required !== false && !required[i]) {
 | 
						|
                return m.toString(Matcher.prec.MOD) + "?";
 | 
						|
            }
 | 
						|
            return m.toString(p);
 | 
						|
        }).join(required === false ? " || " : " && ");
 | 
						|
        if (prec > p) {
 | 
						|
            s = "[ " + s + " ]";
 | 
						|
        }
 | 
						|
        return s;
 | 
						|
    });
 | 
						|
    result.options = ms;
 | 
						|
    return result;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Create a matcher for two or more options, where all options are
 | 
						|
 * mandatory but they may appear in any order.
 | 
						|
 */
 | 
						|
Matcher.andand = function() {
 | 
						|
    var args = Array.prototype.slice.call(arguments);
 | 
						|
    args.unshift(true);
 | 
						|
    return Matcher.many.apply(Matcher, args);
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Create a matcher for two or more options, where options are
 | 
						|
 * optional and may appear in any order, but at least one must be
 | 
						|
 * present.
 | 
						|
 */
 | 
						|
Matcher.oror = function() {
 | 
						|
    var args = Array.prototype.slice.call(arguments);
 | 
						|
    args.unshift(false);
 | 
						|
    return Matcher.many.apply(Matcher, args);
 | 
						|
};
 | 
						|
 | 
						|
/** Instance methods on Matchers. */
 | 
						|
Matcher.prototype = {
 | 
						|
    constructor: Matcher,
 | 
						|
    // These are expected to be overridden in every instance.
 | 
						|
    match: function() { throw new Error("unimplemented"); },
 | 
						|
    toString: function() { throw new Error("unimplemented"); },
 | 
						|
    // This returns a standalone function to do the matching.
 | 
						|
    func: function() { return this.match.bind(this); },
 | 
						|
    // Basic combinators
 | 
						|
    then: function(m) { return Matcher.seq(this, m); },
 | 
						|
    or: function(m) { return Matcher.alt(this, m); },
 | 
						|
    andand: function(m) { return Matcher.many(true, this, m); },
 | 
						|
    oror: function(m) { return Matcher.many(false, this, m); },
 | 
						|
    // Component value multipliers
 | 
						|
    star: function() { return this.braces(0, Infinity, "*"); },
 | 
						|
    plus: function() { return this.braces(1, Infinity, "+"); },
 | 
						|
    question: function() { return this.braces(0, 1, "?"); },
 | 
						|
    hash: function() {
 | 
						|
        return this.braces(1, Infinity, "#", Matcher.cast(","));
 | 
						|
    },
 | 
						|
    braces: function(min, max, marker, optSep) {
 | 
						|
        var m1 = this, m2 = optSep ? optSep.then(this) : this;
 | 
						|
        if (!marker) {
 | 
						|
            marker = "{" + min + "," + max + "}";
 | 
						|
        }
 | 
						|
        return new Matcher(function(expression) {
 | 
						|
            var result = true, i;
 | 
						|
            for (i = 0; i < max; i++) {
 | 
						|
                if (i > 0 && optSep) {
 | 
						|
                    result = m2.match(expression);
 | 
						|
                } else {
 | 
						|
                    result = m1.match(expression);
 | 
						|
                }
 | 
						|
                if (!result) {
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            return i >= min;
 | 
						|
        }, function() {
 | 
						|
            return m1.toString(Matcher.prec.MOD) + marker;
 | 
						|
        });
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
},{"../util/StringReader":24,"../util/SyntaxError":25,"./ValidationTypes":21}],4:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = MediaFeature;
 | 
						|
 | 
						|
var SyntaxUnit = require("../util/SyntaxUnit");
 | 
						|
 | 
						|
var Parser = require("./Parser");
 | 
						|
 | 
						|
/**
 | 
						|
 * Represents a media feature, such as max-width:500.
 | 
						|
 * @namespace parserlib.css
 | 
						|
 * @class MediaFeature
 | 
						|
 * @extends parserlib.util.SyntaxUnit
 | 
						|
 * @constructor
 | 
						|
 * @param {SyntaxUnit} name The name of the feature.
 | 
						|
 * @param {SyntaxUnit} value The value of the feature or null if none.
 | 
						|
 */
 | 
						|
function MediaFeature(name, value) {
 | 
						|
 | 
						|
    SyntaxUnit.call(this, "(" + name + (value !== null ? ":" + value : "") + ")", name.startLine, name.startCol, Parser.MEDIA_FEATURE_TYPE);
 | 
						|
 | 
						|
    /**
 | 
						|
     * The name of the media feature
 | 
						|
     * @type String
 | 
						|
     * @property name
 | 
						|
     */
 | 
						|
    this.name = name;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The value for the feature or null if there is none.
 | 
						|
     * @type SyntaxUnit
 | 
						|
     * @property value
 | 
						|
     */
 | 
						|
    this.value = value;
 | 
						|
}
 | 
						|
 | 
						|
MediaFeature.prototype = new SyntaxUnit();
 | 
						|
MediaFeature.prototype.constructor = MediaFeature;
 | 
						|
 | 
						|
 | 
						|
},{"../util/SyntaxUnit":26,"./Parser":6}],5:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = MediaQuery;
 | 
						|
 | 
						|
var SyntaxUnit = require("../util/SyntaxUnit");
 | 
						|
 | 
						|
var Parser = require("./Parser");
 | 
						|
 | 
						|
/**
 | 
						|
 * Represents an individual media query.
 | 
						|
 * @namespace parserlib.css
 | 
						|
 * @class MediaQuery
 | 
						|
 * @extends parserlib.util.SyntaxUnit
 | 
						|
 * @constructor
 | 
						|
 * @param {String} modifier The modifier "not" or "only" (or null).
 | 
						|
 * @param {String} mediaType The type of media (i.e., "print").
 | 
						|
 * @param {Array} parts Array of selectors parts making up this selector.
 | 
						|
 * @param {int} line The line of text on which the unit resides.
 | 
						|
 * @param {int} col The column of text on which the unit resides.
 | 
						|
 */
 | 
						|
function MediaQuery(modifier, mediaType, features, line, col) {
 | 
						|
 | 
						|
    SyntaxUnit.call(this, (modifier ? modifier + " ": "") + (mediaType ? mediaType : "") + (mediaType && features.length > 0 ? " and " : "") + features.join(" and "), line, col, Parser.MEDIA_QUERY_TYPE);
 | 
						|
 | 
						|
    /**
 | 
						|
     * The media modifier ("not" or "only")
 | 
						|
     * @type String
 | 
						|
     * @property modifier
 | 
						|
     */
 | 
						|
    this.modifier = modifier;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The mediaType (i.e., "print")
 | 
						|
     * @type String
 | 
						|
     * @property mediaType
 | 
						|
     */
 | 
						|
    this.mediaType = mediaType;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The parts that make up the selector.
 | 
						|
     * @type Array
 | 
						|
     * @property features
 | 
						|
     */
 | 
						|
    this.features = features;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
MediaQuery.prototype = new SyntaxUnit();
 | 
						|
MediaQuery.prototype.constructor = MediaQuery;
 | 
						|
 | 
						|
 | 
						|
},{"../util/SyntaxUnit":26,"./Parser":6}],6:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = Parser;
 | 
						|
 | 
						|
var EventTarget = require("../util/EventTarget");
 | 
						|
var SyntaxError = require("../util/SyntaxError");
 | 
						|
var SyntaxUnit = require("../util/SyntaxUnit");
 | 
						|
 | 
						|
var Combinator = require("./Combinator");
 | 
						|
var MediaFeature = require("./MediaFeature");
 | 
						|
var MediaQuery = require("./MediaQuery");
 | 
						|
var PropertyName = require("./PropertyName");
 | 
						|
var PropertyValue = require("./PropertyValue");
 | 
						|
var PropertyValuePart = require("./PropertyValuePart");
 | 
						|
var Selector = require("./Selector");
 | 
						|
var SelectorPart = require("./SelectorPart");
 | 
						|
var SelectorSubPart = require("./SelectorSubPart");
 | 
						|
var TokenStream = require("./TokenStream");
 | 
						|
var Tokens = require("./Tokens");
 | 
						|
var Validation = require("./Validation");
 | 
						|
 | 
						|
/**
 | 
						|
 * A CSS3 parser.
 | 
						|
 * @namespace parserlib.css
 | 
						|
 * @class Parser
 | 
						|
 * @constructor
 | 
						|
 * @param {Object} options (Optional) Various options for the parser:
 | 
						|
 *      starHack (true|false) to allow IE6 star hack as valid,
 | 
						|
 *      underscoreHack (true|false) to interpret leading underscores
 | 
						|
 *      as IE6-7 targeting for known properties, ieFilters (true|false)
 | 
						|
 *      to indicate that IE < 8 filters should be accepted and not throw
 | 
						|
 *      syntax errors.
 | 
						|
 */
 | 
						|
function Parser(options) {
 | 
						|
 | 
						|
    //inherit event functionality
 | 
						|
    EventTarget.call(this);
 | 
						|
 | 
						|
 | 
						|
    this.options = options || {};
 | 
						|
 | 
						|
    this._tokenStream = null;
 | 
						|
}
 | 
						|
 | 
						|
//Static constants
 | 
						|
Parser.DEFAULT_TYPE = 0;
 | 
						|
Parser.COMBINATOR_TYPE = 1;
 | 
						|
Parser.MEDIA_FEATURE_TYPE = 2;
 | 
						|
Parser.MEDIA_QUERY_TYPE = 3;
 | 
						|
Parser.PROPERTY_NAME_TYPE = 4;
 | 
						|
Parser.PROPERTY_VALUE_TYPE = 5;
 | 
						|
Parser.PROPERTY_VALUE_PART_TYPE = 6;
 | 
						|
Parser.SELECTOR_TYPE = 7;
 | 
						|
Parser.SELECTOR_PART_TYPE = 8;
 | 
						|
Parser.SELECTOR_SUB_PART_TYPE = 9;
 | 
						|
 | 
						|
Parser.prototype = function() {
 | 
						|
 | 
						|
    var proto = new EventTarget(),  //new prototype
 | 
						|
        prop,
 | 
						|
        additions =  {
 | 
						|
            __proto__: null,
 | 
						|
 | 
						|
            //restore constructor
 | 
						|
            constructor: Parser,
 | 
						|
 | 
						|
            //instance constants - yuck
 | 
						|
            DEFAULT_TYPE : 0,
 | 
						|
            COMBINATOR_TYPE : 1,
 | 
						|
            MEDIA_FEATURE_TYPE : 2,
 | 
						|
            MEDIA_QUERY_TYPE : 3,
 | 
						|
            PROPERTY_NAME_TYPE : 4,
 | 
						|
            PROPERTY_VALUE_TYPE : 5,
 | 
						|
            PROPERTY_VALUE_PART_TYPE : 6,
 | 
						|
            SELECTOR_TYPE : 7,
 | 
						|
            SELECTOR_PART_TYPE : 8,
 | 
						|
            SELECTOR_SUB_PART_TYPE : 9,
 | 
						|
 | 
						|
            //-----------------------------------------------------------------
 | 
						|
            // Grammar
 | 
						|
            //-----------------------------------------------------------------
 | 
						|
 | 
						|
            _stylesheet: function() {
 | 
						|
 | 
						|
                /*
 | 
						|
                 * stylesheet
 | 
						|
                 *  : [ CHARSET_SYM S* STRING S* ';' ]?
 | 
						|
                 *    [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
 | 
						|
                 *    [ namespace [S|CDO|CDC]* ]*
 | 
						|
                 *    [ [ ruleset | media | page | font_face | keyframes_rule | supports_rule ] [S|CDO|CDC]* ]*
 | 
						|
                 *  ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    count,
 | 
						|
                    token,
 | 
						|
                    tt;
 | 
						|
 | 
						|
                this.fire("startstylesheet");
 | 
						|
 | 
						|
                //try to read character set
 | 
						|
                this._charset();
 | 
						|
 | 
						|
                this._skipCruft();
 | 
						|
 | 
						|
                //try to read imports - may be more than one
 | 
						|
                while (tokenStream.peek() === Tokens.IMPORT_SYM) {
 | 
						|
                    this._import();
 | 
						|
                    this._skipCruft();
 | 
						|
                }
 | 
						|
 | 
						|
                //try to read namespaces - may be more than one
 | 
						|
                while (tokenStream.peek() === Tokens.NAMESPACE_SYM) {
 | 
						|
                    this._namespace();
 | 
						|
                    this._skipCruft();
 | 
						|
                }
 | 
						|
 | 
						|
                //get the next token
 | 
						|
                tt = tokenStream.peek();
 | 
						|
 | 
						|
                //try to read the rest
 | 
						|
                while (tt > Tokens.EOF) {
 | 
						|
 | 
						|
                    try {
 | 
						|
 | 
						|
                        switch (tt) {
 | 
						|
                            case Tokens.MEDIA_SYM:
 | 
						|
                                this._media();
 | 
						|
                                this._skipCruft();
 | 
						|
                                break;
 | 
						|
                            case Tokens.PAGE_SYM:
 | 
						|
                                this._page();
 | 
						|
                                this._skipCruft();
 | 
						|
                                break;
 | 
						|
                            case Tokens.FONT_FACE_SYM:
 | 
						|
                                this._font_face();
 | 
						|
                                this._skipCruft();
 | 
						|
                                break;
 | 
						|
                            case Tokens.KEYFRAMES_SYM:
 | 
						|
                                this._keyframes();
 | 
						|
                                this._skipCruft();
 | 
						|
                                break;
 | 
						|
                            case Tokens.VIEWPORT_SYM:
 | 
						|
                                this._viewport();
 | 
						|
                                this._skipCruft();
 | 
						|
                                break;
 | 
						|
                            case Tokens.DOCUMENT_SYM:
 | 
						|
                                this._document();
 | 
						|
                                this._skipCruft();
 | 
						|
                                break;
 | 
						|
                            case Tokens.SUPPORTS_SYM:
 | 
						|
                                this._supports();
 | 
						|
                                this._skipCruft();
 | 
						|
                                break;
 | 
						|
                            case Tokens.UNKNOWN_SYM:  //unknown @ rule
 | 
						|
                                tokenStream.get();
 | 
						|
                                if (!this.options.strict) {
 | 
						|
 | 
						|
                                    //fire error event
 | 
						|
                                    this.fire({
 | 
						|
                                        type:       "error",
 | 
						|
                                        error:      null,
 | 
						|
                                        message:    "Unknown @ rule: " + tokenStream.LT(0).value + ".",
 | 
						|
                                        line:       tokenStream.LT(0).startLine,
 | 
						|
                                        col:        tokenStream.LT(0).startCol
 | 
						|
                                    });
 | 
						|
 | 
						|
                                    //skip braces
 | 
						|
                                    count=0;
 | 
						|
                                    while (tokenStream.advance([Tokens.LBRACE, Tokens.RBRACE]) === Tokens.LBRACE) {
 | 
						|
                                        count++;    //keep track of nesting depth
 | 
						|
                                    }
 | 
						|
 | 
						|
                                    while (count) {
 | 
						|
                                        tokenStream.advance([Tokens.RBRACE]);
 | 
						|
                                        count--;
 | 
						|
                                    }
 | 
						|
 | 
						|
                                } else {
 | 
						|
                                    //not a syntax error, rethrow it
 | 
						|
                                    throw new SyntaxError("Unknown @ rule.", tokenStream.LT(0).startLine, tokenStream.LT(0).startCol);
 | 
						|
                                }
 | 
						|
                                break;
 | 
						|
                            case Tokens.S:
 | 
						|
                                this._readWhitespace();
 | 
						|
                                break;
 | 
						|
                            default:
 | 
						|
                                if (!this._ruleset()) {
 | 
						|
 | 
						|
                                    //error handling for known issues
 | 
						|
                                    switch (tt) {
 | 
						|
                                        case Tokens.CHARSET_SYM:
 | 
						|
                                            token = tokenStream.LT(1);
 | 
						|
                                            this._charset(false);
 | 
						|
                                            throw new SyntaxError("@charset not allowed here.", token.startLine, token.startCol);
 | 
						|
                                        case Tokens.IMPORT_SYM:
 | 
						|
                                            token = tokenStream.LT(1);
 | 
						|
                                            this._import(false);
 | 
						|
                                            throw new SyntaxError("@import not allowed here.", token.startLine, token.startCol);
 | 
						|
                                        case Tokens.NAMESPACE_SYM:
 | 
						|
                                            token = tokenStream.LT(1);
 | 
						|
                                            this._namespace(false);
 | 
						|
                                            throw new SyntaxError("@namespace not allowed here.", token.startLine, token.startCol);
 | 
						|
                                        default:
 | 
						|
                                            tokenStream.get();  //get the last token
 | 
						|
                                            this._unexpectedToken(tokenStream.token());
 | 
						|
                                    }
 | 
						|
 | 
						|
                                }
 | 
						|
                        }
 | 
						|
                    } catch (ex) {
 | 
						|
                        if (ex instanceof SyntaxError && !this.options.strict) {
 | 
						|
                            this.fire({
 | 
						|
                                type:       "error",
 | 
						|
                                error:      ex,
 | 
						|
                                message:    ex.message,
 | 
						|
                                line:       ex.line,
 | 
						|
                                col:        ex.col
 | 
						|
                            });
 | 
						|
                        } else {
 | 
						|
                            throw ex;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
 | 
						|
                    tt = tokenStream.peek();
 | 
						|
                }
 | 
						|
 | 
						|
                if (tt !== Tokens.EOF) {
 | 
						|
                    this._unexpectedToken(tokenStream.token());
 | 
						|
                }
 | 
						|
 | 
						|
                this.fire("endstylesheet");
 | 
						|
            },
 | 
						|
 | 
						|
            _charset: function(emit) {
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    charset,
 | 
						|
                    token,
 | 
						|
                    line,
 | 
						|
                    col;
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.CHARSET_SYM)) {
 | 
						|
                    line = tokenStream.token().startLine;
 | 
						|
                    col = tokenStream.token().startCol;
 | 
						|
 | 
						|
                    this._readWhitespace();
 | 
						|
                    tokenStream.mustMatch(Tokens.STRING);
 | 
						|
 | 
						|
                    token = tokenStream.token();
 | 
						|
                    charset = token.value;
 | 
						|
 | 
						|
                    this._readWhitespace();
 | 
						|
                    tokenStream.mustMatch(Tokens.SEMICOLON);
 | 
						|
 | 
						|
                    if (emit !== false) {
 | 
						|
                        this.fire({
 | 
						|
                            type:   "charset",
 | 
						|
                            charset:charset,
 | 
						|
                            line:   line,
 | 
						|
                            col:    col
 | 
						|
                        });
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            },
 | 
						|
 | 
						|
            _import: function(emit) {
 | 
						|
                /*
 | 
						|
                 * import
 | 
						|
                 *   : IMPORT_SYM S*
 | 
						|
                 *    [STRING|URI] S* media_query_list? ';' S*
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    uri,
 | 
						|
                    importToken,
 | 
						|
                    mediaList   = [];
 | 
						|
 | 
						|
                //read import symbol
 | 
						|
                tokenStream.mustMatch(Tokens.IMPORT_SYM);
 | 
						|
                importToken = tokenStream.token();
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
 | 
						|
 | 
						|
                //grab the URI value
 | 
						|
                uri = tokenStream.token().value.replace(/^(?:url\()?["']?([^"']+?)["']?\)?$/, "$1");
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                mediaList = this._media_query_list();
 | 
						|
 | 
						|
                //must end with a semicolon
 | 
						|
                tokenStream.mustMatch(Tokens.SEMICOLON);
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                if (emit !== false) {
 | 
						|
                    this.fire({
 | 
						|
                        type:   "import",
 | 
						|
                        uri:    uri,
 | 
						|
                        media:  mediaList,
 | 
						|
                        line:   importToken.startLine,
 | 
						|
                        col:    importToken.startCol
 | 
						|
                    });
 | 
						|
                }
 | 
						|
 | 
						|
            },
 | 
						|
 | 
						|
            _namespace: function(emit) {
 | 
						|
                /*
 | 
						|
                 * namespace
 | 
						|
                 *   : NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    line,
 | 
						|
                    col,
 | 
						|
                    prefix,
 | 
						|
                    uri;
 | 
						|
 | 
						|
                //read import symbol
 | 
						|
                tokenStream.mustMatch(Tokens.NAMESPACE_SYM);
 | 
						|
                line = tokenStream.token().startLine;
 | 
						|
                col = tokenStream.token().startCol;
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                //it's a namespace prefix - no _namespace_prefix() method because it's just an IDENT
 | 
						|
                if (tokenStream.match(Tokens.IDENT)) {
 | 
						|
                    prefix = tokenStream.token().value;
 | 
						|
                    this._readWhitespace();
 | 
						|
                }
 | 
						|
 | 
						|
                tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
 | 
						|
                /*if (!tokenStream.match(Tokens.STRING)){
 | 
						|
                    tokenStream.mustMatch(Tokens.URI);
 | 
						|
                }*/
 | 
						|
 | 
						|
                //grab the URI value
 | 
						|
                uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1");
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                //must end with a semicolon
 | 
						|
                tokenStream.mustMatch(Tokens.SEMICOLON);
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                if (emit !== false) {
 | 
						|
                    this.fire({
 | 
						|
                        type:   "namespace",
 | 
						|
                        prefix: prefix,
 | 
						|
                        uri:    uri,
 | 
						|
                        line:   line,
 | 
						|
                        col:    col
 | 
						|
                    });
 | 
						|
                }
 | 
						|
 | 
						|
            },
 | 
						|
 | 
						|
            _supports: function(emit) {
 | 
						|
                /*
 | 
						|
                 * supports_rule
 | 
						|
                 *  : SUPPORTS_SYM S* supports_condition S* group_rule_body
 | 
						|
                 *  ;
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    line,
 | 
						|
                    col;
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.SUPPORTS_SYM)) {
 | 
						|
                    line = tokenStream.token().startLine;
 | 
						|
                    col = tokenStream.token().startCol;
 | 
						|
 | 
						|
                    this._readWhitespace();
 | 
						|
                    this._supports_condition();
 | 
						|
                    this._readWhitespace();
 | 
						|
 | 
						|
                    tokenStream.mustMatch(Tokens.LBRACE);
 | 
						|
                    this._readWhitespace();
 | 
						|
 | 
						|
                    if (emit !== false) {
 | 
						|
                        this.fire({
 | 
						|
                            type:   "startsupports",
 | 
						|
                            line:   line,
 | 
						|
                            col:    col
 | 
						|
                        });
 | 
						|
                    }
 | 
						|
 | 
						|
                    while (true) {
 | 
						|
                        if (!this._ruleset()) {
 | 
						|
                            break;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
 | 
						|
                    tokenStream.mustMatch(Tokens.RBRACE);
 | 
						|
                    this._readWhitespace();
 | 
						|
 | 
						|
                    this.fire({
 | 
						|
                        type:   "endsupports",
 | 
						|
                        line:   line,
 | 
						|
                        col:    col
 | 
						|
                    });
 | 
						|
                }
 | 
						|
            },
 | 
						|
 | 
						|
            _supports_condition: function() {
 | 
						|
                /*
 | 
						|
                 * supports_condition
 | 
						|
                 *  : supports_negation | supports_conjunction | supports_disjunction |
 | 
						|
                 *    supports_condition_in_parens
 | 
						|
                 *  ;
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    ident;
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.IDENT)) {
 | 
						|
                    ident = tokenStream.token().value.toLowerCase();
 | 
						|
 | 
						|
                    if (ident === "not") {
 | 
						|
                        tokenStream.mustMatch(Tokens.S);
 | 
						|
                        this._supports_condition_in_parens();
 | 
						|
                    } else {
 | 
						|
                        tokenStream.unget();
 | 
						|
                    }
 | 
						|
                } else {
 | 
						|
                    this._supports_condition_in_parens();
 | 
						|
                    this._readWhitespace();
 | 
						|
 | 
						|
                    while (tokenStream.peek() === Tokens.IDENT) {
 | 
						|
                        ident = tokenStream.LT(1).value.toLowerCase();
 | 
						|
                        if (ident === "and" || ident === "or") {
 | 
						|
                            tokenStream.mustMatch(Tokens.IDENT);
 | 
						|
                            this._readWhitespace();
 | 
						|
                            this._supports_condition_in_parens();
 | 
						|
                            this._readWhitespace();
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            },
 | 
						|
 | 
						|
            _supports_condition_in_parens: function() {
 | 
						|
                /*
 | 
						|
                 * supports_condition_in_parens
 | 
						|
                 *  : ( '(' S* supports_condition S* ')' ) | supports_declaration_condition |
 | 
						|
                 *    general_enclosed
 | 
						|
                 *  ;
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    ident;
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.LPAREN)) {
 | 
						|
                    this._readWhitespace();
 | 
						|
                    if (tokenStream.match(Tokens.IDENT)) {
 | 
						|
                        // look ahead for not keyword, if not given, continue with declaration condition.
 | 
						|
                        ident = tokenStream.token().value.toLowerCase();
 | 
						|
                        if (ident === "not") {
 | 
						|
                            this._readWhitespace();
 | 
						|
                            this._supports_condition();
 | 
						|
                            this._readWhitespace();
 | 
						|
                            tokenStream.mustMatch(Tokens.RPAREN);
 | 
						|
                        } else {
 | 
						|
                            tokenStream.unget();
 | 
						|
                            this._supports_declaration_condition(false);
 | 
						|
                        }
 | 
						|
                    } else {
 | 
						|
                        this._supports_condition();
 | 
						|
                        this._readWhitespace();
 | 
						|
                        tokenStream.mustMatch(Tokens.RPAREN);
 | 
						|
                    }
 | 
						|
                } else {
 | 
						|
                    this._supports_declaration_condition();
 | 
						|
                }
 | 
						|
            },
 | 
						|
 | 
						|
            _supports_declaration_condition: function(requireStartParen) {
 | 
						|
                /*
 | 
						|
                 * supports_declaration_condition
 | 
						|
                 *  : '(' S* declaration ')'
 | 
						|
                 *  ;
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream;
 | 
						|
 | 
						|
                if (requireStartParen !== false) {
 | 
						|
                    tokenStream.mustMatch(Tokens.LPAREN);
 | 
						|
                }
 | 
						|
                this._readWhitespace();
 | 
						|
                this._declaration();
 | 
						|
                tokenStream.mustMatch(Tokens.RPAREN);
 | 
						|
            },
 | 
						|
 | 
						|
            _media: function() {
 | 
						|
                /*
 | 
						|
                 * media
 | 
						|
                 *   : MEDIA_SYM S* media_query_list S* '{' S* ruleset* '}' S*
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
                var tokenStream     = this._tokenStream,
 | 
						|
                    line,
 | 
						|
                    col,
 | 
						|
                    mediaList;//       = [];
 | 
						|
 | 
						|
                //look for @media
 | 
						|
                tokenStream.mustMatch(Tokens.MEDIA_SYM);
 | 
						|
                line = tokenStream.token().startLine;
 | 
						|
                col = tokenStream.token().startCol;
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                mediaList = this._media_query_list();
 | 
						|
 | 
						|
                tokenStream.mustMatch(Tokens.LBRACE);
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                this.fire({
 | 
						|
                    type:   "startmedia",
 | 
						|
                    media:  mediaList,
 | 
						|
                    line:   line,
 | 
						|
                    col:    col
 | 
						|
                });
 | 
						|
 | 
						|
                while (true) {
 | 
						|
                    if (tokenStream.peek() === Tokens.PAGE_SYM) {
 | 
						|
                        this._page();
 | 
						|
                    } else if (tokenStream.peek() === Tokens.FONT_FACE_SYM) {
 | 
						|
                        this._font_face();
 | 
						|
                    } else if (tokenStream.peek() === Tokens.VIEWPORT_SYM) {
 | 
						|
                        this._viewport();
 | 
						|
                    } else if (tokenStream.peek() === Tokens.DOCUMENT_SYM) {
 | 
						|
                        this._document();
 | 
						|
                    } else if (tokenStream.peek() === Tokens.SUPPORTS_SYM) {
 | 
						|
                        this._supports();
 | 
						|
                    } else if (tokenStream.peek() === Tokens.MEDIA_SYM) {
 | 
						|
                        this._media();
 | 
						|
                    } else if (!this._ruleset()) {
 | 
						|
                        break;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                tokenStream.mustMatch(Tokens.RBRACE);
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                this.fire({
 | 
						|
                    type:   "endmedia",
 | 
						|
                    media:  mediaList,
 | 
						|
                    line:   line,
 | 
						|
                    col:    col
 | 
						|
                });
 | 
						|
            },
 | 
						|
 | 
						|
 | 
						|
            //CSS3 Media Queries
 | 
						|
            _media_query_list: function() {
 | 
						|
                /*
 | 
						|
                 * media_query_list
 | 
						|
                 *   : S* [media_query [ ',' S* media_query ]* ]?
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    mediaList   = [];
 | 
						|
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                if (tokenStream.peek() === Tokens.IDENT || tokenStream.peek() === Tokens.LPAREN) {
 | 
						|
                    mediaList.push(this._media_query());
 | 
						|
                }
 | 
						|
 | 
						|
                while (tokenStream.match(Tokens.COMMA)) {
 | 
						|
                    this._readWhitespace();
 | 
						|
                    mediaList.push(this._media_query());
 | 
						|
                }
 | 
						|
 | 
						|
                return mediaList;
 | 
						|
            },
 | 
						|
 | 
						|
            /*
 | 
						|
             * Note: "expression" in the grammar maps to the _media_expression
 | 
						|
             * method.
 | 
						|
 | 
						|
             */
 | 
						|
            _media_query: function() {
 | 
						|
                /*
 | 
						|
                 * media_query
 | 
						|
                 *   : [ONLY | NOT]? S* media_type S* [ AND S* expression ]*
 | 
						|
                 *   | expression [ AND S* expression ]*
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    type        = null,
 | 
						|
                    ident       = null,
 | 
						|
                    token       = null,
 | 
						|
                    expressions = [];
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.IDENT)) {
 | 
						|
                    ident = tokenStream.token().value.toLowerCase();
 | 
						|
 | 
						|
                    //since there's no custom tokens for these, need to manually check
 | 
						|
                    if (ident !== "only" && ident !== "not") {
 | 
						|
                        tokenStream.unget();
 | 
						|
                        ident = null;
 | 
						|
                    } else {
 | 
						|
                        token = tokenStream.token();
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                if (tokenStream.peek() === Tokens.IDENT) {
 | 
						|
                    type = this._media_type();
 | 
						|
                    if (token === null) {
 | 
						|
                        token = tokenStream.token();
 | 
						|
                    }
 | 
						|
                } else if (tokenStream.peek() === Tokens.LPAREN) {
 | 
						|
                    if (token === null) {
 | 
						|
                        token = tokenStream.LT(1);
 | 
						|
                    }
 | 
						|
                    expressions.push(this._media_expression());
 | 
						|
                }
 | 
						|
 | 
						|
                if (type === null && expressions.length === 0) {
 | 
						|
                    return null;
 | 
						|
                } else {
 | 
						|
                    this._readWhitespace();
 | 
						|
                    while (tokenStream.match(Tokens.IDENT)) {
 | 
						|
                        if (tokenStream.token().value.toLowerCase() !== "and") {
 | 
						|
                            this._unexpectedToken(tokenStream.token());
 | 
						|
                        }
 | 
						|
 | 
						|
                        this._readWhitespace();
 | 
						|
                        expressions.push(this._media_expression());
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                return new MediaQuery(ident, type, expressions, token.startLine, token.startCol);
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Media Queries
 | 
						|
            _media_type: function() {
 | 
						|
                /*
 | 
						|
                 * media_type
 | 
						|
                 *   : IDENT
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
                return this._media_feature();
 | 
						|
            },
 | 
						|
 | 
						|
            /**
 | 
						|
             * Note: in CSS3 Media Queries, this is called "expression".
 | 
						|
             * Renamed here to avoid conflict with CSS3 Selectors
 | 
						|
             * definition of "expression". Also note that "expr" in the
 | 
						|
             * grammar now maps to "expression" from CSS3 selectors.
 | 
						|
             * @method _media_expression
 | 
						|
             * @private
 | 
						|
             */
 | 
						|
            _media_expression: function() {
 | 
						|
                /*
 | 
						|
                 * expression
 | 
						|
                 *  : '(' S* media_feature S* [ ':' S* expr ]? ')' S*
 | 
						|
                 *  ;
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    feature     = null,
 | 
						|
                    token,
 | 
						|
                    expression  = null;
 | 
						|
 | 
						|
                tokenStream.mustMatch(Tokens.LPAREN);
 | 
						|
 | 
						|
                feature = this._media_feature();
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.COLON)) {
 | 
						|
                    this._readWhitespace();
 | 
						|
                    token = tokenStream.LT(1);
 | 
						|
                    expression = this._expression();
 | 
						|
                }
 | 
						|
 | 
						|
                tokenStream.mustMatch(Tokens.RPAREN);
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                return new MediaFeature(feature, expression ? new SyntaxUnit(expression, token.startLine, token.startCol) : null);
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Media Queries
 | 
						|
            _media_feature: function() {
 | 
						|
                /*
 | 
						|
                 * media_feature
 | 
						|
                 *   : IDENT
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream;
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                tokenStream.mustMatch(Tokens.IDENT);
 | 
						|
 | 
						|
                return SyntaxUnit.fromToken(tokenStream.token());
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Paged Media
 | 
						|
            _page: function() {
 | 
						|
                /*
 | 
						|
                 * page:
 | 
						|
                 *    PAGE_SYM S* IDENT? pseudo_page? S*
 | 
						|
                 *    '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
 | 
						|
                 *    ;
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    line,
 | 
						|
                    col,
 | 
						|
                    identifier  = null,
 | 
						|
                    pseudoPage  = null;
 | 
						|
 | 
						|
                //look for @page
 | 
						|
                tokenStream.mustMatch(Tokens.PAGE_SYM);
 | 
						|
                line = tokenStream.token().startLine;
 | 
						|
                col = tokenStream.token().startCol;
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.IDENT)) {
 | 
						|
                    identifier = tokenStream.token().value;
 | 
						|
 | 
						|
                    //The value 'auto' may not be used as a page name and MUST be treated as a syntax error.
 | 
						|
                    if (identifier.toLowerCase() === "auto") {
 | 
						|
                        this._unexpectedToken(tokenStream.token());
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                //see if there's a colon upcoming
 | 
						|
                if (tokenStream.peek() === Tokens.COLON) {
 | 
						|
                    pseudoPage = this._pseudo_page();
 | 
						|
                }
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                this.fire({
 | 
						|
                    type:   "startpage",
 | 
						|
                    id:     identifier,
 | 
						|
                    pseudo: pseudoPage,
 | 
						|
                    line:   line,
 | 
						|
                    col:    col
 | 
						|
                });
 | 
						|
 | 
						|
                this._readDeclarations(true, true);
 | 
						|
 | 
						|
                this.fire({
 | 
						|
                    type:   "endpage",
 | 
						|
                    id:     identifier,
 | 
						|
                    pseudo: pseudoPage,
 | 
						|
                    line:   line,
 | 
						|
                    col:    col
 | 
						|
                });
 | 
						|
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Paged Media
 | 
						|
            _margin: function() {
 | 
						|
                /*
 | 
						|
                 * margin :
 | 
						|
                 *    margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S*
 | 
						|
                 *    ;
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    line,
 | 
						|
                    col,
 | 
						|
                    marginSym   = this._margin_sym();
 | 
						|
 | 
						|
                if (marginSym) {
 | 
						|
                    line = tokenStream.token().startLine;
 | 
						|
                    col = tokenStream.token().startCol;
 | 
						|
 | 
						|
                    this.fire({
 | 
						|
                        type: "startpagemargin",
 | 
						|
                        margin: marginSym,
 | 
						|
                        line:   line,
 | 
						|
                        col:    col
 | 
						|
                    });
 | 
						|
 | 
						|
                    this._readDeclarations(true);
 | 
						|
 | 
						|
                    this.fire({
 | 
						|
                        type: "endpagemargin",
 | 
						|
                        margin: marginSym,
 | 
						|
                        line:   line,
 | 
						|
                        col:    col
 | 
						|
                    });
 | 
						|
                    return true;
 | 
						|
                } else {
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Paged Media
 | 
						|
            _margin_sym: function() {
 | 
						|
 | 
						|
                /*
 | 
						|
                 * margin_sym :
 | 
						|
                 *    TOPLEFTCORNER_SYM |
 | 
						|
                 *    TOPLEFT_SYM |
 | 
						|
                 *    TOPCENTER_SYM |
 | 
						|
                 *    TOPRIGHT_SYM |
 | 
						|
                 *    TOPRIGHTCORNER_SYM |
 | 
						|
                 *    BOTTOMLEFTCORNER_SYM |
 | 
						|
                 *    BOTTOMLEFT_SYM |
 | 
						|
                 *    BOTTOMCENTER_SYM |
 | 
						|
                 *    BOTTOMRIGHT_SYM |
 | 
						|
                 *    BOTTOMRIGHTCORNER_SYM |
 | 
						|
                 *    LEFTTOP_SYM |
 | 
						|
                 *    LEFTMIDDLE_SYM |
 | 
						|
                 *    LEFTBOTTOM_SYM |
 | 
						|
                 *    RIGHTTOP_SYM |
 | 
						|
                 *    RIGHTMIDDLE_SYM |
 | 
						|
                 *    RIGHTBOTTOM_SYM
 | 
						|
                 *    ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream;
 | 
						|
 | 
						|
                if (tokenStream.match([Tokens.TOPLEFTCORNER_SYM, Tokens.TOPLEFT_SYM,
 | 
						|
                        Tokens.TOPCENTER_SYM, Tokens.TOPRIGHT_SYM, Tokens.TOPRIGHTCORNER_SYM,
 | 
						|
                        Tokens.BOTTOMLEFTCORNER_SYM, Tokens.BOTTOMLEFT_SYM,
 | 
						|
                        Tokens.BOTTOMCENTER_SYM, Tokens.BOTTOMRIGHT_SYM,
 | 
						|
                        Tokens.BOTTOMRIGHTCORNER_SYM, Tokens.LEFTTOP_SYM,
 | 
						|
                        Tokens.LEFTMIDDLE_SYM, Tokens.LEFTBOTTOM_SYM, Tokens.RIGHTTOP_SYM,
 | 
						|
                        Tokens.RIGHTMIDDLE_SYM, Tokens.RIGHTBOTTOM_SYM])) {
 | 
						|
                    return SyntaxUnit.fromToken(tokenStream.token());
 | 
						|
                } else {
 | 
						|
                    return null;
 | 
						|
                }
 | 
						|
 | 
						|
            },
 | 
						|
 | 
						|
            _pseudo_page: function() {
 | 
						|
                /*
 | 
						|
                 * pseudo_page
 | 
						|
                 *   : ':' IDENT
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream;
 | 
						|
 | 
						|
                tokenStream.mustMatch(Tokens.COLON);
 | 
						|
                tokenStream.mustMatch(Tokens.IDENT);
 | 
						|
 | 
						|
                //TODO: CSS3 Paged Media says only "left", "center", and "right" are allowed
 | 
						|
 | 
						|
                return tokenStream.token().value;
 | 
						|
            },
 | 
						|
 | 
						|
            _font_face: function() {
 | 
						|
                /*
 | 
						|
                 * font_face
 | 
						|
                 *   : FONT_FACE_SYM S*
 | 
						|
                 *     '{' S* declaration [ ';' S* declaration ]* '}' S*
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    line,
 | 
						|
                    col;
 | 
						|
 | 
						|
                //look for @page
 | 
						|
                tokenStream.mustMatch(Tokens.FONT_FACE_SYM);
 | 
						|
                line = tokenStream.token().startLine;
 | 
						|
                col = tokenStream.token().startCol;
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                this.fire({
 | 
						|
                    type:   "startfontface",
 | 
						|
                    line:   line,
 | 
						|
                    col:    col
 | 
						|
                });
 | 
						|
 | 
						|
                this._readDeclarations(true);
 | 
						|
 | 
						|
                this.fire({
 | 
						|
                    type:   "endfontface",
 | 
						|
                    line:   line,
 | 
						|
                    col:    col
 | 
						|
                });
 | 
						|
            },
 | 
						|
 | 
						|
            _viewport: function() {
 | 
						|
                /*
 | 
						|
                 * viewport
 | 
						|
                 *   : VIEWPORT_SYM S*
 | 
						|
                 *     '{' S* declaration? [ ';' S* declaration? ]* '}' S*
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    line,
 | 
						|
                    col;
 | 
						|
 | 
						|
                tokenStream.mustMatch(Tokens.VIEWPORT_SYM);
 | 
						|
                line = tokenStream.token().startLine;
 | 
						|
                col = tokenStream.token().startCol;
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                this.fire({
 | 
						|
                    type:   "startviewport",
 | 
						|
                    line:   line,
 | 
						|
                    col:    col
 | 
						|
                });
 | 
						|
 | 
						|
                this._readDeclarations(true);
 | 
						|
 | 
						|
                this.fire({
 | 
						|
                    type:   "endviewport",
 | 
						|
                    line:   line,
 | 
						|
                    col:    col
 | 
						|
                });
 | 
						|
 | 
						|
            },
 | 
						|
 | 
						|
            _document: function() {
 | 
						|
                /*
 | 
						|
                 * document
 | 
						|
                 *   : DOCUMENT_SYM S*
 | 
						|
                 *     _document_function [ ',' S* _document_function ]* S*
 | 
						|
                 *     '{' S* ruleset* '}'
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    token,
 | 
						|
                    functions = [],
 | 
						|
                    prefix = "";
 | 
						|
 | 
						|
                tokenStream.mustMatch(Tokens.DOCUMENT_SYM);
 | 
						|
                token = tokenStream.token();
 | 
						|
                if (/^@\-([^\-]+)\-/.test(token.value)) {
 | 
						|
                    prefix = RegExp.$1;
 | 
						|
                }
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
                functions.push(this._document_function());
 | 
						|
 | 
						|
                while (tokenStream.match(Tokens.COMMA)) {
 | 
						|
                    this._readWhitespace();
 | 
						|
                    functions.push(this._document_function());
 | 
						|
                }
 | 
						|
 | 
						|
                tokenStream.mustMatch(Tokens.LBRACE);
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                this.fire({
 | 
						|
                    type:      "startdocument",
 | 
						|
                    functions: functions,
 | 
						|
                    prefix:    prefix,
 | 
						|
                    line:      token.startLine,
 | 
						|
                    col:       token.startCol
 | 
						|
                });
 | 
						|
 | 
						|
                var ok = true;
 | 
						|
                while (ok) {
 | 
						|
                    switch (tokenStream.peek()) {
 | 
						|
                        case Tokens.PAGE_SYM:
 | 
						|
                            this._page();
 | 
						|
                            break;
 | 
						|
                        case Tokens.FONT_FACE_SYM:
 | 
						|
                            this._font_face();
 | 
						|
                            break;
 | 
						|
                        case Tokens.VIEWPORT_SYM:
 | 
						|
                            this._viewport();
 | 
						|
                            break;
 | 
						|
                        case Tokens.MEDIA_SYM:
 | 
						|
                            this._media();
 | 
						|
                            break;
 | 
						|
                        case Tokens.KEYFRAMES_SYM:
 | 
						|
                            this._keyframes();
 | 
						|
                            break;
 | 
						|
                        case Tokens.DOCUMENT_SYM:
 | 
						|
                            this._document();
 | 
						|
                            break;
 | 
						|
                        default:
 | 
						|
                            ok = Boolean(this._ruleset());
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                tokenStream.mustMatch(Tokens.RBRACE);
 | 
						|
                token = tokenStream.token();
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                this.fire({
 | 
						|
                    type:      "enddocument",
 | 
						|
                    functions: functions,
 | 
						|
                    prefix:    prefix,
 | 
						|
                    line:      token.startLine,
 | 
						|
                    col:       token.startCol
 | 
						|
                });
 | 
						|
            },
 | 
						|
 | 
						|
            _document_function: function() {
 | 
						|
                /*
 | 
						|
                 * document_function
 | 
						|
                 *   : function | URI S*
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    value;
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.URI)) {
 | 
						|
                    value = tokenStream.token().value;
 | 
						|
                    this._readWhitespace();
 | 
						|
                } else {
 | 
						|
                    value = this._function();
 | 
						|
                }
 | 
						|
 | 
						|
                return value;
 | 
						|
            },
 | 
						|
 | 
						|
            _operator: function(inFunction) {
 | 
						|
 | 
						|
                /*
 | 
						|
                 * operator (outside function)
 | 
						|
                 *  : '/' S* | ',' S* | /( empty )/
 | 
						|
                 * operator (inside function)
 | 
						|
                 *  : '/' S* | '+' S* | '*' S* | '-' S* /( empty )/
 | 
						|
                 *  ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    token       = null;
 | 
						|
 | 
						|
                if (tokenStream.match([Tokens.SLASH, Tokens.COMMA]) ||
 | 
						|
                    (inFunction && tokenStream.match([Tokens.PLUS, Tokens.STAR, Tokens.MINUS]))) {
 | 
						|
                    token =  tokenStream.token();
 | 
						|
                    this._readWhitespace();
 | 
						|
                }
 | 
						|
                return token ? PropertyValuePart.fromToken(token) : null;
 | 
						|
 | 
						|
            },
 | 
						|
 | 
						|
            _combinator: function() {
 | 
						|
 | 
						|
                /*
 | 
						|
                 * combinator
 | 
						|
                 *  : PLUS S* | GREATER S* | TILDE S* | S+
 | 
						|
                 *  ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    value       = null,
 | 
						|
                    token;
 | 
						|
 | 
						|
                if (tokenStream.match([Tokens.PLUS, Tokens.GREATER, Tokens.TILDE])) {
 | 
						|
                    token = tokenStream.token();
 | 
						|
                    value = new Combinator(token.value, token.startLine, token.startCol);
 | 
						|
                    this._readWhitespace();
 | 
						|
                }
 | 
						|
 | 
						|
                return value;
 | 
						|
            },
 | 
						|
 | 
						|
            _unary_operator: function() {
 | 
						|
 | 
						|
                /*
 | 
						|
                 * unary_operator
 | 
						|
                 *  : '-' | '+'
 | 
						|
                 *  ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream;
 | 
						|
 | 
						|
                if (tokenStream.match([Tokens.MINUS, Tokens.PLUS])) {
 | 
						|
                    return tokenStream.token().value;
 | 
						|
                } else {
 | 
						|
                    return null;
 | 
						|
                }
 | 
						|
            },
 | 
						|
 | 
						|
            _property: function() {
 | 
						|
 | 
						|
                /*
 | 
						|
                 * property
 | 
						|
                 *   : IDENT S*
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    value       = null,
 | 
						|
                    hack        = null,
 | 
						|
                    tokenValue,
 | 
						|
                    token,
 | 
						|
                    line,
 | 
						|
                    col;
 | 
						|
 | 
						|
                //check for star hack - throws error if not allowed
 | 
						|
                if (tokenStream.peek() === Tokens.STAR && this.options.starHack) {
 | 
						|
                    tokenStream.get();
 | 
						|
                    token = tokenStream.token();
 | 
						|
                    hack = token.value;
 | 
						|
                    line = token.startLine;
 | 
						|
                    col = token.startCol;
 | 
						|
                }
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.IDENT)) {
 | 
						|
                    token = tokenStream.token();
 | 
						|
                    tokenValue = token.value;
 | 
						|
 | 
						|
                    //check for underscore hack - no error if not allowed because it's valid CSS syntax
 | 
						|
                    if (tokenValue.charAt(0) === "_" && this.options.underscoreHack) {
 | 
						|
                        hack = "_";
 | 
						|
                        tokenValue = tokenValue.substring(1);
 | 
						|
                    }
 | 
						|
 | 
						|
                    value = new PropertyName(tokenValue, hack, (line||token.startLine), (col||token.startCol));
 | 
						|
                    this._readWhitespace();
 | 
						|
                }
 | 
						|
 | 
						|
                return value;
 | 
						|
            },
 | 
						|
 | 
						|
            //Augmented with CSS3 Selectors
 | 
						|
            _ruleset: function() {
 | 
						|
                /*
 | 
						|
                 * ruleset
 | 
						|
                 *   : selectors_group
 | 
						|
                 *     '{' S* declaration? [ ';' S* declaration? ]* '}' S*
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    tt,
 | 
						|
                    selectors;
 | 
						|
 | 
						|
 | 
						|
                /*
 | 
						|
                 * Error Recovery: If even a single selector fails to parse,
 | 
						|
                 * then the entire ruleset should be thrown away.
 | 
						|
                 */
 | 
						|
                try {
 | 
						|
                    selectors = this._selectors_group();
 | 
						|
                } catch (ex) {
 | 
						|
                    if (ex instanceof SyntaxError && !this.options.strict) {
 | 
						|
 | 
						|
                        //fire error event
 | 
						|
                        this.fire({
 | 
						|
                            type:       "error",
 | 
						|
                            error:      ex,
 | 
						|
                            message:    ex.message,
 | 
						|
                            line:       ex.line,
 | 
						|
                            col:        ex.col
 | 
						|
                        });
 | 
						|
 | 
						|
                        //skip over everything until closing brace
 | 
						|
                        tt = tokenStream.advance([Tokens.RBRACE]);
 | 
						|
                        if (tt === Tokens.RBRACE) {
 | 
						|
                            //if there's a right brace, the rule is finished so don't do anything
 | 
						|
                        } else {
 | 
						|
                            //otherwise, rethrow the error because it wasn't handled properly
 | 
						|
                            throw ex;
 | 
						|
                        }
 | 
						|
 | 
						|
                    } else {
 | 
						|
                        //not a syntax error, rethrow it
 | 
						|
                        throw ex;
 | 
						|
                    }
 | 
						|
 | 
						|
                    //trigger parser to continue
 | 
						|
                    return true;
 | 
						|
                }
 | 
						|
 | 
						|
                //if it got here, all selectors parsed
 | 
						|
                if (selectors) {
 | 
						|
 | 
						|
                    this.fire({
 | 
						|
                        type:       "startrule",
 | 
						|
                        selectors:  selectors,
 | 
						|
                        line:       selectors[0].line,
 | 
						|
                        col:        selectors[0].col
 | 
						|
                    });
 | 
						|
 | 
						|
                    this._readDeclarations(true);
 | 
						|
 | 
						|
                    this.fire({
 | 
						|
                        type:       "endrule",
 | 
						|
                        selectors:  selectors,
 | 
						|
                        line:       selectors[0].line,
 | 
						|
                        col:        selectors[0].col
 | 
						|
                    });
 | 
						|
 | 
						|
                }
 | 
						|
 | 
						|
                return selectors;
 | 
						|
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Selectors
 | 
						|
            _selectors_group: function() {
 | 
						|
 | 
						|
                /*
 | 
						|
                 * selectors_group
 | 
						|
                 *   : selector [ COMMA S* selector ]*
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    selectors   = [],
 | 
						|
                    selector;
 | 
						|
 | 
						|
                selector = this._selector();
 | 
						|
                if (selector !== null) {
 | 
						|
 | 
						|
                    selectors.push(selector);
 | 
						|
                    while (tokenStream.match(Tokens.COMMA)) {
 | 
						|
                        this._readWhitespace();
 | 
						|
                        selector = this._selector();
 | 
						|
                        if (selector !== null) {
 | 
						|
                            selectors.push(selector);
 | 
						|
                        } else {
 | 
						|
                            this._unexpectedToken(tokenStream.LT(1));
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                return selectors.length ? selectors : null;
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Selectors
 | 
						|
            _selector: function() {
 | 
						|
                /*
 | 
						|
                 * selector
 | 
						|
                 *   : simple_selector_sequence [ combinator simple_selector_sequence ]*
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    selector    = [],
 | 
						|
                    nextSelector = null,
 | 
						|
                    combinator  = null,
 | 
						|
                    ws          = null;
 | 
						|
 | 
						|
                //if there's no simple selector, then there's no selector
 | 
						|
                nextSelector = this._simple_selector_sequence();
 | 
						|
                if (nextSelector === null) {
 | 
						|
                    return null;
 | 
						|
                }
 | 
						|
 | 
						|
                selector.push(nextSelector);
 | 
						|
 | 
						|
                do {
 | 
						|
 | 
						|
                    //look for a combinator
 | 
						|
                    combinator = this._combinator();
 | 
						|
 | 
						|
                    if (combinator !== null) {
 | 
						|
                        selector.push(combinator);
 | 
						|
                        nextSelector = this._simple_selector_sequence();
 | 
						|
 | 
						|
                        //there must be a next selector
 | 
						|
                        if (nextSelector === null) {
 | 
						|
                            this._unexpectedToken(tokenStream.LT(1));
 | 
						|
                        } else {
 | 
						|
 | 
						|
                            //nextSelector is an instance of SelectorPart
 | 
						|
                            selector.push(nextSelector);
 | 
						|
                        }
 | 
						|
                    } else {
 | 
						|
 | 
						|
                        //if there's not whitespace, we're done
 | 
						|
                        if (this._readWhitespace()) {
 | 
						|
 | 
						|
                            //add whitespace separator
 | 
						|
                            ws = new Combinator(tokenStream.token().value, tokenStream.token().startLine, tokenStream.token().startCol);
 | 
						|
 | 
						|
                            //combinator is not required
 | 
						|
                            combinator = this._combinator();
 | 
						|
 | 
						|
                            //selector is required if there's a combinator
 | 
						|
                            nextSelector = this._simple_selector_sequence();
 | 
						|
                            if (nextSelector === null) {
 | 
						|
                                if (combinator !== null) {
 | 
						|
                                    this._unexpectedToken(tokenStream.LT(1));
 | 
						|
                                }
 | 
						|
                            } else {
 | 
						|
 | 
						|
                                if (combinator !== null) {
 | 
						|
                                    selector.push(combinator);
 | 
						|
                                } else {
 | 
						|
                                    selector.push(ws);
 | 
						|
                                }
 | 
						|
 | 
						|
                                selector.push(nextSelector);
 | 
						|
                            }
 | 
						|
                        } else {
 | 
						|
                            break;
 | 
						|
                        }
 | 
						|
 | 
						|
                    }
 | 
						|
                } while (true);
 | 
						|
 | 
						|
                return new Selector(selector, selector[0].line, selector[0].col);
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Selectors
 | 
						|
            _simple_selector_sequence: function() {
 | 
						|
                /*
 | 
						|
                 * simple_selector_sequence
 | 
						|
                 *   : [ type_selector | universal ]
 | 
						|
                 *     [ HASH | class | attrib | pseudo | negation ]*
 | 
						|
                 *   | [ HASH | class | attrib | pseudo | negation ]+
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
 | 
						|
                    //parts of a simple selector
 | 
						|
                    elementName = null,
 | 
						|
                    modifiers   = [],
 | 
						|
 | 
						|
                    //complete selector text
 | 
						|
                    selectorText= "",
 | 
						|
 | 
						|
                    //the different parts after the element name to search for
 | 
						|
                    components  = [
 | 
						|
                        //HASH
 | 
						|
                        function() {
 | 
						|
                            return tokenStream.match(Tokens.HASH) ?
 | 
						|
                                    new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
 | 
						|
                                    null;
 | 
						|
                        },
 | 
						|
                        this._class,
 | 
						|
                        this._attrib,
 | 
						|
                        this._pseudo,
 | 
						|
                        this._negation
 | 
						|
                    ],
 | 
						|
                    i           = 0,
 | 
						|
                    len         = components.length,
 | 
						|
                    component   = null,
 | 
						|
                    line,
 | 
						|
                    col;
 | 
						|
 | 
						|
 | 
						|
                //get starting line and column for the selector
 | 
						|
                line = tokenStream.LT(1).startLine;
 | 
						|
                col = tokenStream.LT(1).startCol;
 | 
						|
 | 
						|
                elementName = this._type_selector();
 | 
						|
                if (!elementName) {
 | 
						|
                    elementName = this._universal();
 | 
						|
                }
 | 
						|
 | 
						|
                if (elementName !== null) {
 | 
						|
                    selectorText += elementName;
 | 
						|
                }
 | 
						|
 | 
						|
                while (true) {
 | 
						|
 | 
						|
                    //whitespace means we're done
 | 
						|
                    if (tokenStream.peek() === Tokens.S) {
 | 
						|
                        break;
 | 
						|
                    }
 | 
						|
 | 
						|
                    //check for each component
 | 
						|
                    while (i < len && component === null) {
 | 
						|
                        component = components[i++].call(this);
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (component === null) {
 | 
						|
 | 
						|
                        //we don't have a selector
 | 
						|
                        if (selectorText === "") {
 | 
						|
                            return null;
 | 
						|
                        } else {
 | 
						|
                            break;
 | 
						|
                        }
 | 
						|
                    } else {
 | 
						|
                        i = 0;
 | 
						|
                        modifiers.push(component);
 | 
						|
                        selectorText += component.toString();
 | 
						|
                        component = null;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
 | 
						|
                return selectorText !== "" ?
 | 
						|
                        new SelectorPart(elementName, modifiers, selectorText, line, col) :
 | 
						|
                        null;
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Selectors
 | 
						|
            _type_selector: function() {
 | 
						|
                /*
 | 
						|
                 * type_selector
 | 
						|
                 *   : [ namespace_prefix ]? element_name
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    ns          = this._namespace_prefix(),
 | 
						|
                    elementName = this._element_name();
 | 
						|
 | 
						|
                if (!elementName) {
 | 
						|
                    /*
 | 
						|
                     * Need to back out the namespace that was read due to both
 | 
						|
                     * type_selector and universal reading namespace_prefix
 | 
						|
                     * first. Kind of hacky, but only way I can figure out
 | 
						|
                     * right now how to not change the grammar.
 | 
						|
                     */
 | 
						|
                    if (ns) {
 | 
						|
                        tokenStream.unget();
 | 
						|
                        if (ns.length > 1) {
 | 
						|
                            tokenStream.unget();
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
 | 
						|
                    return null;
 | 
						|
                } else {
 | 
						|
                    if (ns) {
 | 
						|
                        elementName.text = ns + elementName.text;
 | 
						|
                        elementName.col -= ns.length;
 | 
						|
                    }
 | 
						|
                    return elementName;
 | 
						|
                }
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Selectors
 | 
						|
            _class: function() {
 | 
						|
                /*
 | 
						|
                 * class
 | 
						|
                 *   : '.' IDENT
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    token;
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.DOT)) {
 | 
						|
                    tokenStream.mustMatch(Tokens.IDENT);
 | 
						|
                    token = tokenStream.token();
 | 
						|
                    return new SelectorSubPart("." + token.value, "class", token.startLine, token.startCol - 1);
 | 
						|
                } else {
 | 
						|
                    return null;
 | 
						|
                }
 | 
						|
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Selectors
 | 
						|
            _element_name: function() {
 | 
						|
                /*
 | 
						|
                 * element_name
 | 
						|
                 *   : IDENT
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    token;
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.IDENT)) {
 | 
						|
                    token = tokenStream.token();
 | 
						|
                    return new SelectorSubPart(token.value, "elementName", token.startLine, token.startCol);
 | 
						|
 | 
						|
                } else {
 | 
						|
                    return null;
 | 
						|
                }
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Selectors
 | 
						|
            _namespace_prefix: function() {
 | 
						|
                /*
 | 
						|
                 * namespace_prefix
 | 
						|
                 *   : [ IDENT | '*' ]? '|'
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    value       = "";
 | 
						|
 | 
						|
                //verify that this is a namespace prefix
 | 
						|
                if (tokenStream.LA(1) === Tokens.PIPE || tokenStream.LA(2) === Tokens.PIPE) {
 | 
						|
 | 
						|
                    if (tokenStream.match([Tokens.IDENT, Tokens.STAR])) {
 | 
						|
                        value += tokenStream.token().value;
 | 
						|
                    }
 | 
						|
 | 
						|
                    tokenStream.mustMatch(Tokens.PIPE);
 | 
						|
                    value += "|";
 | 
						|
 | 
						|
                }
 | 
						|
 | 
						|
                return value.length ? value : null;
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Selectors
 | 
						|
            _universal: function() {
 | 
						|
                /*
 | 
						|
                 * universal
 | 
						|
                 *   : [ namespace_prefix ]? '*'
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    value       = "",
 | 
						|
                    ns;
 | 
						|
 | 
						|
                ns = this._namespace_prefix();
 | 
						|
                if (ns) {
 | 
						|
                    value += ns;
 | 
						|
                }
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.STAR)) {
 | 
						|
                    value += "*";
 | 
						|
                }
 | 
						|
 | 
						|
                return value.length ? value : null;
 | 
						|
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Selectors
 | 
						|
            _attrib: function() {
 | 
						|
                /*
 | 
						|
                 * attrib
 | 
						|
                 *   : '[' S* [ namespace_prefix ]? IDENT S*
 | 
						|
                 *         [ [ PREFIXMATCH |
 | 
						|
                 *             SUFFIXMATCH |
 | 
						|
                 *             SUBSTRINGMATCH |
 | 
						|
                 *             '=' |
 | 
						|
                 *             INCLUDES |
 | 
						|
                 *             DASHMATCH ] S* [ IDENT | STRING ] S*
 | 
						|
                 *         ]? ']'
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    value       = null,
 | 
						|
                    ns,
 | 
						|
                    token;
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.LBRACKET)) {
 | 
						|
                    token = tokenStream.token();
 | 
						|
                    value = token.value;
 | 
						|
                    value += this._readWhitespace();
 | 
						|
 | 
						|
                    ns = this._namespace_prefix();
 | 
						|
 | 
						|
                    if (ns) {
 | 
						|
                        value += ns;
 | 
						|
                    }
 | 
						|
 | 
						|
                    tokenStream.mustMatch(Tokens.IDENT);
 | 
						|
                    value += tokenStream.token().value;
 | 
						|
                    value += this._readWhitespace();
 | 
						|
 | 
						|
                    if (tokenStream.match([Tokens.PREFIXMATCH, Tokens.SUFFIXMATCH, Tokens.SUBSTRINGMATCH,
 | 
						|
                            Tokens.EQUALS, Tokens.INCLUDES, Tokens.DASHMATCH])) {
 | 
						|
 | 
						|
                        value += tokenStream.token().value;
 | 
						|
                        value += this._readWhitespace();
 | 
						|
 | 
						|
                        tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
 | 
						|
                        value += tokenStream.token().value;
 | 
						|
                        value += this._readWhitespace();
 | 
						|
                    }
 | 
						|
 | 
						|
                    tokenStream.mustMatch(Tokens.RBRACKET);
 | 
						|
 | 
						|
                    return new SelectorSubPart(value + "]", "attribute", token.startLine, token.startCol);
 | 
						|
                } else {
 | 
						|
                    return null;
 | 
						|
                }
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Selectors
 | 
						|
            _pseudo: function() {
 | 
						|
 | 
						|
                /*
 | 
						|
                 * pseudo
 | 
						|
                 *   : ':' ':'? [ IDENT | functional_pseudo ]
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    pseudo      = null,
 | 
						|
                    colons      = ":",
 | 
						|
                    line,
 | 
						|
                    col;
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.COLON)) {
 | 
						|
 | 
						|
                    if (tokenStream.match(Tokens.COLON)) {
 | 
						|
                        colons += ":";
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (tokenStream.match(Tokens.IDENT)) {
 | 
						|
                        pseudo = tokenStream.token().value;
 | 
						|
                        line = tokenStream.token().startLine;
 | 
						|
                        col = tokenStream.token().startCol - colons.length;
 | 
						|
                    } else if (tokenStream.peek() === Tokens.FUNCTION) {
 | 
						|
                        line = tokenStream.LT(1).startLine;
 | 
						|
                        col = tokenStream.LT(1).startCol - colons.length;
 | 
						|
                        pseudo = this._functional_pseudo();
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (pseudo) {
 | 
						|
                        pseudo = new SelectorSubPart(colons + pseudo, "pseudo", line, col);
 | 
						|
                    } else {
 | 
						|
                        var startLine = tokenStream.LT(1).startLine,
 | 
						|
                            startCol  = tokenStream.LT(0).startCol;
 | 
						|
                        throw new SyntaxError("Expected a `FUNCTION` or `IDENT` after colon at line " + startLine + ", col " + startCol + ".", startLine, startCol);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                return pseudo;
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Selectors
 | 
						|
            _functional_pseudo: function() {
 | 
						|
                /*
 | 
						|
                 * functional_pseudo
 | 
						|
                 *   : FUNCTION S* expression ')'
 | 
						|
                 *   ;
 | 
						|
                */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    value = null;
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.FUNCTION)) {
 | 
						|
                    value = tokenStream.token().value;
 | 
						|
                    value += this._readWhitespace();
 | 
						|
                    value += this._expression();
 | 
						|
                    tokenStream.mustMatch(Tokens.RPAREN);
 | 
						|
                    value += ")";
 | 
						|
                }
 | 
						|
 | 
						|
                return value;
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Selectors
 | 
						|
            _expression: function() {
 | 
						|
                /*
 | 
						|
                 * expression
 | 
						|
                 *   : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    value       = "";
 | 
						|
 | 
						|
                while (tokenStream.match([Tokens.PLUS, Tokens.MINUS, Tokens.DIMENSION,
 | 
						|
                        Tokens.NUMBER, Tokens.STRING, Tokens.IDENT, Tokens.LENGTH,
 | 
						|
                        Tokens.FREQ, Tokens.ANGLE, Tokens.TIME,
 | 
						|
                        Tokens.RESOLUTION, Tokens.SLASH])) {
 | 
						|
 | 
						|
                    value += tokenStream.token().value;
 | 
						|
                    value += this._readWhitespace();
 | 
						|
                }
 | 
						|
 | 
						|
                return value.length ? value : null;
 | 
						|
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Selectors
 | 
						|
            _negation: function() {
 | 
						|
                /*
 | 
						|
                 * negation
 | 
						|
                 *   : NOT S* negation_arg S* ')'
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    line,
 | 
						|
                    col,
 | 
						|
                    value       = "",
 | 
						|
                    arg,
 | 
						|
                    subpart     = null;
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.NOT)) {
 | 
						|
                    value = tokenStream.token().value;
 | 
						|
                    line = tokenStream.token().startLine;
 | 
						|
                    col = tokenStream.token().startCol;
 | 
						|
                    value += this._readWhitespace();
 | 
						|
                    arg = this._negation_arg();
 | 
						|
                    value += arg;
 | 
						|
                    value += this._readWhitespace();
 | 
						|
                    tokenStream.match(Tokens.RPAREN);
 | 
						|
                    value += tokenStream.token().value;
 | 
						|
 | 
						|
                    subpart = new SelectorSubPart(value, "not", line, col);
 | 
						|
                    subpart.args.push(arg);
 | 
						|
                }
 | 
						|
 | 
						|
                return subpart;
 | 
						|
            },
 | 
						|
 | 
						|
            //CSS3 Selectors
 | 
						|
            _negation_arg: function() {
 | 
						|
                /*
 | 
						|
                 * negation_arg
 | 
						|
                 *   : type_selector | universal | HASH | class | attrib | pseudo
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    args        = [
 | 
						|
                        this._type_selector,
 | 
						|
                        this._universal,
 | 
						|
                        function() {
 | 
						|
                            return tokenStream.match(Tokens.HASH) ?
 | 
						|
                                    new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
 | 
						|
                                    null;
 | 
						|
                        },
 | 
						|
                        this._class,
 | 
						|
                        this._attrib,
 | 
						|
                        this._pseudo
 | 
						|
                    ],
 | 
						|
                    arg         = null,
 | 
						|
                    i           = 0,
 | 
						|
                    len         = args.length,
 | 
						|
                    line,
 | 
						|
                    col,
 | 
						|
                    part;
 | 
						|
 | 
						|
                line = tokenStream.LT(1).startLine;
 | 
						|
                col = tokenStream.LT(1).startCol;
 | 
						|
 | 
						|
                while (i < len && arg === null) {
 | 
						|
 | 
						|
                    arg = args[i].call(this);
 | 
						|
                    i++;
 | 
						|
                }
 | 
						|
 | 
						|
                //must be a negation arg
 | 
						|
                if (arg === null) {
 | 
						|
                    this._unexpectedToken(tokenStream.LT(1));
 | 
						|
                }
 | 
						|
 | 
						|
                //it's an element name
 | 
						|
                if (arg.type === "elementName") {
 | 
						|
                    part = new SelectorPart(arg, [], arg.toString(), line, col);
 | 
						|
                } else {
 | 
						|
                    part = new SelectorPart(null, [arg], arg.toString(), line, col);
 | 
						|
                }
 | 
						|
 | 
						|
                return part;
 | 
						|
            },
 | 
						|
 | 
						|
            _declaration: function() {
 | 
						|
 | 
						|
                /*
 | 
						|
                 * declaration
 | 
						|
                 *   : property ':' S* expr prio?
 | 
						|
                 *   | /( empty )/
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    property    = null,
 | 
						|
                    expr        = null,
 | 
						|
                    prio        = null,
 | 
						|
                    invalid     = null,
 | 
						|
                    propertyName= "";
 | 
						|
 | 
						|
                property = this._property();
 | 
						|
                if (property !== null) {
 | 
						|
 | 
						|
                    tokenStream.mustMatch(Tokens.COLON);
 | 
						|
                    this._readWhitespace();
 | 
						|
 | 
						|
                    expr = this._expr();
 | 
						|
 | 
						|
                    //if there's no parts for the value, it's an error
 | 
						|
                    if (!expr || expr.length === 0) {
 | 
						|
                        this._unexpectedToken(tokenStream.LT(1));
 | 
						|
                    }
 | 
						|
 | 
						|
                    prio = this._prio();
 | 
						|
 | 
						|
                    /*
 | 
						|
                     * If hacks should be allowed, then only check the root
 | 
						|
                     * property. If hacks should not be allowed, treat
 | 
						|
                     * _property or *property as invalid properties.
 | 
						|
                     */
 | 
						|
                    propertyName = property.toString();
 | 
						|
                    if (this.options.starHack && property.hack === "*" ||
 | 
						|
                            this.options.underscoreHack && property.hack === "_") {
 | 
						|
 | 
						|
                        propertyName = property.text;
 | 
						|
                    }
 | 
						|
 | 
						|
                    try {
 | 
						|
                        this._validateProperty(propertyName, expr);
 | 
						|
                    } catch (ex) {
 | 
						|
                        invalid = ex;
 | 
						|
                    }
 | 
						|
 | 
						|
                    this.fire({
 | 
						|
                        type:       "property",
 | 
						|
                        property:   property,
 | 
						|
                        value:      expr,
 | 
						|
                        important:  prio,
 | 
						|
                        line:       property.line,
 | 
						|
                        col:        property.col,
 | 
						|
                        invalid:    invalid
 | 
						|
                    });
 | 
						|
 | 
						|
                    return true;
 | 
						|
                } else {
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
            },
 | 
						|
 | 
						|
            _prio: function() {
 | 
						|
                /*
 | 
						|
                 * prio
 | 
						|
                 *   : IMPORTANT_SYM S*
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    result      = tokenStream.match(Tokens.IMPORTANT_SYM);
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
                return result;
 | 
						|
            },
 | 
						|
 | 
						|
            _expr: function(inFunction) {
 | 
						|
                /*
 | 
						|
                 * expr
 | 
						|
                 *   : term [ operator term ]*
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var values      = [],
 | 
						|
                    //valueParts    = [],
 | 
						|
                    value       = null,
 | 
						|
                    operator    = null;
 | 
						|
 | 
						|
                value = this._term(inFunction);
 | 
						|
                if (value !== null) {
 | 
						|
 | 
						|
                    values.push(value);
 | 
						|
 | 
						|
                    do {
 | 
						|
                        operator = this._operator(inFunction);
 | 
						|
 | 
						|
                        //if there's an operator, keep building up the value parts
 | 
						|
                        if (operator) {
 | 
						|
                            values.push(operator);
 | 
						|
                        } /*else {
 | 
						|
                            //if there's not an operator, you have a full value
 | 
						|
                            values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
 | 
						|
                            valueParts = [];
 | 
						|
                        }*/
 | 
						|
 | 
						|
                        value = this._term(inFunction);
 | 
						|
 | 
						|
                        if (value === null) {
 | 
						|
                            break;
 | 
						|
                        } else {
 | 
						|
                            values.push(value);
 | 
						|
                        }
 | 
						|
                    } while (true);
 | 
						|
                }
 | 
						|
 | 
						|
                //cleanup
 | 
						|
                /*if (valueParts.length) {
 | 
						|
                    values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
 | 
						|
                }*/
 | 
						|
 | 
						|
                return values.length > 0 ? new PropertyValue(values, values[0].line, values[0].col) : null;
 | 
						|
            },
 | 
						|
 | 
						|
            _term: function(inFunction) {
 | 
						|
 | 
						|
                /*
 | 
						|
                 * term
 | 
						|
                 *   : unary_operator?
 | 
						|
                 *     [ NUMBER S* | PERCENTAGE S* | LENGTH S* | ANGLE S* |
 | 
						|
                 *       TIME S* | FREQ S* | function | ie_function ]
 | 
						|
                 *   | STRING S* | IDENT S* | URI S* | UNICODERANGE S* | hexcolor
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    unary       = null,
 | 
						|
                    value       = null,
 | 
						|
                    endChar     = null,
 | 
						|
                    part        = null,
 | 
						|
                    token,
 | 
						|
                    line,
 | 
						|
                    col;
 | 
						|
 | 
						|
                //returns the operator or null
 | 
						|
                unary = this._unary_operator();
 | 
						|
                if (unary !== null) {
 | 
						|
                    line = tokenStream.token().startLine;
 | 
						|
                    col = tokenStream.token().startCol;
 | 
						|
                }
 | 
						|
 | 
						|
                //exception for IE filters
 | 
						|
                if (tokenStream.peek() === Tokens.IE_FUNCTION && this.options.ieFilters) {
 | 
						|
 | 
						|
                    value = this._ie_function();
 | 
						|
                    if (unary === null) {
 | 
						|
                        line = tokenStream.token().startLine;
 | 
						|
                        col = tokenStream.token().startCol;
 | 
						|
                    }
 | 
						|
 | 
						|
                //see if it's a simple block
 | 
						|
                } else if (inFunction && tokenStream.match([Tokens.LPAREN, Tokens.LBRACE, Tokens.LBRACKET])) {
 | 
						|
 | 
						|
                    token = tokenStream.token();
 | 
						|
                    endChar = token.endChar;
 | 
						|
                    value = token.value + this._expr(inFunction).text;
 | 
						|
                    if (unary === null) {
 | 
						|
                        line = tokenStream.token().startLine;
 | 
						|
                        col = tokenStream.token().startCol;
 | 
						|
                    }
 | 
						|
                    tokenStream.mustMatch(Tokens.type(endChar));
 | 
						|
                    value += endChar;
 | 
						|
                    this._readWhitespace();
 | 
						|
 | 
						|
                //see if there's a simple match
 | 
						|
                } else if (tokenStream.match([Tokens.NUMBER, Tokens.PERCENTAGE, Tokens.LENGTH,
 | 
						|
                        Tokens.ANGLE, Tokens.TIME,
 | 
						|
                        Tokens.FREQ, Tokens.STRING, Tokens.IDENT, Tokens.URI, Tokens.UNICODE_RANGE])) {
 | 
						|
 | 
						|
                    value = tokenStream.token().value;
 | 
						|
                    if (unary === null) {
 | 
						|
                        line = tokenStream.token().startLine;
 | 
						|
                        col = tokenStream.token().startCol;
 | 
						|
                        // Correct potentially-inaccurate IDENT parsing in
 | 
						|
                        // PropertyValuePart constructor.
 | 
						|
                        part = PropertyValuePart.fromToken(tokenStream.token());
 | 
						|
                    }
 | 
						|
                    this._readWhitespace();
 | 
						|
                } else {
 | 
						|
 | 
						|
                    //see if it's a color
 | 
						|
                    token = this._hexcolor();
 | 
						|
                    if (token === null) {
 | 
						|
 | 
						|
                        //if there's no unary, get the start of the next token for line/col info
 | 
						|
                        if (unary === null) {
 | 
						|
                            line = tokenStream.LT(1).startLine;
 | 
						|
                            col = tokenStream.LT(1).startCol;
 | 
						|
                        }
 | 
						|
 | 
						|
                        //has to be a function
 | 
						|
                        if (value === null) {
 | 
						|
 | 
						|
                            /*
 | 
						|
                             * This checks for alpha(opacity=0) style of IE
 | 
						|
                             * functions. IE_FUNCTION only presents progid: style.
 | 
						|
                             */
 | 
						|
                            if (tokenStream.LA(3) === Tokens.EQUALS && this.options.ieFilters) {
 | 
						|
                                value = this._ie_function();
 | 
						|
                            } else {
 | 
						|
                                value = this._function();
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
 | 
						|
                        /*if (value === null) {
 | 
						|
                            return null;
 | 
						|
                            //throw new Error("Expected identifier at line " + tokenStream.token().startLine + ", character " +  tokenStream.token().startCol + ".");
 | 
						|
                        }*/
 | 
						|
 | 
						|
                    } else {
 | 
						|
                        value = token.value;
 | 
						|
                        if (unary === null) {
 | 
						|
                            line = token.startLine;
 | 
						|
                            col = token.startCol;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
 | 
						|
                }
 | 
						|
 | 
						|
                return part !== null ? part : value !== null ?
 | 
						|
                        new PropertyValuePart(unary !== null ? unary + value : value, line, col) :
 | 
						|
                        null;
 | 
						|
 | 
						|
            },
 | 
						|
 | 
						|
            _function: function() {
 | 
						|
 | 
						|
                /*
 | 
						|
                 * function
 | 
						|
                 *   : FUNCTION S* expr ')' S*
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    functionText = null,
 | 
						|
                    expr        = null,
 | 
						|
                    lt;
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.FUNCTION)) {
 | 
						|
                    functionText = tokenStream.token().value;
 | 
						|
                    this._readWhitespace();
 | 
						|
                    expr = this._expr(true);
 | 
						|
                    functionText += expr;
 | 
						|
 | 
						|
                    //START: Horrible hack in case it's an IE filter
 | 
						|
                    if (this.options.ieFilters && tokenStream.peek() === Tokens.EQUALS) {
 | 
						|
                        do {
 | 
						|
 | 
						|
                            if (this._readWhitespace()) {
 | 
						|
                                functionText += tokenStream.token().value;
 | 
						|
                            }
 | 
						|
 | 
						|
                            //might be second time in the loop
 | 
						|
                            if (tokenStream.LA(0) === Tokens.COMMA) {
 | 
						|
                                functionText += tokenStream.token().value;
 | 
						|
                            }
 | 
						|
 | 
						|
                            tokenStream.match(Tokens.IDENT);
 | 
						|
                            functionText += tokenStream.token().value;
 | 
						|
 | 
						|
                            tokenStream.match(Tokens.EQUALS);
 | 
						|
                            functionText += tokenStream.token().value;
 | 
						|
 | 
						|
                            //functionText += this._term();
 | 
						|
                            lt = tokenStream.peek();
 | 
						|
                            while (lt !== Tokens.COMMA && lt !== Tokens.S && lt !== Tokens.RPAREN) {
 | 
						|
                                tokenStream.get();
 | 
						|
                                functionText += tokenStream.token().value;
 | 
						|
                                lt = tokenStream.peek();
 | 
						|
                            }
 | 
						|
                        } while (tokenStream.match([Tokens.COMMA, Tokens.S]));
 | 
						|
                    }
 | 
						|
 | 
						|
                    //END: Horrible Hack
 | 
						|
 | 
						|
                    tokenStream.match(Tokens.RPAREN);
 | 
						|
                    functionText += ")";
 | 
						|
                    this._readWhitespace();
 | 
						|
                }
 | 
						|
 | 
						|
                return functionText;
 | 
						|
            },
 | 
						|
 | 
						|
            _ie_function: function() {
 | 
						|
 | 
						|
                /* (My own extension)
 | 
						|
                 * ie_function
 | 
						|
                 *   : IE_FUNCTION S* IDENT '=' term [S* ','? IDENT '=' term]+ ')' S*
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    functionText = null,
 | 
						|
                    lt;
 | 
						|
 | 
						|
                //IE function can begin like a regular function, too
 | 
						|
                if (tokenStream.match([Tokens.IE_FUNCTION, Tokens.FUNCTION])) {
 | 
						|
                    functionText = tokenStream.token().value;
 | 
						|
 | 
						|
                    do {
 | 
						|
 | 
						|
                        if (this._readWhitespace()) {
 | 
						|
                            functionText += tokenStream.token().value;
 | 
						|
                        }
 | 
						|
 | 
						|
                        //might be second time in the loop
 | 
						|
                        if (tokenStream.LA(0) === Tokens.COMMA) {
 | 
						|
                            functionText += tokenStream.token().value;
 | 
						|
                        }
 | 
						|
 | 
						|
                        tokenStream.match(Tokens.IDENT);
 | 
						|
                        functionText += tokenStream.token().value;
 | 
						|
 | 
						|
                        tokenStream.match(Tokens.EQUALS);
 | 
						|
                        functionText += tokenStream.token().value;
 | 
						|
 | 
						|
                        //functionText += this._term();
 | 
						|
                        lt = tokenStream.peek();
 | 
						|
                        while (lt !== Tokens.COMMA && lt !== Tokens.S && lt !== Tokens.RPAREN) {
 | 
						|
                            tokenStream.get();
 | 
						|
                            functionText += tokenStream.token().value;
 | 
						|
                            lt = tokenStream.peek();
 | 
						|
                        }
 | 
						|
                    } while (tokenStream.match([Tokens.COMMA, Tokens.S]));
 | 
						|
 | 
						|
                    tokenStream.match(Tokens.RPAREN);
 | 
						|
                    functionText += ")";
 | 
						|
                    this._readWhitespace();
 | 
						|
                }
 | 
						|
 | 
						|
                return functionText;
 | 
						|
            },
 | 
						|
 | 
						|
            _hexcolor: function() {
 | 
						|
                /*
 | 
						|
                 * There is a constraint on the color that it must
 | 
						|
                 * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
 | 
						|
                 * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
 | 
						|
                 *
 | 
						|
                 * hexcolor
 | 
						|
                 *   : HASH S*
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    token = null,
 | 
						|
                    color;
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.HASH)) {
 | 
						|
 | 
						|
                    //need to do some validation here
 | 
						|
 | 
						|
                    token = tokenStream.token();
 | 
						|
                    color = token.value;
 | 
						|
                    if (!/#[a-f0-9]{3,6}/i.test(color)) {
 | 
						|
                        throw new SyntaxError("Expected a hex color but found '" + color + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
 | 
						|
                    }
 | 
						|
                    this._readWhitespace();
 | 
						|
                }
 | 
						|
 | 
						|
                return token;
 | 
						|
            },
 | 
						|
 | 
						|
            //-----------------------------------------------------------------
 | 
						|
            // Animations methods
 | 
						|
            //-----------------------------------------------------------------
 | 
						|
 | 
						|
            _keyframes: function() {
 | 
						|
 | 
						|
                /*
 | 
						|
                 * keyframes:
 | 
						|
                 *   : KEYFRAMES_SYM S* keyframe_name S* '{' S* keyframe_rule* '}' {
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    token,
 | 
						|
                    tt,
 | 
						|
                    name,
 | 
						|
                    prefix = "";
 | 
						|
 | 
						|
                tokenStream.mustMatch(Tokens.KEYFRAMES_SYM);
 | 
						|
                token = tokenStream.token();
 | 
						|
                if (/^@\-([^\-]+)\-/.test(token.value)) {
 | 
						|
                    prefix = RegExp.$1;
 | 
						|
                }
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
                name = this._keyframe_name();
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
                tokenStream.mustMatch(Tokens.LBRACE);
 | 
						|
 | 
						|
                this.fire({
 | 
						|
                    type:   "startkeyframes",
 | 
						|
                    name:   name,
 | 
						|
                    prefix: prefix,
 | 
						|
                    line:   token.startLine,
 | 
						|
                    col:    token.startCol
 | 
						|
                });
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
                tt = tokenStream.peek();
 | 
						|
 | 
						|
                //check for key
 | 
						|
                while (tt === Tokens.IDENT || tt === Tokens.PERCENTAGE) {
 | 
						|
                    this._keyframe_rule();
 | 
						|
                    this._readWhitespace();
 | 
						|
                    tt = tokenStream.peek();
 | 
						|
                }
 | 
						|
 | 
						|
                this.fire({
 | 
						|
                    type:   "endkeyframes",
 | 
						|
                    name:   name,
 | 
						|
                    prefix: prefix,
 | 
						|
                    line:   token.startLine,
 | 
						|
                    col:    token.startCol
 | 
						|
                });
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
                tokenStream.mustMatch(Tokens.RBRACE);
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
            },
 | 
						|
 | 
						|
            _keyframe_name: function() {
 | 
						|
 | 
						|
                /*
 | 
						|
                 * keyframe_name:
 | 
						|
                 *   : IDENT
 | 
						|
                 *   | STRING
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream;
 | 
						|
 | 
						|
                tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
 | 
						|
                return SyntaxUnit.fromToken(tokenStream.token());
 | 
						|
            },
 | 
						|
 | 
						|
            _keyframe_rule: function() {
 | 
						|
 | 
						|
                /*
 | 
						|
                 * keyframe_rule:
 | 
						|
                 *   : key_list S*
 | 
						|
                 *     '{' S* declaration [ ';' S* declaration ]* '}' S*
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
                var keyList = this._key_list();
 | 
						|
 | 
						|
                this.fire({
 | 
						|
                    type:   "startkeyframerule",
 | 
						|
                    keys:   keyList,
 | 
						|
                    line:   keyList[0].line,
 | 
						|
                    col:    keyList[0].col
 | 
						|
                });
 | 
						|
 | 
						|
                this._readDeclarations(true);
 | 
						|
 | 
						|
                this.fire({
 | 
						|
                    type:   "endkeyframerule",
 | 
						|
                    keys:   keyList,
 | 
						|
                    line:   keyList[0].line,
 | 
						|
                    col:    keyList[0].col
 | 
						|
                });
 | 
						|
 | 
						|
            },
 | 
						|
 | 
						|
            _key_list: function() {
 | 
						|
 | 
						|
                /*
 | 
						|
                 * key_list:
 | 
						|
                 *   : key [ S* ',' S* key]*
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    keyList = [];
 | 
						|
 | 
						|
                //must be least one key
 | 
						|
                keyList.push(this._key());
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                while (tokenStream.match(Tokens.COMMA)) {
 | 
						|
                    this._readWhitespace();
 | 
						|
                    keyList.push(this._key());
 | 
						|
                    this._readWhitespace();
 | 
						|
                }
 | 
						|
 | 
						|
                return keyList;
 | 
						|
            },
 | 
						|
 | 
						|
            _key: function() {
 | 
						|
                /*
 | 
						|
                 * There is a restriction that IDENT can be only "from" or "to".
 | 
						|
                 *
 | 
						|
                 * key
 | 
						|
                 *   : PERCENTAGE
 | 
						|
                 *   | IDENT
 | 
						|
                 *   ;
 | 
						|
                 */
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    token;
 | 
						|
 | 
						|
                if (tokenStream.match(Tokens.PERCENTAGE)) {
 | 
						|
                    return SyntaxUnit.fromToken(tokenStream.token());
 | 
						|
                } else if (tokenStream.match(Tokens.IDENT)) {
 | 
						|
                    token = tokenStream.token();
 | 
						|
 | 
						|
                    if (/from|to/i.test(token.value)) {
 | 
						|
                        return SyntaxUnit.fromToken(token);
 | 
						|
                    }
 | 
						|
 | 
						|
                    tokenStream.unget();
 | 
						|
                }
 | 
						|
 | 
						|
                //if it gets here, there wasn't a valid token, so time to explode
 | 
						|
                this._unexpectedToken(tokenStream.LT(1));
 | 
						|
            },
 | 
						|
 | 
						|
            //-----------------------------------------------------------------
 | 
						|
            // Helper methods
 | 
						|
            //-----------------------------------------------------------------
 | 
						|
 | 
						|
            /**
 | 
						|
             * Not part of CSS grammar, but useful for skipping over
 | 
						|
             * combination of white space and HTML-style comments.
 | 
						|
             * @return {void}
 | 
						|
             * @method _skipCruft
 | 
						|
             * @private
 | 
						|
             */
 | 
						|
            _skipCruft: function() {
 | 
						|
                while (this._tokenStream.match([Tokens.S, Tokens.CDO, Tokens.CDC])) {
 | 
						|
                    //noop
 | 
						|
                }
 | 
						|
            },
 | 
						|
 | 
						|
            /**
 | 
						|
             * Not part of CSS grammar, but this pattern occurs frequently
 | 
						|
             * in the official CSS grammar. Split out here to eliminate
 | 
						|
             * duplicate code.
 | 
						|
             * @param {Boolean} checkStart Indicates if the rule should check
 | 
						|
             *      for the left brace at the beginning.
 | 
						|
             * @param {Boolean} readMargins Indicates if the rule should check
 | 
						|
             *      for margin patterns.
 | 
						|
             * @return {void}
 | 
						|
             * @method _readDeclarations
 | 
						|
             * @private
 | 
						|
             */
 | 
						|
            _readDeclarations: function(checkStart, readMargins) {
 | 
						|
                /*
 | 
						|
                 * Reads the pattern
 | 
						|
                 * S* '{' S* declaration [ ';' S* declaration ]* '}' S*
 | 
						|
                 * or
 | 
						|
                 * S* '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
 | 
						|
                 * Note that this is how it is described in CSS3 Paged Media, but is actually incorrect.
 | 
						|
                 * A semicolon is only necessary following a declaration if there's another declaration
 | 
						|
                 * or margin afterwards.
 | 
						|
                 */
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    tt;
 | 
						|
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                if (checkStart) {
 | 
						|
                    tokenStream.mustMatch(Tokens.LBRACE);
 | 
						|
                }
 | 
						|
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                try {
 | 
						|
 | 
						|
                    while (true) {
 | 
						|
 | 
						|
                        if (tokenStream.match(Tokens.SEMICOLON) || (readMargins && this._margin())) {
 | 
						|
                            //noop
 | 
						|
                        } else if (this._declaration()) {
 | 
						|
                            if (!tokenStream.match(Tokens.SEMICOLON)) {
 | 
						|
                                break;
 | 
						|
                            }
 | 
						|
                        } else {
 | 
						|
                            break;
 | 
						|
                        }
 | 
						|
 | 
						|
                        //if ((!this._margin() && !this._declaration()) || !tokenStream.match(Tokens.SEMICOLON)){
 | 
						|
                        //    break;
 | 
						|
                        //}
 | 
						|
                        this._readWhitespace();
 | 
						|
                    }
 | 
						|
 | 
						|
                    tokenStream.mustMatch(Tokens.RBRACE);
 | 
						|
                    this._readWhitespace();
 | 
						|
 | 
						|
                } catch (ex) {
 | 
						|
                    if (ex instanceof SyntaxError && !this.options.strict) {
 | 
						|
 | 
						|
                        //fire error event
 | 
						|
                        this.fire({
 | 
						|
                            type:       "error",
 | 
						|
                            error:      ex,
 | 
						|
                            message:    ex.message,
 | 
						|
                            line:       ex.line,
 | 
						|
                            col:        ex.col
 | 
						|
                        });
 | 
						|
 | 
						|
                        //see if there's another declaration
 | 
						|
                        tt = tokenStream.advance([Tokens.SEMICOLON, Tokens.RBRACE]);
 | 
						|
                        if (tt === Tokens.SEMICOLON) {
 | 
						|
                            //if there's a semicolon, then there might be another declaration
 | 
						|
                            this._readDeclarations(false, readMargins);
 | 
						|
                        } else if (tt !== Tokens.RBRACE) {
 | 
						|
                            //if there's a right brace, the rule is finished so don't do anything
 | 
						|
                            //otherwise, rethrow the error because it wasn't handled properly
 | 
						|
                            throw ex;
 | 
						|
                        }
 | 
						|
 | 
						|
                    } else {
 | 
						|
                        //not a syntax error, rethrow it
 | 
						|
                        throw ex;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
            },
 | 
						|
 | 
						|
            /**
 | 
						|
             * In some cases, you can end up with two white space tokens in a
 | 
						|
             * row. Instead of making a change in every function that looks for
 | 
						|
             * white space, this function is used to match as much white space
 | 
						|
             * as necessary.
 | 
						|
             * @method _readWhitespace
 | 
						|
             * @return {String} The white space if found, empty string if not.
 | 
						|
             * @private
 | 
						|
             */
 | 
						|
            _readWhitespace: function() {
 | 
						|
 | 
						|
                var tokenStream = this._tokenStream,
 | 
						|
                    ws = "";
 | 
						|
 | 
						|
                while (tokenStream.match(Tokens.S)) {
 | 
						|
                    ws += tokenStream.token().value;
 | 
						|
                }
 | 
						|
 | 
						|
                return ws;
 | 
						|
            },
 | 
						|
 | 
						|
 | 
						|
            /**
 | 
						|
             * Throws an error when an unexpected token is found.
 | 
						|
             * @param {Object} token The token that was found.
 | 
						|
             * @method _unexpectedToken
 | 
						|
             * @return {void}
 | 
						|
             * @private
 | 
						|
             */
 | 
						|
            _unexpectedToken: function(token) {
 | 
						|
                throw new SyntaxError("Unexpected token '" + token.value + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
 | 
						|
            },
 | 
						|
 | 
						|
            /**
 | 
						|
             * Helper method used for parsing subparts of a style sheet.
 | 
						|
             * @return {void}
 | 
						|
             * @method _verifyEnd
 | 
						|
             * @private
 | 
						|
             */
 | 
						|
            _verifyEnd: function() {
 | 
						|
                if (this._tokenStream.LA(1) !== Tokens.EOF) {
 | 
						|
                    this._unexpectedToken(this._tokenStream.LT(1));
 | 
						|
                }
 | 
						|
            },
 | 
						|
 | 
						|
            //-----------------------------------------------------------------
 | 
						|
            // Validation methods
 | 
						|
            //-----------------------------------------------------------------
 | 
						|
            _validateProperty: function(property, value) {
 | 
						|
                Validation.validate(property, value);
 | 
						|
            },
 | 
						|
 | 
						|
            //-----------------------------------------------------------------
 | 
						|
            // Parsing methods
 | 
						|
            //-----------------------------------------------------------------
 | 
						|
 | 
						|
            parse: function(input) {
 | 
						|
                this._tokenStream = new TokenStream(input, Tokens);
 | 
						|
                this._stylesheet();
 | 
						|
            },
 | 
						|
 | 
						|
            parseStyleSheet: function(input) {
 | 
						|
                //just passthrough
 | 
						|
                return this.parse(input);
 | 
						|
            },
 | 
						|
 | 
						|
            parseMediaQuery: function(input) {
 | 
						|
                this._tokenStream = new TokenStream(input, Tokens);
 | 
						|
                var result = this._media_query();
 | 
						|
 | 
						|
                //if there's anything more, then it's an invalid selector
 | 
						|
                this._verifyEnd();
 | 
						|
 | 
						|
                //otherwise return result
 | 
						|
                return result;
 | 
						|
            },
 | 
						|
 | 
						|
            /**
 | 
						|
             * Parses a property value (everything after the semicolon).
 | 
						|
             * @return {parserlib.css.PropertyValue} The property value.
 | 
						|
             * @throws parserlib.util.SyntaxError If an unexpected token is found.
 | 
						|
             * @method parserPropertyValue
 | 
						|
             */
 | 
						|
            parsePropertyValue: function(input) {
 | 
						|
 | 
						|
                this._tokenStream = new TokenStream(input, Tokens);
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                var result = this._expr();
 | 
						|
 | 
						|
                //okay to have a trailing white space
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                //if there's anything more, then it's an invalid selector
 | 
						|
                this._verifyEnd();
 | 
						|
 | 
						|
                //otherwise return result
 | 
						|
                return result;
 | 
						|
            },
 | 
						|
 | 
						|
            /**
 | 
						|
             * Parses a complete CSS rule, including selectors and
 | 
						|
             * properties.
 | 
						|
             * @param {String} input The text to parser.
 | 
						|
             * @return {Boolean} True if the parse completed successfully, false if not.
 | 
						|
             * @method parseRule
 | 
						|
             */
 | 
						|
            parseRule: function(input) {
 | 
						|
                this._tokenStream = new TokenStream(input, Tokens);
 | 
						|
 | 
						|
                //skip any leading white space
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                var result = this._ruleset();
 | 
						|
 | 
						|
                //skip any trailing white space
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                //if there's anything more, then it's an invalid selector
 | 
						|
                this._verifyEnd();
 | 
						|
 | 
						|
                //otherwise return result
 | 
						|
                return result;
 | 
						|
            },
 | 
						|
 | 
						|
            /**
 | 
						|
             * Parses a single CSS selector (no comma)
 | 
						|
             * @param {String} input The text to parse as a CSS selector.
 | 
						|
             * @return {Selector} An object representing the selector.
 | 
						|
             * @throws parserlib.util.SyntaxError If an unexpected token is found.
 | 
						|
             * @method parseSelector
 | 
						|
             */
 | 
						|
            parseSelector: function(input) {
 | 
						|
 | 
						|
                this._tokenStream = new TokenStream(input, Tokens);
 | 
						|
 | 
						|
                //skip any leading white space
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                var result = this._selector();
 | 
						|
 | 
						|
                //skip any trailing white space
 | 
						|
                this._readWhitespace();
 | 
						|
 | 
						|
                //if there's anything more, then it's an invalid selector
 | 
						|
                this._verifyEnd();
 | 
						|
 | 
						|
                //otherwise return result
 | 
						|
                return result;
 | 
						|
            },
 | 
						|
 | 
						|
            /**
 | 
						|
             * Parses an HTML style attribute: a set of CSS declarations
 | 
						|
             * separated by semicolons.
 | 
						|
             * @param {String} input The text to parse as a style attribute
 | 
						|
             * @return {void}
 | 
						|
             * @method parseStyleAttribute
 | 
						|
             */
 | 
						|
            parseStyleAttribute: function(input) {
 | 
						|
                input += "}"; // for error recovery in _readDeclarations()
 | 
						|
                this._tokenStream = new TokenStream(input, Tokens);
 | 
						|
                this._readDeclarations();
 | 
						|
            }
 | 
						|
        };
 | 
						|
 | 
						|
    //copy over onto prototype
 | 
						|
    for (prop in additions) {
 | 
						|
        if (Object.prototype.hasOwnProperty.call(additions, prop)) {
 | 
						|
            proto[prop] = additions[prop];
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return proto;
 | 
						|
}();
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
nth
 | 
						|
  : S* [ ['-'|'+']? INTEGER? {N} [ S* ['-'|'+'] S* INTEGER ]? |
 | 
						|
         ['-'|'+']? INTEGER | {O}{D}{D} | {E}{V}{E}{N} ] S*
 | 
						|
  ;
 | 
						|
*/
 | 
						|
 | 
						|
},{"../util/EventTarget":23,"../util/SyntaxError":25,"../util/SyntaxUnit":26,"./Combinator":2,"./MediaFeature":4,"./MediaQuery":5,"./PropertyName":8,"./PropertyValue":9,"./PropertyValuePart":11,"./Selector":13,"./SelectorPart":14,"./SelectorSubPart":15,"./TokenStream":17,"./Tokens":18,"./Validation":19}],7:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
/* exported Properties */
 | 
						|
 | 
						|
var Properties = module.exports = {
 | 
						|
    __proto__: null,
 | 
						|
 | 
						|
    //A
 | 
						|
    "align-items"                   : "flex-start | flex-end | center | baseline | stretch",
 | 
						|
    "align-content"                 : "flex-start | flex-end | center | space-between | space-around | stretch",
 | 
						|
    "align-self"                    : "auto | flex-start | flex-end | center | baseline | stretch",
 | 
						|
    "all"                           : "initial | inherit | unset",
 | 
						|
    "-webkit-align-items"           : "flex-start | flex-end | center | baseline | stretch",
 | 
						|
    "-webkit-align-content"         : "flex-start | flex-end | center | space-between | space-around | stretch",
 | 
						|
    "-webkit-align-self"            : "auto | flex-start | flex-end | center | baseline | stretch",
 | 
						|
    "alignment-adjust"              : "auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | <percentage> | <length>",
 | 
						|
    "alignment-baseline"            : "auto | baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
 | 
						|
    "animation"                     : 1,
 | 
						|
    "animation-delay"               : "<time>#",
 | 
						|
    "animation-direction"           : "<single-animation-direction>#",
 | 
						|
    "animation-duration"            : "<time>#",
 | 
						|
    "animation-fill-mode"           : "[ none | forwards | backwards | both ]#",
 | 
						|
    "animation-iteration-count"     : "[ <number> | infinite ]#",
 | 
						|
    "animation-name"                : "[ none | <single-animation-name> ]#",
 | 
						|
    "animation-play-state"          : "[ running | paused ]#",
 | 
						|
    "animation-timing-function"     : 1,
 | 
						|
 | 
						|
    //vendor prefixed
 | 
						|
    "-moz-animation-delay"               : "<time>#",
 | 
						|
    "-moz-animation-direction"           : "[ normal | alternate ]#",
 | 
						|
    "-moz-animation-duration"            : "<time>#",
 | 
						|
    "-moz-animation-iteration-count"     : "[ <number> | infinite ]#",
 | 
						|
    "-moz-animation-name"                : "[ none | <single-animation-name> ]#",
 | 
						|
    "-moz-animation-play-state"          : "[ running | paused ]#",
 | 
						|
 | 
						|
    "-ms-animation-delay"               : "<time>#",
 | 
						|
    "-ms-animation-direction"           : "[ normal | alternate ]#",
 | 
						|
    "-ms-animation-duration"            : "<time>#",
 | 
						|
    "-ms-animation-iteration-count"     : "[ <number> | infinite ]#",
 | 
						|
    "-ms-animation-name"                : "[ none | <single-animation-name> ]#",
 | 
						|
    "-ms-animation-play-state"          : "[ running | paused ]#",
 | 
						|
 | 
						|
    "-webkit-animation-delay"               : "<time>#",
 | 
						|
    "-webkit-animation-direction"           : "[ normal | alternate ]#",
 | 
						|
    "-webkit-animation-duration"            : "<time>#",
 | 
						|
    "-webkit-animation-fill-mode"           : "[ none | forwards | backwards | both ]#",
 | 
						|
    "-webkit-animation-iteration-count"     : "[ <number> | infinite ]#",
 | 
						|
    "-webkit-animation-name"                : "[ none | <single-animation-name> ]#",
 | 
						|
    "-webkit-animation-play-state"          : "[ running | paused ]#",
 | 
						|
 | 
						|
    "-o-animation-delay"               : "<time>#",
 | 
						|
    "-o-animation-direction"           : "[ normal | alternate ]#",
 | 
						|
    "-o-animation-duration"            : "<time>#",
 | 
						|
    "-o-animation-iteration-count"     : "[ <number> | infinite ]#",
 | 
						|
    "-o-animation-name"                : "[ none | <single-animation-name> ]#",
 | 
						|
    "-o-animation-play-state"          : "[ running | paused ]#",
 | 
						|
 | 
						|
    "appearance"                    : "none | auto",
 | 
						|
    "-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",
 | 
						|
    "-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",
 | 
						|
    "-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",
 | 
						|
    "-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",
 | 
						|
 | 
						|
    "azimuth"                       : "<azimuth>",
 | 
						|
 | 
						|
    //B
 | 
						|
    "backface-visibility"           : "visible | hidden",
 | 
						|
    "background"                    : 1,
 | 
						|
    "background-attachment"         : "<attachment>#",
 | 
						|
    "background-clip"               : "<box>#",
 | 
						|
    "background-color"              : "<color>",
 | 
						|
    "background-image"              : "<bg-image>#",
 | 
						|
    "background-origin"             : "<box>#",
 | 
						|
    "background-position"           : "<bg-position>",
 | 
						|
    "background-repeat"             : "<repeat-style>#",
 | 
						|
    "background-size"               : "<bg-size>#",
 | 
						|
    "baseline-shift"                : "baseline | sub | super | <percentage> | <length>",
 | 
						|
    "behavior"                      : 1,
 | 
						|
    "binding"                       : 1,
 | 
						|
    "bleed"                         : "<length>",
 | 
						|
    "bookmark-label"                : "<content> | <attr> | <string>",
 | 
						|
    "bookmark-level"                : "none | <integer>",
 | 
						|
    "bookmark-state"                : "open | closed",
 | 
						|
    "bookmark-target"               : "none | <uri> | <attr>",
 | 
						|
    "border"                        : "<border-width> || <border-style> || <color>",
 | 
						|
    "border-bottom"                 : "<border-width> || <border-style> || <color>",
 | 
						|
    "border-bottom-color"           : "<color>",
 | 
						|
    "border-bottom-left-radius"     :  "<x-one-radius>",
 | 
						|
    "border-bottom-right-radius"    :  "<x-one-radius>",
 | 
						|
    "border-bottom-style"           : "<border-style>",
 | 
						|
    "border-bottom-width"           : "<border-width>",
 | 
						|
    "border-collapse"               : "collapse | separate",
 | 
						|
    "border-color"                  : "<color>{1,4}",
 | 
						|
    "border-image"                  : 1,
 | 
						|
    "border-image-outset"           : "[ <length> | <number> ]{1,4}",
 | 
						|
    "border-image-repeat"           : "[ stretch | repeat | round ]{1,2}",
 | 
						|
    "border-image-slice"            : "<border-image-slice>",
 | 
						|
    "border-image-source"           : "<image> | none",
 | 
						|
    "border-image-width"            : "[ <length> | <percentage> | <number> | auto ]{1,4}",
 | 
						|
    "border-left"                   : "<border-width> || <border-style> || <color>",
 | 
						|
    "border-left-color"             : "<color>",
 | 
						|
    "border-left-style"             : "<border-style>",
 | 
						|
    "border-left-width"             : "<border-width>",
 | 
						|
    "border-radius"                 : "<border-radius>",
 | 
						|
    "border-right"                  : "<border-width> || <border-style> || <color>",
 | 
						|
    "border-right-color"            : "<color>",
 | 
						|
    "border-right-style"            : "<border-style>",
 | 
						|
    "border-right-width"            : "<border-width>",
 | 
						|
    "border-spacing"                : "<length>{1,2}",
 | 
						|
    "border-style"                  : "<border-style>{1,4}",
 | 
						|
    "border-top"                    : "<border-width> || <border-style> || <color>",
 | 
						|
    "border-top-color"              : "<color>",
 | 
						|
    "border-top-left-radius"        : "<x-one-radius>",
 | 
						|
    "border-top-right-radius"       : "<x-one-radius>",
 | 
						|
    "border-top-style"              : "<border-style>",
 | 
						|
    "border-top-width"              : "<border-width>",
 | 
						|
    "border-width"                  : "<border-width>{1,4}",
 | 
						|
    "bottom"                        : "<margin-width>",
 | 
						|
    "-moz-box-align"                : "start | end | center | baseline | stretch",
 | 
						|
    "-moz-box-decoration-break"     : "slice | clone",
 | 
						|
    "-moz-box-direction"            : "normal | reverse",
 | 
						|
    "-moz-box-flex"                 : "<number>",
 | 
						|
    "-moz-box-flex-group"           : "<integer>",
 | 
						|
    "-moz-box-lines"                : "single | multiple",
 | 
						|
    "-moz-box-ordinal-group"        : "<integer>",
 | 
						|
    "-moz-box-orient"               : "horizontal | vertical | inline-axis | block-axis",
 | 
						|
    "-moz-box-pack"                 : "start | end | center | justify",
 | 
						|
    "-o-box-decoration-break"       : "slice | clone",
 | 
						|
    "-webkit-box-align"             : "start | end | center | baseline | stretch",
 | 
						|
    "-webkit-box-decoration-break"  : "slice | clone",
 | 
						|
    "-webkit-box-direction"         : "normal | reverse",
 | 
						|
    "-webkit-box-flex"              : "<number>",
 | 
						|
    "-webkit-box-flex-group"        : "<integer>",
 | 
						|
    "-webkit-box-lines"             : "single | multiple",
 | 
						|
    "-webkit-box-ordinal-group"     : "<integer>",
 | 
						|
    "-webkit-box-orient"            : "horizontal | vertical | inline-axis | block-axis",
 | 
						|
    "-webkit-box-pack"              : "start | end | center | justify",
 | 
						|
    "box-decoration-break"          : "slice | clone",
 | 
						|
    "box-shadow"                    : "<box-shadow>",
 | 
						|
    "box-sizing"                    : "content-box | border-box",
 | 
						|
    "break-after"                   : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
 | 
						|
    "break-before"                  : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
 | 
						|
    "break-inside"                  : "auto | avoid | avoid-page | avoid-column",
 | 
						|
 | 
						|
    //C
 | 
						|
    "caption-side"                  : "top | bottom",
 | 
						|
    "clear"                         : "none | right | left | both",
 | 
						|
    "clip"                          : "<shape> | auto",
 | 
						|
    "-webkit-clip-path"             : "<clip-source> | <clip-path> | none",
 | 
						|
    "clip-path"                     : "<clip-source> | <clip-path> | none",
 | 
						|
    "clip-rule"                     : "nonzero | evenodd",
 | 
						|
    "color"                         : "<color>",
 | 
						|
    "color-interpolation"           : "auto | sRGB | linearRGB",
 | 
						|
    "color-interpolation-filters"   : "auto | sRGB | linearRGB",
 | 
						|
    "color-profile"                 : 1,
 | 
						|
    "color-rendering"               : "auto | optimizeSpeed | optimizeQuality",
 | 
						|
    "column-count"                  : "<integer> | auto",                      //https://www.w3.org/TR/css3-multicol/
 | 
						|
    "column-fill"                   : "auto | balance",
 | 
						|
    "column-gap"                    : "<length> | normal",
 | 
						|
    "column-rule"                   : "<border-width> || <border-style> || <color>",
 | 
						|
    "column-rule-color"             : "<color>",
 | 
						|
    "column-rule-style"             : "<border-style>",
 | 
						|
    "column-rule-width"             : "<border-width>",
 | 
						|
    "column-span"                   : "none | all",
 | 
						|
    "column-width"                  : "<length> | auto",
 | 
						|
    "columns"                       : 1,
 | 
						|
    "content"                       : 1,
 | 
						|
    "counter-increment"             : 1,
 | 
						|
    "counter-reset"                 : 1,
 | 
						|
    "crop"                          : "<shape> | auto",
 | 
						|
    "cue"                           : "cue-after | cue-before",
 | 
						|
    "cue-after"                     : 1,
 | 
						|
    "cue-before"                    : 1,
 | 
						|
    "cursor"                        : 1,
 | 
						|
 | 
						|
    //D
 | 
						|
    "direction"                     : "ltr | rtl",
 | 
						|
    "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",
 | 
						|
    "dominant-baseline"             : "auto | use-script | no-change | reset-size | ideographic | alphabetic | hanging | mathematical | central | middle | text-after-edge | text-before-edge",
 | 
						|
    "drop-initial-after-adjust"     : "central | middle | after-edge | text-after-edge | ideographic | alphabetic | mathematical | <percentage> | <length>",
 | 
						|
    "drop-initial-after-align"      : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
 | 
						|
    "drop-initial-before-adjust"    : "before-edge | text-before-edge | central | middle | hanging | mathematical | <percentage> | <length>",
 | 
						|
    "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",
 | 
						|
    "drop-initial-size"             : "auto | line | <length> | <percentage>",
 | 
						|
    "drop-initial-value"            : "<integer>",
 | 
						|
 | 
						|
    //E
 | 
						|
    "elevation"                     : "<angle> | below | level | above | higher | lower",
 | 
						|
    "empty-cells"                   : "show | hide",
 | 
						|
    "enable-background"             : 1,
 | 
						|
 | 
						|
    //F
 | 
						|
    "fill"                          : "<paint>",
 | 
						|
    "fill-opacity"                  : "<opacity-value>",
 | 
						|
    "fill-rule"                     : "nonzero | evenodd",
 | 
						|
    "filter"                        : "<filter-function-list> | none",
 | 
						|
    "fit"                           : "fill | hidden | meet | slice",
 | 
						|
    "fit-position"                  : 1,
 | 
						|
    "flex"                          : "<flex>",
 | 
						|
    "flex-basis"                    : "<width>",
 | 
						|
    "flex-direction"                : "row | row-reverse | column | column-reverse",
 | 
						|
    "flex-flow"                     : "<flex-direction> || <flex-wrap>",
 | 
						|
    "flex-grow"                     : "<number>",
 | 
						|
    "flex-shrink"                   : "<number>",
 | 
						|
    "flex-wrap"                     : "nowrap | wrap | wrap-reverse",
 | 
						|
    "-webkit-flex"                  : "<flex>",
 | 
						|
    "-webkit-flex-basis"            : "<width>",
 | 
						|
    "-webkit-flex-direction"        : "row | row-reverse | column | column-reverse",
 | 
						|
    "-webkit-flex-flow"             : "<flex-direction> || <flex-wrap>",
 | 
						|
    "-webkit-flex-grow"             : "<number>",
 | 
						|
    "-webkit-flex-shrink"           : "<number>",
 | 
						|
    "-webkit-flex-wrap"             : "nowrap | wrap | wrap-reverse",
 | 
						|
    "-ms-flex"                      : "<flex>",
 | 
						|
    "-ms-flex-align"                : "start | end | center | stretch | baseline",
 | 
						|
    "-ms-flex-direction"            : "row | row-reverse | column | column-reverse",
 | 
						|
    "-ms-flex-order"                : "<number>",
 | 
						|
    "-ms-flex-pack"                 : "start | end | center | justify",
 | 
						|
    "-ms-flex-wrap"                 : "nowrap | wrap | wrap-reverse",
 | 
						|
    "float"                         : "left | right | none",
 | 
						|
    "float-offset"                  : 1,
 | 
						|
    "flood-color"                   : 1,
 | 
						|
    "flood-opacity"                 : "<opacity-value>",
 | 
						|
    "font"                          : "<font-shorthand> | caption | icon | menu | message-box | small-caption | status-bar",
 | 
						|
    "font-family"                   : "<font-family>",
 | 
						|
    "font-feature-settings"         : "<feature-tag-value> | normal",
 | 
						|
    "font-kerning"                  : "auto | normal | none",
 | 
						|
    "font-size"                     : "<font-size>",
 | 
						|
    "font-size-adjust"              : "<number> | none",
 | 
						|
    "font-stretch"                  : "<font-stretch>",
 | 
						|
    "font-style"                    : "<font-style>",
 | 
						|
    "font-variant"                  : "<font-variant> | normal | none",
 | 
						|
    "font-variant-alternates"       : "<font-variant-alternates> | normal",
 | 
						|
    "font-variant-caps"             : "<font-variant-caps> | normal",
 | 
						|
    "font-variant-east-asian"       : "<font-variant-east-asian> | normal",
 | 
						|
    "font-variant-ligatures"        : "<font-variant-ligatures> | normal | none",
 | 
						|
    "font-variant-numeric"          : "<font-variant-numeric> | normal",
 | 
						|
    "font-variant-position"         : "normal | sub | super",
 | 
						|
    "font-weight"                   : "<font-weight>",
 | 
						|
 | 
						|
    //G
 | 
						|
    "glyph-orientation-horizontal"  : "<glyph-angle>",
 | 
						|
    "glyph-orientation-vertical"    : "auto | <glyph-angle>",
 | 
						|
    "grid"                          : 1,
 | 
						|
    "grid-area"                     : 1,
 | 
						|
    "grid-auto-columns"             : 1,
 | 
						|
    "grid-auto-flow"                : 1,
 | 
						|
    "grid-auto-position"            : 1,
 | 
						|
    "grid-auto-rows"                : 1,
 | 
						|
    "grid-cell-stacking"            : "columns | rows | layer",
 | 
						|
    "grid-column"                   : 1,
 | 
						|
    "grid-columns"                  : 1,
 | 
						|
    "grid-column-align"             : "start | end | center | stretch",
 | 
						|
    "grid-column-sizing"            : 1,
 | 
						|
    "grid-column-start"             : 1,
 | 
						|
    "grid-column-end"               : 1,
 | 
						|
    "grid-column-span"              : "<integer>",
 | 
						|
    "grid-flow"                     : "none | rows | columns",
 | 
						|
    "grid-layer"                    : "<integer>",
 | 
						|
    "grid-row"                      : 1,
 | 
						|
    "grid-rows"                     : 1,
 | 
						|
    "grid-row-align"                : "start | end | center | stretch",
 | 
						|
    "grid-row-start"                : 1,
 | 
						|
    "grid-row-end"                  : 1,
 | 
						|
    "grid-row-span"                 : "<integer>",
 | 
						|
    "grid-row-sizing"               : 1,
 | 
						|
    "grid-template"                 : 1,
 | 
						|
    "grid-template-areas"           : 1,
 | 
						|
    "grid-template-columns"         : 1,
 | 
						|
    "grid-template-rows"            : 1,
 | 
						|
 | 
						|
    //H
 | 
						|
    "hanging-punctuation"           : 1,
 | 
						|
    "height"                        : "<margin-width> | <content-sizing>",
 | 
						|
    "hyphenate-after"               : "<integer> | auto",
 | 
						|
    "hyphenate-before"              : "<integer> | auto",
 | 
						|
    "hyphenate-character"           : "<string> | auto",
 | 
						|
    "hyphenate-lines"               : "no-limit | <integer>",
 | 
						|
    "hyphenate-resource"            : 1,
 | 
						|
    "hyphens"                       : "none | manual | auto",
 | 
						|
 | 
						|
    //I
 | 
						|
    "icon"                          : 1,
 | 
						|
    "image-orientation"             : "angle | auto",
 | 
						|
    "image-rendering"               : "auto | optimizeSpeed | optimizeQuality",
 | 
						|
    "image-resolution"              : 1,
 | 
						|
    "ime-mode"                      : "auto | normal | active | inactive | disabled",
 | 
						|
    "inline-box-align"              : "last | <integer>",
 | 
						|
 | 
						|
    //J
 | 
						|
    "justify-content"               : "flex-start | flex-end | center | space-between | space-around",
 | 
						|
    "-webkit-justify-content"       : "flex-start | flex-end | center | space-between | space-around",
 | 
						|
 | 
						|
    //K
 | 
						|
    "kerning"                       : "auto | <length>",
 | 
						|
 | 
						|
    //L
 | 
						|
    "left"                          : "<margin-width>",
 | 
						|
    "letter-spacing"                : "<length> | normal",
 | 
						|
    "line-height"                   : "<line-height>",
 | 
						|
    "line-break"                    : "auto | loose | normal | strict",
 | 
						|
    "line-stacking"                 : 1,
 | 
						|
    "line-stacking-ruby"            : "exclude-ruby | include-ruby",
 | 
						|
    "line-stacking-shift"           : "consider-shifts | disregard-shifts",
 | 
						|
    "line-stacking-strategy"        : "inline-line-height | block-line-height | max-height | grid-height",
 | 
						|
    "list-style"                    : 1,
 | 
						|
    "list-style-image"              : "<uri> | none",
 | 
						|
    "list-style-position"           : "inside | outside",
 | 
						|
    "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",
 | 
						|
 | 
						|
    //M
 | 
						|
    "margin"                        : "<margin-width>{1,4}",
 | 
						|
    "margin-bottom"                 : "<margin-width>",
 | 
						|
    "margin-left"                   : "<margin-width>",
 | 
						|
    "margin-right"                  : "<margin-width>",
 | 
						|
    "margin-top"                    : "<margin-width>",
 | 
						|
    "mark"                          : 1,
 | 
						|
    "mark-after"                    : 1,
 | 
						|
    "mark-before"                   : 1,
 | 
						|
    "marker"                        : 1,
 | 
						|
    "marker-end"                    : 1,
 | 
						|
    "marker-mid"                    : 1,
 | 
						|
    "marker-start"                  : 1,
 | 
						|
    "marks"                         : 1,
 | 
						|
    "marquee-direction"             : 1,
 | 
						|
    "marquee-play-count"            : 1,
 | 
						|
    "marquee-speed"                 : 1,
 | 
						|
    "marquee-style"                 : 1,
 | 
						|
    "mask"                          : 1,
 | 
						|
    "max-height"                    : "<length> | <percentage> | <content-sizing> | none",
 | 
						|
    "max-width"                     : "<length> | <percentage> | <content-sizing> | none",
 | 
						|
    "min-height"                    : "<length> | <percentage> | <content-sizing> | contain-floats | -moz-contain-floats | -webkit-contain-floats",
 | 
						|
    "min-width"                     : "<length> | <percentage> | <content-sizing> | contain-floats | -moz-contain-floats | -webkit-contain-floats",
 | 
						|
    "move-to"                       : 1,
 | 
						|
 | 
						|
    //N
 | 
						|
    "nav-down"                      : 1,
 | 
						|
    "nav-index"                     : 1,
 | 
						|
    "nav-left"                      : 1,
 | 
						|
    "nav-right"                     : 1,
 | 
						|
    "nav-up"                        : 1,
 | 
						|
 | 
						|
    //O
 | 
						|
    "object-fit"                    : "fill | contain | cover | none | scale-down",
 | 
						|
    "object-position"               : "<position>",
 | 
						|
    "opacity"                       : "<opacity-value>",
 | 
						|
    "order"                         : "<integer>",
 | 
						|
    "-webkit-order"                 : "<integer>",
 | 
						|
    "orphans"                       : "<integer>",
 | 
						|
    "outline"                       : 1,
 | 
						|
    "outline-color"                 : "<color> | invert",
 | 
						|
    "outline-offset"                : 1,
 | 
						|
    "outline-style"                 : "<border-style>",
 | 
						|
    "outline-width"                 : "<border-width>",
 | 
						|
    "overflow"                      : "visible | hidden | scroll | auto",
 | 
						|
    "overflow-style"                : 1,
 | 
						|
    "overflow-wrap"                 : "normal | break-word",
 | 
						|
    "overflow-x"                    : 1,
 | 
						|
    "overflow-y"                    : 1,
 | 
						|
 | 
						|
    //P
 | 
						|
    "padding"                       : "<padding-width>{1,4}",
 | 
						|
    "padding-bottom"                : "<padding-width>",
 | 
						|
    "padding-left"                  : "<padding-width>",
 | 
						|
    "padding-right"                 : "<padding-width>",
 | 
						|
    "padding-top"                   : "<padding-width>",
 | 
						|
    "page"                          : 1,
 | 
						|
    "page-break-after"              : "auto | always | avoid | left | right",
 | 
						|
    "page-break-before"             : "auto | always | avoid | left | right",
 | 
						|
    "page-break-inside"             : "auto | avoid",
 | 
						|
    "page-policy"                   : 1,
 | 
						|
    "pause"                         : 1,
 | 
						|
    "pause-after"                   : 1,
 | 
						|
    "pause-before"                  : 1,
 | 
						|
    "perspective"                   : 1,
 | 
						|
    "perspective-origin"            : 1,
 | 
						|
    "phonemes"                      : 1,
 | 
						|
    "pitch"                         : 1,
 | 
						|
    "pitch-range"                   : 1,
 | 
						|
    "play-during"                   : 1,
 | 
						|
    "pointer-events"                : "auto | none | visiblePainted | visibleFill | visibleStroke | visible | painted | fill | stroke | all",
 | 
						|
    "position"                      : "static | relative | absolute | fixed",
 | 
						|
    "presentation-level"            : 1,
 | 
						|
    "punctuation-trim"              : 1,
 | 
						|
 | 
						|
    //Q
 | 
						|
    "quotes"                        : 1,
 | 
						|
 | 
						|
    //R
 | 
						|
    "rendering-intent"              : 1,
 | 
						|
    "resize"                        : 1,
 | 
						|
    "rest"                          : 1,
 | 
						|
    "rest-after"                    : 1,
 | 
						|
    "rest-before"                   : 1,
 | 
						|
    "richness"                      : 1,
 | 
						|
    "right"                         : "<margin-width>",
 | 
						|
    "rotation"                      : 1,
 | 
						|
    "rotation-point"                : 1,
 | 
						|
    "ruby-align"                    : 1,
 | 
						|
    "ruby-overhang"                 : 1,
 | 
						|
    "ruby-position"                 : 1,
 | 
						|
    "ruby-span"                     : 1,
 | 
						|
 | 
						|
    //S
 | 
						|
    "shape-rendering"               : "auto | optimizeSpeed | crispEdges | geometricPrecision",
 | 
						|
    "size"                          : 1,
 | 
						|
    "speak"                         : "normal | none | spell-out",
 | 
						|
    "speak-header"                  : "once | always",
 | 
						|
    "speak-numeral"                 : "digits | continuous",
 | 
						|
    "speak-punctuation"             : "code | none",
 | 
						|
    "speech-rate"                   : 1,
 | 
						|
    "src"                           : 1,
 | 
						|
    "stop-color"                    : 1,
 | 
						|
    "stop-opacity"                  : "<opacity-value>",
 | 
						|
    "stress"                        : 1,
 | 
						|
    "string-set"                    : 1,
 | 
						|
    "stroke"                        : "<paint>",
 | 
						|
    "stroke-dasharray"              : "none | <dasharray>",
 | 
						|
    "stroke-dashoffset"             : "<percentage> | <length>",
 | 
						|
    "stroke-linecap"                : "butt | round | square",
 | 
						|
    "stroke-linejoin"               : "miter | round | bevel",
 | 
						|
    "stroke-miterlimit"             : "<miterlimit>",
 | 
						|
    "stroke-opacity"                : "<opacity-value>",
 | 
						|
    "stroke-width"                  : "<percentage> | <length>",
 | 
						|
 | 
						|
    "table-layout"                  : "auto | fixed",
 | 
						|
    "tab-size"                      : "<integer> | <length>",
 | 
						|
    "target"                        : 1,
 | 
						|
    "target-name"                   : 1,
 | 
						|
    "target-new"                    : 1,
 | 
						|
    "target-position"               : 1,
 | 
						|
    "text-align"                    : "left | right | center | justify | match-parent | start | end",
 | 
						|
    "text-align-last"               : 1,
 | 
						|
    "text-anchor"                   : "start | middle | end",
 | 
						|
    "text-decoration"               : "<text-decoration-line> || <text-decoration-style> || <text-decoration-color>",
 | 
						|
    "text-decoration-color"         : "<text-decoration-color>",
 | 
						|
    "text-decoration-line"          : "<text-decoration-line>",
 | 
						|
    "text-decoration-style"         : "<text-decoration-style>",
 | 
						|
    "text-emphasis"                 : 1,
 | 
						|
    "text-height"                   : 1,
 | 
						|
    "text-indent"                   : "<length> | <percentage>",
 | 
						|
    "text-justify"                  : "auto | none | inter-word | inter-ideograph | inter-cluster | distribute | kashida",
 | 
						|
    "text-outline"                  : 1,
 | 
						|
    "text-overflow"                 : 1,
 | 
						|
    "text-rendering"                : "auto | optimizeSpeed | optimizeLegibility | geometricPrecision",
 | 
						|
    "text-shadow"                   : 1,
 | 
						|
    "text-transform"                : "capitalize | uppercase | lowercase | none",
 | 
						|
    "text-wrap"                     : "normal | none | avoid",
 | 
						|
    "top"                           : "<margin-width>",
 | 
						|
    "-ms-touch-action"              : "auto | none | pan-x | pan-y | pan-left | pan-right | pan-up | pan-down | manipulation",
 | 
						|
    "touch-action"                  : "auto | none | pan-x | pan-y | pan-left | pan-right | pan-up | pan-down | manipulation",
 | 
						|
    "transform"                     : 1,
 | 
						|
    "transform-origin"              : 1,
 | 
						|
    "transform-style"               : 1,
 | 
						|
    "transition"                    : 1,
 | 
						|
    "transition-delay"              : 1,
 | 
						|
    "transition-duration"           : 1,
 | 
						|
    "transition-property"           : 1,
 | 
						|
    "transition-timing-function"    : 1,
 | 
						|
 | 
						|
    //U
 | 
						|
    "unicode-bidi"                  : "normal | embed | isolate | bidi-override | isolate-override | plaintext",
 | 
						|
    "user-modify"                   : "read-only | read-write | write-only",
 | 
						|
    "user-select"                   : "none | text | toggle | element | elements | all",
 | 
						|
 | 
						|
    //V
 | 
						|
    "vertical-align"                : "auto | use-script | baseline | sub | super | top | text-top | central | middle | bottom | text-bottom | <percentage> | <length>",
 | 
						|
    "visibility"                    : "visible | hidden | collapse",
 | 
						|
    "voice-balance"                 : 1,
 | 
						|
    "voice-duration"                : 1,
 | 
						|
    "voice-family"                  : 1,
 | 
						|
    "voice-pitch"                   : 1,
 | 
						|
    "voice-pitch-range"             : 1,
 | 
						|
    "voice-rate"                    : 1,
 | 
						|
    "voice-stress"                  : 1,
 | 
						|
    "voice-volume"                  : 1,
 | 
						|
    "volume"                        : 1,
 | 
						|
 | 
						|
    //W
 | 
						|
    "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/
 | 
						|
    "white-space-collapse"          : 1,
 | 
						|
    "widows"                        : "<integer>",
 | 
						|
    "width"                         : "<length> | <percentage> | <content-sizing> | auto",
 | 
						|
    "will-change"                   : "<will-change>",
 | 
						|
    "word-break"                    : "normal | keep-all | break-all",
 | 
						|
    "word-spacing"                  : "<length> | normal",
 | 
						|
    "word-wrap"                     : "normal | break-word",
 | 
						|
    "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",
 | 
						|
 | 
						|
    //Z
 | 
						|
    "z-index"                       : "<integer> | auto",
 | 
						|
    "zoom"                          : "<number> | <percentage> | normal"
 | 
						|
};
 | 
						|
 | 
						|
},{}],8:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = PropertyName;
 | 
						|
 | 
						|
var SyntaxUnit = require("../util/SyntaxUnit");
 | 
						|
 | 
						|
var Parser = require("./Parser");
 | 
						|
 | 
						|
/**
 | 
						|
 * Represents a selector combinator (whitespace, +, >).
 | 
						|
 * @namespace parserlib.css
 | 
						|
 * @class PropertyName
 | 
						|
 * @extends parserlib.util.SyntaxUnit
 | 
						|
 * @constructor
 | 
						|
 * @param {String} text The text representation of the unit.
 | 
						|
 * @param {String} hack The type of IE hack applied ("*", "_", or null).
 | 
						|
 * @param {int} line The line of text on which the unit resides.
 | 
						|
 * @param {int} col The column of text on which the unit resides.
 | 
						|
 */
 | 
						|
function PropertyName(text, hack, line, col) {
 | 
						|
 | 
						|
    SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_NAME_TYPE);
 | 
						|
 | 
						|
    /**
 | 
						|
     * The type of IE hack applied ("*", "_", or null).
 | 
						|
     * @type String
 | 
						|
     * @property hack
 | 
						|
     */
 | 
						|
    this.hack = hack;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
PropertyName.prototype = new SyntaxUnit();
 | 
						|
PropertyName.prototype.constructor = PropertyName;
 | 
						|
PropertyName.prototype.toString = function() {
 | 
						|
    return (this.hack ? this.hack : "") + this.text;
 | 
						|
};
 | 
						|
 | 
						|
},{"../util/SyntaxUnit":26,"./Parser":6}],9:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = PropertyValue;
 | 
						|
 | 
						|
var SyntaxUnit = require("../util/SyntaxUnit");
 | 
						|
 | 
						|
var Parser = require("./Parser");
 | 
						|
 | 
						|
/**
 | 
						|
 * Represents a single part of a CSS property value, meaning that it represents
 | 
						|
 * just everything single part between ":" and ";". If there are multiple values
 | 
						|
 * separated by commas, this type represents just one of the values.
 | 
						|
 * @param {String[]} parts An array of value parts making up this value.
 | 
						|
 * @param {int} line The line of text on which the unit resides.
 | 
						|
 * @param {int} col The column of text on which the unit resides.
 | 
						|
 * @namespace parserlib.css
 | 
						|
 * @class PropertyValue
 | 
						|
 * @extends parserlib.util.SyntaxUnit
 | 
						|
 * @constructor
 | 
						|
 */
 | 
						|
function PropertyValue(parts, line, col) {
 | 
						|
 | 
						|
    SyntaxUnit.call(this, parts.join(" "), line, col, Parser.PROPERTY_VALUE_TYPE);
 | 
						|
 | 
						|
    /**
 | 
						|
     * The parts that make up the selector.
 | 
						|
     * @type Array
 | 
						|
     * @property parts
 | 
						|
     */
 | 
						|
    this.parts = parts;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
PropertyValue.prototype = new SyntaxUnit();
 | 
						|
PropertyValue.prototype.constructor = PropertyValue;
 | 
						|
 | 
						|
 | 
						|
},{"../util/SyntaxUnit":26,"./Parser":6}],10:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = PropertyValueIterator;
 | 
						|
 | 
						|
/**
 | 
						|
 * A utility class that allows for easy iteration over the various parts of a
 | 
						|
 * property value.
 | 
						|
 * @param {parserlib.css.PropertyValue} value The property value to iterate over.
 | 
						|
 * @namespace parserlib.css
 | 
						|
 * @class PropertyValueIterator
 | 
						|
 * @constructor
 | 
						|
 */
 | 
						|
function PropertyValueIterator(value) {
 | 
						|
 | 
						|
    /**
 | 
						|
     * Iterator value
 | 
						|
     * @type int
 | 
						|
     * @property _i
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    this._i = 0;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The parts that make up the value.
 | 
						|
     * @type Array
 | 
						|
     * @property _parts
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    this._parts = value.parts;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Keeps track of bookmarks along the way.
 | 
						|
     * @type Array
 | 
						|
     * @property _marks
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    this._marks = [];
 | 
						|
 | 
						|
    /**
 | 
						|
     * Holds the original property value.
 | 
						|
     * @type parserlib.css.PropertyValue
 | 
						|
     * @property value
 | 
						|
     */
 | 
						|
    this.value = value;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Returns the total number of parts in the value.
 | 
						|
 * @return {int} The total number of parts in the value.
 | 
						|
 * @method count
 | 
						|
 */
 | 
						|
PropertyValueIterator.prototype.count = function() {
 | 
						|
    return this._parts.length;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Indicates if the iterator is positioned at the first item.
 | 
						|
 * @return {Boolean} True if positioned at first item, false if not.
 | 
						|
 * @method isFirst
 | 
						|
 */
 | 
						|
PropertyValueIterator.prototype.isFirst = function() {
 | 
						|
    return this._i === 0;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Indicates if there are more parts of the property value.
 | 
						|
 * @return {Boolean} True if there are more parts, false if not.
 | 
						|
 * @method hasNext
 | 
						|
 */
 | 
						|
PropertyValueIterator.prototype.hasNext = function() {
 | 
						|
    return this._i < this._parts.length;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Marks the current spot in the iteration so it can be restored to
 | 
						|
 * later on.
 | 
						|
 * @return {void}
 | 
						|
 * @method mark
 | 
						|
 */
 | 
						|
PropertyValueIterator.prototype.mark = function() {
 | 
						|
    this._marks.push(this._i);
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Returns the next part of the property value or null if there is no next
 | 
						|
 * part. Does not move the internal counter forward.
 | 
						|
 * @return {parserlib.css.PropertyValuePart} The next part of the property value or null if there is no next
 | 
						|
 * part.
 | 
						|
 * @method peek
 | 
						|
 */
 | 
						|
PropertyValueIterator.prototype.peek = function(count) {
 | 
						|
    return this.hasNext() ? this._parts[this._i + (count || 0)] : null;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Returns the next part of the property value or null if there is no next
 | 
						|
 * part.
 | 
						|
 * @return {parserlib.css.PropertyValuePart} The next part of the property value or null if there is no next
 | 
						|
 * part.
 | 
						|
 * @method next
 | 
						|
 */
 | 
						|
PropertyValueIterator.prototype.next = function() {
 | 
						|
    return this.hasNext() ? this._parts[this._i++] : null;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Returns the previous part of the property value or null if there is no
 | 
						|
 * previous part.
 | 
						|
 * @return {parserlib.css.PropertyValuePart} The previous part of the
 | 
						|
 * property value or null if there is no previous part.
 | 
						|
 * @method previous
 | 
						|
 */
 | 
						|
PropertyValueIterator.prototype.previous = function() {
 | 
						|
    return this._i > 0 ? this._parts[--this._i] : null;
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Restores the last saved bookmark.
 | 
						|
 * @return {void}
 | 
						|
 * @method restore
 | 
						|
 */
 | 
						|
PropertyValueIterator.prototype.restore = function() {
 | 
						|
    if (this._marks.length) {
 | 
						|
        this._i = this._marks.pop();
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Drops the last saved bookmark.
 | 
						|
 * @return {void}
 | 
						|
 * @method drop
 | 
						|
 */
 | 
						|
PropertyValueIterator.prototype.drop = function() {
 | 
						|
    this._marks.pop();
 | 
						|
};
 | 
						|
 | 
						|
},{}],11:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = PropertyValuePart;
 | 
						|
 | 
						|
var SyntaxUnit = require("../util/SyntaxUnit");
 | 
						|
 | 
						|
var Colors = require("./Colors");
 | 
						|
var Parser = require("./Parser");
 | 
						|
var Tokens = require("./Tokens");
 | 
						|
 | 
						|
/**
 | 
						|
 * Represents a single part of a CSS property value, meaning that it represents
 | 
						|
 * just one part of the data between ":" and ";".
 | 
						|
 * @param {String} text The text representation of the unit.
 | 
						|
 * @param {int} line The line of text on which the unit resides.
 | 
						|
 * @param {int} col The column of text on which the unit resides.
 | 
						|
 * @namespace parserlib.css
 | 
						|
 * @class PropertyValuePart
 | 
						|
 * @extends parserlib.util.SyntaxUnit
 | 
						|
 * @constructor
 | 
						|
 */
 | 
						|
function PropertyValuePart(text, line, col, optionalHint) {
 | 
						|
    var hint = optionalHint || {};
 | 
						|
 | 
						|
    SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_VALUE_PART_TYPE);
 | 
						|
 | 
						|
    /**
 | 
						|
     * Indicates the type of value unit.
 | 
						|
     * @type String
 | 
						|
     * @property type
 | 
						|
     */
 | 
						|
    this.type = "unknown";
 | 
						|
 | 
						|
    //figure out what type of data it is
 | 
						|
 | 
						|
    var temp;
 | 
						|
 | 
						|
    //it is a measurement?
 | 
						|
    if (/^([+\-]?[\d\.]+)([a-z]+)$/i.test(text)) {  //dimension
 | 
						|
        this.type = "dimension";
 | 
						|
        this.value = +RegExp.$1;
 | 
						|
        this.units = RegExp.$2;
 | 
						|
 | 
						|
        //try to narrow down
 | 
						|
        switch (this.units.toLowerCase()) {
 | 
						|
 | 
						|
            case "em":
 | 
						|
            case "rem":
 | 
						|
            case "ex":
 | 
						|
            case "px":
 | 
						|
            case "cm":
 | 
						|
            case "mm":
 | 
						|
            case "in":
 | 
						|
            case "pt":
 | 
						|
            case "pc":
 | 
						|
            case "ch":
 | 
						|
            case "vh":
 | 
						|
            case "vw":
 | 
						|
            case "vmax":
 | 
						|
            case "vmin":
 | 
						|
                this.type = "length";
 | 
						|
                break;
 | 
						|
 | 
						|
            case "fr":
 | 
						|
                this.type = "grid";
 | 
						|
                break;
 | 
						|
 | 
						|
            case "deg":
 | 
						|
            case "rad":
 | 
						|
            case "grad":
 | 
						|
            case "turn":
 | 
						|
                this.type = "angle";
 | 
						|
                break;
 | 
						|
 | 
						|
            case "ms":
 | 
						|
            case "s":
 | 
						|
                this.type = "time";
 | 
						|
                break;
 | 
						|
 | 
						|
            case "hz":
 | 
						|
            case "khz":
 | 
						|
                this.type = "frequency";
 | 
						|
                break;
 | 
						|
 | 
						|
            case "dpi":
 | 
						|
            case "dpcm":
 | 
						|
                this.type = "resolution";
 | 
						|
                break;
 | 
						|
 | 
						|
            //default
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
    } else if (/^([+\-]?[\d\.]+)%$/i.test(text)) {  //percentage
 | 
						|
        this.type = "percentage";
 | 
						|
        this.value = +RegExp.$1;
 | 
						|
    } else if (/^([+\-]?\d+)$/i.test(text)) {  //integer
 | 
						|
        this.type = "integer";
 | 
						|
        this.value = +RegExp.$1;
 | 
						|
    } else if (/^([+\-]?[\d\.]+)$/i.test(text)) {  //number
 | 
						|
        this.type = "number";
 | 
						|
        this.value = +RegExp.$1;
 | 
						|
 | 
						|
    } else if (/^#([a-f0-9]{3,6})/i.test(text)) {  //hexcolor
 | 
						|
        this.type = "color";
 | 
						|
        temp = RegExp.$1;
 | 
						|
        if (temp.length === 3) {
 | 
						|
            this.red    = parseInt(temp.charAt(0)+temp.charAt(0), 16);
 | 
						|
            this.green  = parseInt(temp.charAt(1)+temp.charAt(1), 16);
 | 
						|
            this.blue   = parseInt(temp.charAt(2)+temp.charAt(2), 16);
 | 
						|
        } else {
 | 
						|
            this.red    = parseInt(temp.substring(0, 2), 16);
 | 
						|
            this.green  = parseInt(temp.substring(2, 4), 16);
 | 
						|
            this.blue   = parseInt(temp.substring(4, 6), 16);
 | 
						|
        }
 | 
						|
    } else if (/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i.test(text)) { //rgb() color with absolute numbers
 | 
						|
        this.type   = "color";
 | 
						|
        this.red    = +RegExp.$1;
 | 
						|
        this.green  = +RegExp.$2;
 | 
						|
        this.blue   = +RegExp.$3;
 | 
						|
    } else if (/^rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)) { //rgb() color with percentages
 | 
						|
        this.type   = "color";
 | 
						|
        this.red    = +RegExp.$1 * 255 / 100;
 | 
						|
        this.green  = +RegExp.$2 * 255 / 100;
 | 
						|
        this.blue   = +RegExp.$3 * 255 / 100;
 | 
						|
    } else if (/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //rgba() color with absolute numbers
 | 
						|
        this.type   = "color";
 | 
						|
        this.red    = +RegExp.$1;
 | 
						|
        this.green  = +RegExp.$2;
 | 
						|
        this.blue   = +RegExp.$3;
 | 
						|
        this.alpha  = +RegExp.$4;
 | 
						|
    } else if (/^rgba\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //rgba() color with percentages
 | 
						|
        this.type   = "color";
 | 
						|
        this.red    = +RegExp.$1 * 255 / 100;
 | 
						|
        this.green  = +RegExp.$2 * 255 / 100;
 | 
						|
        this.blue   = +RegExp.$3 * 255 / 100;
 | 
						|
        this.alpha  = +RegExp.$4;
 | 
						|
    } else if (/^hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)) { //hsl()
 | 
						|
        this.type   = "color";
 | 
						|
        this.hue    = +RegExp.$1;
 | 
						|
        this.saturation = +RegExp.$2 / 100;
 | 
						|
        this.lightness  = +RegExp.$3 / 100;
 | 
						|
    } else if (/^hsla\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //hsla() color with percentages
 | 
						|
        this.type   = "color";
 | 
						|
        this.hue    = +RegExp.$1;
 | 
						|
        this.saturation = +RegExp.$2 / 100;
 | 
						|
        this.lightness  = +RegExp.$3 / 100;
 | 
						|
        this.alpha  = +RegExp.$4;
 | 
						|
    } else if (/^url\(("([^\\"]|\\.)*")\)/i.test(text)) { //URI
 | 
						|
        // generated by TokenStream.readURI, so always double-quoted.
 | 
						|
        this.type   = "uri";
 | 
						|
        this.uri    = PropertyValuePart.parseString(RegExp.$1);
 | 
						|
    } else if (/^([^\(]+)\(/i.test(text)) {
 | 
						|
        this.type   = "function";
 | 
						|
        this.name   = RegExp.$1;
 | 
						|
        this.value  = text;
 | 
						|
    } 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
 | 
						|
        this.type   = "string";
 | 
						|
        this.value  = PropertyValuePart.parseString(text);
 | 
						|
    } 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
 | 
						|
        this.type   = "string";
 | 
						|
        this.value  = PropertyValuePart.parseString(text);
 | 
						|
    } else if (Colors[text.toLowerCase()]) {  //named color
 | 
						|
        this.type   = "color";
 | 
						|
        temp        = Colors[text.toLowerCase()].substring(1);
 | 
						|
        this.red    = parseInt(temp.substring(0, 2), 16);
 | 
						|
        this.green  = parseInt(temp.substring(2, 4), 16);
 | 
						|
        this.blue   = parseInt(temp.substring(4, 6), 16);
 | 
						|
    } else if (/^[,\/]$/.test(text)) {
 | 
						|
        this.type   = "operator";
 | 
						|
        this.value  = text;
 | 
						|
    } else if (/^-?[a-z_\u00A0-\uFFFF][a-z0-9\-_\u00A0-\uFFFF]*$/i.test(text)) {
 | 
						|
        this.type   = "identifier";
 | 
						|
        this.value  = text;
 | 
						|
    }
 | 
						|
 | 
						|
    // There can be ambiguity with escape sequences in identifiers, as
 | 
						|
    // well as with "color" parts which are also "identifiers", so record
 | 
						|
    // an explicit hint when the token generating this PropertyValuePart
 | 
						|
    // was an identifier.
 | 
						|
    this.wasIdent = Boolean(hint.ident);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
PropertyValuePart.prototype = new SyntaxUnit();
 | 
						|
PropertyValuePart.prototype.constructor = PropertyValuePart;
 | 
						|
 | 
						|
/**
 | 
						|
 * Helper method to parse a CSS string.
 | 
						|
 */
 | 
						|
PropertyValuePart.parseString = function(str) {
 | 
						|
    str = str.slice(1, -1); // Strip surrounding single/double quotes
 | 
						|
    var replacer = function(match, esc) {
 | 
						|
        if (/^(\n|\r\n|\r|\f)$/.test(esc)) {
 | 
						|
            return "";
 | 
						|
        }
 | 
						|
        var m = /^[0-9a-f]{1,6}/i.exec(esc);
 | 
						|
        if (m) {
 | 
						|
            var codePoint = parseInt(m[0], 16);
 | 
						|
            if (String.fromCodePoint) {
 | 
						|
                return String.fromCodePoint(codePoint);
 | 
						|
            } else {
 | 
						|
                // XXX No support for surrogates on old JavaScript engines.
 | 
						|
                return String.fromCharCode(codePoint);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return esc;
 | 
						|
    };
 | 
						|
    return str.replace(/\\(\r\n|[^\r0-9a-f]|[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)/ig,
 | 
						|
                       replacer);
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Helper method to serialize a CSS string.
 | 
						|
 */
 | 
						|
PropertyValuePart.serializeString = function(value) {
 | 
						|
    var replacer = function(match, c) {
 | 
						|
        if (c === "\"") {
 | 
						|
            return "\\" + c;
 | 
						|
        }
 | 
						|
        var cp = String.codePointAt ? String.codePointAt(0) :
 | 
						|
            // We only escape non-surrogate chars, so using charCodeAt
 | 
						|
            // is harmless here.
 | 
						|
            String.charCodeAt(0);
 | 
						|
        return "\\" + cp.toString(16) + " ";
 | 
						|
    };
 | 
						|
    return "\"" + value.replace(/["\r\n\f]/g, replacer) + "\"";
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Create a new syntax unit based solely on the given token.
 | 
						|
 * Convenience method for creating a new syntax unit when
 | 
						|
 * it represents a single token instead of multiple.
 | 
						|
 * @param {Object} token The token object to represent.
 | 
						|
 * @return {parserlib.css.PropertyValuePart} The object representing the token.
 | 
						|
 * @static
 | 
						|
 * @method fromToken
 | 
						|
 */
 | 
						|
PropertyValuePart.fromToken = function(token) {
 | 
						|
    var part = new PropertyValuePart(token.value, token.startLine, token.startCol, {
 | 
						|
        // Tokens can have escaped characters that would fool the type
 | 
						|
        // identification in the PropertyValuePart constructor, so pass
 | 
						|
        // in a hint if this was an identifier.
 | 
						|
        ident: token.type === Tokens.IDENT
 | 
						|
    });
 | 
						|
    return part;
 | 
						|
};
 | 
						|
 | 
						|
},{"../util/SyntaxUnit":26,"./Colors":1,"./Parser":6,"./Tokens":18}],12:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
var Pseudos = module.exports = {
 | 
						|
    __proto__:       null,
 | 
						|
    ":first-letter": 1,
 | 
						|
    ":first-line":   1,
 | 
						|
    ":before":       1,
 | 
						|
    ":after":        1
 | 
						|
};
 | 
						|
 | 
						|
Pseudos.ELEMENT = 1;
 | 
						|
Pseudos.CLASS = 2;
 | 
						|
 | 
						|
Pseudos.isElement = function(pseudo) {
 | 
						|
    return pseudo.indexOf("::") === 0 || Pseudos[pseudo.toLowerCase()] === Pseudos.ELEMENT;
 | 
						|
};
 | 
						|
 | 
						|
},{}],13:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = Selector;
 | 
						|
 | 
						|
var SyntaxUnit = require("../util/SyntaxUnit");
 | 
						|
 | 
						|
var Parser = require("./Parser");
 | 
						|
var Specificity = require("./Specificity");
 | 
						|
 | 
						|
/**
 | 
						|
 * Represents an entire single selector, including all parts but not
 | 
						|
 * including multiple selectors (those separated by commas).
 | 
						|
 * @namespace parserlib.css
 | 
						|
 * @class Selector
 | 
						|
 * @extends parserlib.util.SyntaxUnit
 | 
						|
 * @constructor
 | 
						|
 * @param {Array} parts Array of selectors parts making up this selector.
 | 
						|
 * @param {int} line The line of text on which the unit resides.
 | 
						|
 * @param {int} col The column of text on which the unit resides.
 | 
						|
 */
 | 
						|
function Selector(parts, line, col) {
 | 
						|
 | 
						|
    SyntaxUnit.call(this, parts.join(" "), line, col, Parser.SELECTOR_TYPE);
 | 
						|
 | 
						|
    /**
 | 
						|
     * The parts that make up the selector.
 | 
						|
     * @type Array
 | 
						|
     * @property parts
 | 
						|
     */
 | 
						|
    this.parts = parts;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The specificity of the selector.
 | 
						|
     * @type parserlib.css.Specificity
 | 
						|
     * @property specificity
 | 
						|
     */
 | 
						|
    this.specificity = Specificity.calculate(this);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
Selector.prototype = new SyntaxUnit();
 | 
						|
Selector.prototype.constructor = Selector;
 | 
						|
 | 
						|
 | 
						|
},{"../util/SyntaxUnit":26,"./Parser":6,"./Specificity":16}],14:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = SelectorPart;
 | 
						|
 | 
						|
var SyntaxUnit = require("../util/SyntaxUnit");
 | 
						|
 | 
						|
var Parser = require("./Parser");
 | 
						|
 | 
						|
/**
 | 
						|
 * Represents a single part of a selector string, meaning a single set of
 | 
						|
 * element name and modifiers. This does not include combinators such as
 | 
						|
 * spaces, +, >, etc.
 | 
						|
 * @namespace parserlib.css
 | 
						|
 * @class SelectorPart
 | 
						|
 * @extends parserlib.util.SyntaxUnit
 | 
						|
 * @constructor
 | 
						|
 * @param {String} elementName The element name in the selector or null
 | 
						|
 *      if there is no element name.
 | 
						|
 * @param {Array} modifiers Array of individual modifiers for the element.
 | 
						|
 *      May be empty if there are none.
 | 
						|
 * @param {String} text The text representation of the unit.
 | 
						|
 * @param {int} line The line of text on which the unit resides.
 | 
						|
 * @param {int} col The column of text on which the unit resides.
 | 
						|
 */
 | 
						|
function SelectorPart(elementName, modifiers, text, line, col) {
 | 
						|
 | 
						|
    SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_PART_TYPE);
 | 
						|
 | 
						|
    /**
 | 
						|
     * The tag name of the element to which this part
 | 
						|
     * of the selector affects.
 | 
						|
     * @type String
 | 
						|
     * @property elementName
 | 
						|
     */
 | 
						|
    this.elementName = elementName;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The parts that come after the element name, such as class names, IDs,
 | 
						|
     * pseudo classes/elements, etc.
 | 
						|
     * @type Array
 | 
						|
     * @property modifiers
 | 
						|
     */
 | 
						|
    this.modifiers = modifiers;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
SelectorPart.prototype = new SyntaxUnit();
 | 
						|
SelectorPart.prototype.constructor = SelectorPart;
 | 
						|
 | 
						|
 | 
						|
},{"../util/SyntaxUnit":26,"./Parser":6}],15:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = SelectorSubPart;
 | 
						|
 | 
						|
var SyntaxUnit = require("../util/SyntaxUnit");
 | 
						|
 | 
						|
var Parser = require("./Parser");
 | 
						|
 | 
						|
/**
 | 
						|
 * Represents a selector modifier string, meaning a class name, element name,
 | 
						|
 * element ID, pseudo rule, etc.
 | 
						|
 * @namespace parserlib.css
 | 
						|
 * @class SelectorSubPart
 | 
						|
 * @extends parserlib.util.SyntaxUnit
 | 
						|
 * @constructor
 | 
						|
 * @param {String} text The text representation of the unit.
 | 
						|
 * @param {String} type The type of selector modifier.
 | 
						|
 * @param {int} line The line of text on which the unit resides.
 | 
						|
 * @param {int} col The column of text on which the unit resides.
 | 
						|
 */
 | 
						|
function SelectorSubPart(text, type, line, col) {
 | 
						|
 | 
						|
    SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_SUB_PART_TYPE);
 | 
						|
 | 
						|
    /**
 | 
						|
     * The type of modifier.
 | 
						|
     * @type String
 | 
						|
     * @property type
 | 
						|
     */
 | 
						|
    this.type = type;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Some subparts have arguments, this represents them.
 | 
						|
     * @type Array
 | 
						|
     * @property args
 | 
						|
     */
 | 
						|
    this.args = [];
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
SelectorSubPart.prototype = new SyntaxUnit();
 | 
						|
SelectorSubPart.prototype.constructor = SelectorSubPart;
 | 
						|
 | 
						|
 | 
						|
},{"../util/SyntaxUnit":26,"./Parser":6}],16:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = Specificity;
 | 
						|
 | 
						|
var Pseudos = require("./Pseudos");
 | 
						|
var SelectorPart = require("./SelectorPart");
 | 
						|
 | 
						|
/**
 | 
						|
 * Represents a selector's specificity.
 | 
						|
 * @namespace parserlib.css
 | 
						|
 * @class Specificity
 | 
						|
 * @constructor
 | 
						|
 * @param {int} a Should be 1 for inline styles, zero for stylesheet styles
 | 
						|
 * @param {int} b Number of ID selectors
 | 
						|
 * @param {int} c Number of classes and pseudo classes
 | 
						|
 * @param {int} d Number of element names and pseudo elements
 | 
						|
 */
 | 
						|
function Specificity(a, b, c, d) {
 | 
						|
    this.a = a;
 | 
						|
    this.b = b;
 | 
						|
    this.c = c;
 | 
						|
    this.d = d;
 | 
						|
}
 | 
						|
 | 
						|
Specificity.prototype = {
 | 
						|
    constructor: Specificity,
 | 
						|
 | 
						|
    /**
 | 
						|
     * Compare this specificity to another.
 | 
						|
     * @param {Specificity} other The other specificity to compare to.
 | 
						|
     * @return {int} -1 if the other specificity is larger, 1 if smaller, 0 if equal.
 | 
						|
     * @method compare
 | 
						|
     */
 | 
						|
    compare: function(other) {
 | 
						|
        var comps = ["a", "b", "c", "d"],
 | 
						|
            i, len;
 | 
						|
 | 
						|
        for (i=0, len=comps.length; i < len; i++) {
 | 
						|
            if (this[comps[i]] < other[comps[i]]) {
 | 
						|
                return -1;
 | 
						|
            } else if (this[comps[i]] > other[comps[i]]) {
 | 
						|
                return 1;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return 0;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Creates a numeric value for the specificity.
 | 
						|
     * @return {int} The numeric value for the specificity.
 | 
						|
     * @method valueOf
 | 
						|
     */
 | 
						|
    valueOf: function() {
 | 
						|
        return (this.a * 1000) + (this.b * 100) + (this.c * 10) + this.d;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns a string representation for specificity.
 | 
						|
     * @return {String} The string representation of specificity.
 | 
						|
     * @method toString
 | 
						|
     */
 | 
						|
    toString: function() {
 | 
						|
        return this.a + "," + this.b + "," + this.c + "," + this.d;
 | 
						|
    }
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * Calculates the specificity of the given selector.
 | 
						|
 * @param {parserlib.css.Selector} The selector to calculate specificity for.
 | 
						|
 * @return {parserlib.css.Specificity} The specificity of the selector.
 | 
						|
 * @static
 | 
						|
 * @method calculate
 | 
						|
 */
 | 
						|
Specificity.calculate = function(selector) {
 | 
						|
 | 
						|
    var i, len,
 | 
						|
        part,
 | 
						|
        b=0, c=0, d=0;
 | 
						|
 | 
						|
    function updateValues(part) {
 | 
						|
 | 
						|
        var i, j, len, num,
 | 
						|
            elementName = part.elementName ? part.elementName.text : "",
 | 
						|
            modifier;
 | 
						|
 | 
						|
        if (elementName && elementName.charAt(elementName.length-1) !== "*") {
 | 
						|
            d++;
 | 
						|
        }
 | 
						|
 | 
						|
        for (i=0, len=part.modifiers.length; i < len; i++) {
 | 
						|
            modifier = part.modifiers[i];
 | 
						|
            switch (modifier.type) {
 | 
						|
                case "class":
 | 
						|
                case "attribute":
 | 
						|
                    c++;
 | 
						|
                    break;
 | 
						|
 | 
						|
                case "id":
 | 
						|
                    b++;
 | 
						|
                    break;
 | 
						|
 | 
						|
                case "pseudo":
 | 
						|
                    if (Pseudos.isElement(modifier.text)) {
 | 
						|
                        d++;
 | 
						|
                    } else {
 | 
						|
                        c++;
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
 | 
						|
                case "not":
 | 
						|
                    for (j=0, num=modifier.args.length; j < num; j++) {
 | 
						|
                        updateValues(modifier.args[j]);
 | 
						|
                    }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    for (i=0, len=selector.parts.length; i < len; i++) {
 | 
						|
        part = selector.parts[i];
 | 
						|
 | 
						|
        if (part instanceof SelectorPart) {
 | 
						|
            updateValues(part);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return new Specificity(0, b, c, d);
 | 
						|
};
 | 
						|
 | 
						|
},{"./Pseudos":12,"./SelectorPart":14}],17:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = TokenStream;
 | 
						|
 | 
						|
var TokenStreamBase = require("../util/TokenStreamBase");
 | 
						|
 | 
						|
var PropertyValuePart = require("./PropertyValuePart");
 | 
						|
var Tokens = require("./Tokens");
 | 
						|
 | 
						|
var h = /^[0-9a-fA-F]$/,
 | 
						|
    nonascii = /^[\u00A0-\uFFFF]$/,
 | 
						|
    nl = /\n|\r\n|\r|\f/,
 | 
						|
    whitespace = /\u0009|\u000a|\u000c|\u000d|\u0020/;
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// Helper functions
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
 | 
						|
function isHexDigit(c) {
 | 
						|
    return c !== null && h.test(c);
 | 
						|
}
 | 
						|
 | 
						|
function isDigit(c) {
 | 
						|
    return c !== null && /\d/.test(c);
 | 
						|
}
 | 
						|
 | 
						|
function isWhitespace(c) {
 | 
						|
    return c !== null && whitespace.test(c);
 | 
						|
}
 | 
						|
 | 
						|
function isNewLine(c) {
 | 
						|
    return c !== null && nl.test(c);
 | 
						|
}
 | 
						|
 | 
						|
function isNameStart(c) {
 | 
						|
    return c !== null && /[a-z_\u00A0-\uFFFF\\]/i.test(c);
 | 
						|
}
 | 
						|
 | 
						|
function isNameChar(c) {
 | 
						|
    return c !== null && (isNameStart(c) || /[0-9\-\\]/.test(c));
 | 
						|
}
 | 
						|
 | 
						|
function isIdentStart(c) {
 | 
						|
    return c !== null && (isNameStart(c) || /\-\\/.test(c));
 | 
						|
}
 | 
						|
 | 
						|
function mix(receiver, supplier) {
 | 
						|
    for (var prop in supplier) {
 | 
						|
        if (Object.prototype.hasOwnProperty.call(supplier, prop)) {
 | 
						|
            receiver[prop] = supplier[prop];
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return receiver;
 | 
						|
}
 | 
						|
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
// CSS Token Stream
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * A token stream that produces CSS tokens.
 | 
						|
 * @param {String|Reader} input The source of text to tokenize.
 | 
						|
 * @constructor
 | 
						|
 * @class TokenStream
 | 
						|
 * @namespace parserlib.css
 | 
						|
 */
 | 
						|
function TokenStream(input) {
 | 
						|
    TokenStreamBase.call(this, input, Tokens);
 | 
						|
}
 | 
						|
 | 
						|
TokenStream.prototype = mix(new TokenStreamBase(), {
 | 
						|
 | 
						|
    /**
 | 
						|
     * Overrides the TokenStreamBase method of the same name
 | 
						|
     * to produce CSS tokens.
 | 
						|
     * @return {Object} A token object representing the next token.
 | 
						|
     * @method _getToken
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    _getToken: function() {
 | 
						|
 | 
						|
        var c,
 | 
						|
            reader = this._reader,
 | 
						|
            token   = null,
 | 
						|
            startLine   = reader.getLine(),
 | 
						|
            startCol    = reader.getCol();
 | 
						|
 | 
						|
        c = reader.read();
 | 
						|
 | 
						|
 | 
						|
        while (c) {
 | 
						|
            switch (c) {
 | 
						|
 | 
						|
                /*
 | 
						|
                 * Potential tokens:
 | 
						|
                 * - COMMENT
 | 
						|
                 * - SLASH
 | 
						|
                 * - CHAR
 | 
						|
                 */
 | 
						|
                case "/":
 | 
						|
 | 
						|
                    if (reader.peek() === "*") {
 | 
						|
                        token = this.commentToken(c, startLine, startCol);
 | 
						|
                    } else {
 | 
						|
                        token = this.charToken(c, startLine, startCol);
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
 | 
						|
                /*
 | 
						|
                 * Potential tokens:
 | 
						|
                 * - DASHMATCH
 | 
						|
                 * - INCLUDES
 | 
						|
                 * - PREFIXMATCH
 | 
						|
                 * - SUFFIXMATCH
 | 
						|
                 * - SUBSTRINGMATCH
 | 
						|
                 * - CHAR
 | 
						|
                 */
 | 
						|
                case "|":
 | 
						|
                case "~":
 | 
						|
                case "^":
 | 
						|
                case "$":
 | 
						|
                case "*":
 | 
						|
                    if (reader.peek() === "=") {
 | 
						|
                        token = this.comparisonToken(c, startLine, startCol);
 | 
						|
                    } else {
 | 
						|
                        token = this.charToken(c, startLine, startCol);
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
 | 
						|
                /*
 | 
						|
                 * Potential tokens:
 | 
						|
                 * - STRING
 | 
						|
                 * - INVALID
 | 
						|
                 */
 | 
						|
                case "\"":
 | 
						|
                case "'":
 | 
						|
                    token = this.stringToken(c, startLine, startCol);
 | 
						|
                    break;
 | 
						|
 | 
						|
                /*
 | 
						|
                 * Potential tokens:
 | 
						|
                 * - HASH
 | 
						|
                 * - CHAR
 | 
						|
                 */
 | 
						|
                case "#":
 | 
						|
                    if (isNameChar(reader.peek())) {
 | 
						|
                        token = this.hashToken(c, startLine, startCol);
 | 
						|
                    } else {
 | 
						|
                        token = this.charToken(c, startLine, startCol);
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
 | 
						|
                /*
 | 
						|
                 * Potential tokens:
 | 
						|
                 * - DOT
 | 
						|
                 * - NUMBER
 | 
						|
                 * - DIMENSION
 | 
						|
                 * - PERCENTAGE
 | 
						|
                 */
 | 
						|
                case ".":
 | 
						|
                    if (isDigit(reader.peek())) {
 | 
						|
                        token = this.numberToken(c, startLine, startCol);
 | 
						|
                    } else {
 | 
						|
                        token = this.charToken(c, startLine, startCol);
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
 | 
						|
                /*
 | 
						|
                 * Potential tokens:
 | 
						|
                 * - CDC
 | 
						|
                 * - MINUS
 | 
						|
                 * - NUMBER
 | 
						|
                 * - DIMENSION
 | 
						|
                 * - PERCENTAGE
 | 
						|
                 */
 | 
						|
                case "-":
 | 
						|
                    if (reader.peek() === "-") {  //could be closing HTML-style comment
 | 
						|
                        token = this.htmlCommentEndToken(c, startLine, startCol);
 | 
						|
                    } else if (isNameStart(reader.peek())) {
 | 
						|
                        token = this.identOrFunctionToken(c, startLine, startCol);
 | 
						|
                    } else {
 | 
						|
                        token = this.charToken(c, startLine, startCol);
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
 | 
						|
                /*
 | 
						|
                 * Potential tokens:
 | 
						|
                 * - IMPORTANT_SYM
 | 
						|
                 * - CHAR
 | 
						|
                 */
 | 
						|
                case "!":
 | 
						|
                    token = this.importantToken(c, startLine, startCol);
 | 
						|
                    break;
 | 
						|
 | 
						|
                /*
 | 
						|
                 * Any at-keyword or CHAR
 | 
						|
                 */
 | 
						|
                case "@":
 | 
						|
                    token = this.atRuleToken(c, startLine, startCol);
 | 
						|
                    break;
 | 
						|
 | 
						|
                /*
 | 
						|
                 * Potential tokens:
 | 
						|
                 * - NOT
 | 
						|
                 * - CHAR
 | 
						|
                 */
 | 
						|
                case ":":
 | 
						|
                    token = this.notToken(c, startLine, startCol);
 | 
						|
                    break;
 | 
						|
 | 
						|
                /*
 | 
						|
                 * Potential tokens:
 | 
						|
                 * - CDO
 | 
						|
                 * - CHAR
 | 
						|
                 */
 | 
						|
                case "<":
 | 
						|
                    token = this.htmlCommentStartToken(c, startLine, startCol);
 | 
						|
                    break;
 | 
						|
 | 
						|
                /*
 | 
						|
                 * Potential tokens:
 | 
						|
                 * - IDENT
 | 
						|
                 * - CHAR
 | 
						|
                 */
 | 
						|
                case "\\":
 | 
						|
                    if (/[^\r\n\f]/.test(reader.peek())) {
 | 
						|
                        token = this.identOrFunctionToken(this.readEscape(c, true), startLine, startCol);
 | 
						|
                    } else {
 | 
						|
                        token = this.charToken(c, startLine, startCol);
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
 | 
						|
                /*
 | 
						|
                 * Potential tokens:
 | 
						|
                 * - UNICODE_RANGE
 | 
						|
                 * - URL
 | 
						|
                 * - CHAR
 | 
						|
                 */
 | 
						|
                case "U":
 | 
						|
                case "u":
 | 
						|
                    if (reader.peek() === "+") {
 | 
						|
                        token = this.unicodeRangeToken(c, startLine, startCol);
 | 
						|
                        break;
 | 
						|
                    }
 | 
						|
                    /* falls through */
 | 
						|
                default:
 | 
						|
 | 
						|
                    /*
 | 
						|
                     * Potential tokens:
 | 
						|
                     * - NUMBER
 | 
						|
                     * - DIMENSION
 | 
						|
                     * - LENGTH
 | 
						|
                     * - FREQ
 | 
						|
                     * - TIME
 | 
						|
                     * - EMS
 | 
						|
                     * - EXS
 | 
						|
                     * - ANGLE
 | 
						|
                     */
 | 
						|
                    if (isDigit(c)) {
 | 
						|
                        token = this.numberToken(c, startLine, startCol);
 | 
						|
                    } else
 | 
						|
 | 
						|
                    /*
 | 
						|
                     * Potential tokens:
 | 
						|
                     * - S
 | 
						|
                     */
 | 
						|
                    if (isWhitespace(c)) {
 | 
						|
                        token = this.whitespaceToken(c, startLine, startCol);
 | 
						|
                    } else
 | 
						|
 | 
						|
                    /*
 | 
						|
                     * Potential tokens:
 | 
						|
                     * - IDENT
 | 
						|
                     */
 | 
						|
                    if (isIdentStart(c)) {
 | 
						|
                        token = this.identOrFunctionToken(c, startLine, startCol);
 | 
						|
                    } else {
 | 
						|
                       /*
 | 
						|
                        * Potential tokens:
 | 
						|
                        * - CHAR
 | 
						|
                        * - PLUS
 | 
						|
                        */
 | 
						|
                        token = this.charToken(c, startLine, startCol);
 | 
						|
                    }
 | 
						|
 | 
						|
            }
 | 
						|
 | 
						|
            //make sure this token is wanted
 | 
						|
            //TODO: check channel
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        if (!token && c === null) {
 | 
						|
            token = this.createToken(Tokens.EOF, null, startLine, startCol);
 | 
						|
        }
 | 
						|
 | 
						|
        return token;
 | 
						|
    },
 | 
						|
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
    // Methods to create tokens
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
 | 
						|
    /**
 | 
						|
     * Produces a token based on available data and the current
 | 
						|
     * reader position information. This method is called by other
 | 
						|
     * private methods to create tokens and is never called directly.
 | 
						|
     * @param {int} tt The token type.
 | 
						|
     * @param {String} value The text value of the token.
 | 
						|
     * @param {int} startLine The beginning line for the character.
 | 
						|
     * @param {int} startCol The beginning column for the character.
 | 
						|
     * @param {Object} options (Optional) Specifies a channel property
 | 
						|
     *      to indicate that a different channel should be scanned
 | 
						|
     *      and/or a hide property indicating that the token should
 | 
						|
     *      be hidden.
 | 
						|
     * @return {Object} A token object.
 | 
						|
     * @method createToken
 | 
						|
     */
 | 
						|
    createToken: function(tt, value, startLine, startCol, options) {
 | 
						|
        var reader = this._reader;
 | 
						|
        options = options || {};
 | 
						|
 | 
						|
        return {
 | 
						|
            value:      value,
 | 
						|
            type:       tt,
 | 
						|
            channel:    options.channel,
 | 
						|
            endChar:    options.endChar,
 | 
						|
            hide:       options.hide || false,
 | 
						|
            startLine:  startLine,
 | 
						|
            startCol:   startCol,
 | 
						|
            endLine:    reader.getLine(),
 | 
						|
            endCol:     reader.getCol()
 | 
						|
        };
 | 
						|
    },
 | 
						|
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
    // Methods to create specific tokens
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
 | 
						|
    /**
 | 
						|
     * Produces a token for any at-rule. If the at-rule is unknown, then
 | 
						|
     * the token is for a single "@" character.
 | 
						|
     * @param {String} first The first character for the token.
 | 
						|
     * @param {int} startLine The beginning line for the character.
 | 
						|
     * @param {int} startCol The beginning column for the character.
 | 
						|
     * @return {Object} A token object.
 | 
						|
     * @method atRuleToken
 | 
						|
     */
 | 
						|
    atRuleToken: function(first, startLine, startCol) {
 | 
						|
        var rule    = first,
 | 
						|
            reader  = this._reader,
 | 
						|
            tt      = Tokens.CHAR,
 | 
						|
            ident;
 | 
						|
 | 
						|
        /*
 | 
						|
         * First, mark where we are. There are only four @ rules,
 | 
						|
         * so anything else is really just an invalid token.
 | 
						|
         * Basically, if this doesn't match one of the known @
 | 
						|
         * rules, just return '@' as an unknown token and allow
 | 
						|
         * parsing to continue after that point.
 | 
						|
         */
 | 
						|
        reader.mark();
 | 
						|
 | 
						|
        //try to find the at-keyword
 | 
						|
        ident = this.readName();
 | 
						|
        rule = first + ident;
 | 
						|
        tt = Tokens.type(rule.toLowerCase());
 | 
						|
 | 
						|
        //if it's not valid, use the first character only and reset the reader
 | 
						|
        if (tt === Tokens.CHAR || tt === Tokens.UNKNOWN) {
 | 
						|
            if (rule.length > 1) {
 | 
						|
                tt = Tokens.UNKNOWN_SYM;
 | 
						|
            } else {
 | 
						|
                tt = Tokens.CHAR;
 | 
						|
                rule = first;
 | 
						|
                reader.reset();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return this.createToken(tt, rule, startLine, startCol);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Produces a character token based on the given character
 | 
						|
     * and location in the stream. If there's a special (non-standard)
 | 
						|
     * token name, this is used; otherwise CHAR is used.
 | 
						|
     * @param {String} c The character for the token.
 | 
						|
     * @param {int} startLine The beginning line for the character.
 | 
						|
     * @param {int} startCol The beginning column for the character.
 | 
						|
     * @return {Object} A token object.
 | 
						|
     * @method charToken
 | 
						|
     */
 | 
						|
    charToken: function(c, startLine, startCol) {
 | 
						|
        var tt = Tokens.type(c);
 | 
						|
        var opts = {};
 | 
						|
 | 
						|
        if (tt === -1) {
 | 
						|
            tt = Tokens.CHAR;
 | 
						|
        } else {
 | 
						|
            opts.endChar = Tokens[tt].endChar;
 | 
						|
        }
 | 
						|
 | 
						|
        return this.createToken(tt, c, startLine, startCol, opts);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Produces a character token based on the given character
 | 
						|
     * and location in the stream. If there's a special (non-standard)
 | 
						|
     * token name, this is used; otherwise CHAR is used.
 | 
						|
     * @param {String} first The first character for the token.
 | 
						|
     * @param {int} startLine The beginning line for the character.
 | 
						|
     * @param {int} startCol The beginning column for the character.
 | 
						|
     * @return {Object} A token object.
 | 
						|
     * @method commentToken
 | 
						|
     */
 | 
						|
    commentToken: function(first, startLine, startCol) {
 | 
						|
        var comment = this.readComment(first);
 | 
						|
 | 
						|
        return this.createToken(Tokens.COMMENT, comment, startLine, startCol);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Produces a comparison token based on the given character
 | 
						|
     * and location in the stream. The next character must be
 | 
						|
     * read and is already known to be an equals sign.
 | 
						|
     * @param {String} c The character for the token.
 | 
						|
     * @param {int} startLine The beginning line for the character.
 | 
						|
     * @param {int} startCol The beginning column for the character.
 | 
						|
     * @return {Object} A token object.
 | 
						|
     * @method comparisonToken
 | 
						|
     */
 | 
						|
    comparisonToken: function(c, startLine, startCol) {
 | 
						|
        var reader  = this._reader,
 | 
						|
            comparison  = c + reader.read(),
 | 
						|
            tt      = Tokens.type(comparison) || Tokens.CHAR;
 | 
						|
 | 
						|
        return this.createToken(tt, comparison, startLine, startCol);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Produces a hash token based on the specified information. The
 | 
						|
     * first character provided is the pound sign (#) and then this
 | 
						|
     * method reads a name afterward.
 | 
						|
     * @param {String} first The first character (#) in the hash name.
 | 
						|
     * @param {int} startLine The beginning line for the character.
 | 
						|
     * @param {int} startCol The beginning column for the character.
 | 
						|
     * @return {Object} A token object.
 | 
						|
     * @method hashToken
 | 
						|
     */
 | 
						|
    hashToken: function(first, startLine, startCol) {
 | 
						|
        var name    = this.readName(first);
 | 
						|
 | 
						|
        return this.createToken(Tokens.HASH, name, startLine, startCol);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Produces a CDO or CHAR token based on the specified information. The
 | 
						|
     * first character is provided and the rest is read by the function to determine
 | 
						|
     * the correct token to create.
 | 
						|
     * @param {String} first The first character in the token.
 | 
						|
     * @param {int} startLine The beginning line for the character.
 | 
						|
     * @param {int} startCol The beginning column for the character.
 | 
						|
     * @return {Object} A token object.
 | 
						|
     * @method htmlCommentStartToken
 | 
						|
     */
 | 
						|
    htmlCommentStartToken: function(first, startLine, startCol) {
 | 
						|
        var reader      = this._reader,
 | 
						|
            text        = first;
 | 
						|
 | 
						|
        reader.mark();
 | 
						|
        text += reader.readCount(3);
 | 
						|
 | 
						|
        if (text === "<!--") {
 | 
						|
            return this.createToken(Tokens.CDO, text, startLine, startCol);
 | 
						|
        } else {
 | 
						|
            reader.reset();
 | 
						|
            return this.charToken(first, startLine, startCol);
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Produces a CDC or CHAR token based on the specified information. The
 | 
						|
     * first character is provided and the rest is read by the function to determine
 | 
						|
     * the correct token to create.
 | 
						|
     * @param {String} first The first character in the token.
 | 
						|
     * @param {int} startLine The beginning line for the character.
 | 
						|
     * @param {int} startCol The beginning column for the character.
 | 
						|
     * @return {Object} A token object.
 | 
						|
     * @method htmlCommentEndToken
 | 
						|
     */
 | 
						|
    htmlCommentEndToken: function(first, startLine, startCol) {
 | 
						|
        var reader      = this._reader,
 | 
						|
            text        = first;
 | 
						|
 | 
						|
        reader.mark();
 | 
						|
        text += reader.readCount(2);
 | 
						|
 | 
						|
        if (text === "-->") {
 | 
						|
            return this.createToken(Tokens.CDC, text, startLine, startCol);
 | 
						|
        } else {
 | 
						|
            reader.reset();
 | 
						|
            return this.charToken(first, startLine, startCol);
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Produces an IDENT or FUNCTION token based on the specified information. The
 | 
						|
     * first character is provided and the rest is read by the function to determine
 | 
						|
     * the correct token to create.
 | 
						|
     * @param {String} first The first character in the identifier.
 | 
						|
     * @param {int} startLine The beginning line for the character.
 | 
						|
     * @param {int} startCol The beginning column for the character.
 | 
						|
     * @return {Object} A token object.
 | 
						|
     * @method identOrFunctionToken
 | 
						|
     */
 | 
						|
    identOrFunctionToken: function(first, startLine, startCol) {
 | 
						|
        var reader  = this._reader,
 | 
						|
            ident   = this.readName(first),
 | 
						|
            tt      = Tokens.IDENT,
 | 
						|
            uriFns  = ["url(", "url-prefix(", "domain("],
 | 
						|
            uri;
 | 
						|
 | 
						|
        //if there's a left paren immediately after, it's a URI or function
 | 
						|
        if (reader.peek() === "(") {
 | 
						|
            ident += reader.read();
 | 
						|
            if (uriFns.indexOf(ident.toLowerCase()) > -1) {
 | 
						|
                reader.mark();
 | 
						|
                uri = this.readURI(ident);
 | 
						|
                if (uri === null) {
 | 
						|
                    //didn't find a valid URL or there's no closing paren
 | 
						|
                    reader.reset();
 | 
						|
                    tt = Tokens.FUNCTION;
 | 
						|
                } else {
 | 
						|
                    tt = Tokens.URI;
 | 
						|
                    ident = uri;
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                tt = Tokens.FUNCTION;
 | 
						|
            }
 | 
						|
        } else if (reader.peek() === ":") {  //might be an IE function
 | 
						|
 | 
						|
            //IE-specific functions always being with progid:
 | 
						|
            if (ident.toLowerCase() === "progid") {
 | 
						|
                ident += reader.readTo("(");
 | 
						|
                tt = Tokens.IE_FUNCTION;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return this.createToken(tt, ident, startLine, startCol);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Produces an IMPORTANT_SYM or CHAR token based on the specified information. The
 | 
						|
     * first character is provided and the rest is read by the function to determine
 | 
						|
     * the correct token to create.
 | 
						|
     * @param {String} first The first character in the token.
 | 
						|
     * @param {int} startLine The beginning line for the character.
 | 
						|
     * @param {int} startCol The beginning column for the character.
 | 
						|
     * @return {Object} A token object.
 | 
						|
     * @method importantToken
 | 
						|
     */
 | 
						|
    importantToken: function(first, startLine, startCol) {
 | 
						|
        var reader      = this._reader,
 | 
						|
            important   = first,
 | 
						|
            tt          = Tokens.CHAR,
 | 
						|
            temp,
 | 
						|
            c;
 | 
						|
 | 
						|
        reader.mark();
 | 
						|
        c = reader.read();
 | 
						|
 | 
						|
        while (c) {
 | 
						|
 | 
						|
            //there can be a comment in here
 | 
						|
            if (c === "/") {
 | 
						|
 | 
						|
                //if the next character isn't a star, then this isn't a valid !important token
 | 
						|
                if (reader.peek() !== "*") {
 | 
						|
                    break;
 | 
						|
                } else {
 | 
						|
                    temp = this.readComment(c);
 | 
						|
                    if (temp === "") {    //broken!
 | 
						|
                        break;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            } else if (isWhitespace(c)) {
 | 
						|
                important += c + this.readWhitespace();
 | 
						|
            } else if (/i/i.test(c)) {
 | 
						|
                temp = reader.readCount(8);
 | 
						|
                if (/mportant/i.test(temp)) {
 | 
						|
                    important += c + temp;
 | 
						|
                    tt = Tokens.IMPORTANT_SYM;
 | 
						|
 | 
						|
                }
 | 
						|
                break;  //we're done
 | 
						|
            } else {
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            c = reader.read();
 | 
						|
        }
 | 
						|
 | 
						|
        if (tt === Tokens.CHAR) {
 | 
						|
            reader.reset();
 | 
						|
            return this.charToken(first, startLine, startCol);
 | 
						|
        } else {
 | 
						|
            return this.createToken(tt, important, startLine, startCol);
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Produces a NOT or CHAR token based on the specified information. The
 | 
						|
     * first character is provided and the rest is read by the function to determine
 | 
						|
     * the correct token to create.
 | 
						|
     * @param {String} first The first character in the token.
 | 
						|
     * @param {int} startLine The beginning line for the character.
 | 
						|
     * @param {int} startCol The beginning column for the character.
 | 
						|
     * @return {Object} A token object.
 | 
						|
     * @method notToken
 | 
						|
     */
 | 
						|
    notToken: function(first, startLine, startCol) {
 | 
						|
        var reader      = this._reader,
 | 
						|
            text        = first;
 | 
						|
 | 
						|
        reader.mark();
 | 
						|
        text += reader.readCount(4);
 | 
						|
 | 
						|
        if (text.toLowerCase() === ":not(") {
 | 
						|
            return this.createToken(Tokens.NOT, text, startLine, startCol);
 | 
						|
        } else {
 | 
						|
            reader.reset();
 | 
						|
            return this.charToken(first, startLine, startCol);
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Produces a number token based on the given character
 | 
						|
     * and location in the stream. This may return a token of
 | 
						|
     * NUMBER, EMS, EXS, LENGTH, ANGLE, TIME, FREQ, DIMENSION,
 | 
						|
     * or PERCENTAGE.
 | 
						|
     * @param {String} first The first character for the token.
 | 
						|
     * @param {int} startLine The beginning line for the character.
 | 
						|
     * @param {int} startCol The beginning column for the character.
 | 
						|
     * @return {Object} A token object.
 | 
						|
     * @method numberToken
 | 
						|
     */
 | 
						|
    numberToken: function(first, startLine, startCol) {
 | 
						|
        var reader  = this._reader,
 | 
						|
            value   = this.readNumber(first),
 | 
						|
            ident,
 | 
						|
            tt      = Tokens.NUMBER,
 | 
						|
            c       = reader.peek();
 | 
						|
 | 
						|
        if (isIdentStart(c)) {
 | 
						|
            ident = this.readName(reader.read());
 | 
						|
            value += ident;
 | 
						|
 | 
						|
            if (/^em$|^ex$|^px$|^gd$|^rem$|^vw$|^vh$|^vmax$|^vmin$|^ch$|^cm$|^mm$|^in$|^pt$|^pc$/i.test(ident)) {
 | 
						|
                tt = Tokens.LENGTH;
 | 
						|
            } else if (/^deg|^rad$|^grad$|^turn$/i.test(ident)) {
 | 
						|
                tt = Tokens.ANGLE;
 | 
						|
            } else if (/^ms$|^s$/i.test(ident)) {
 | 
						|
                tt = Tokens.TIME;
 | 
						|
            } else if (/^hz$|^khz$/i.test(ident)) {
 | 
						|
                tt = Tokens.FREQ;
 | 
						|
            } else if (/^dpi$|^dpcm$/i.test(ident)) {
 | 
						|
                tt = Tokens.RESOLUTION;
 | 
						|
            } else {
 | 
						|
                tt = Tokens.DIMENSION;
 | 
						|
            }
 | 
						|
 | 
						|
        } else if (c === "%") {
 | 
						|
            value += reader.read();
 | 
						|
            tt = Tokens.PERCENTAGE;
 | 
						|
        }
 | 
						|
 | 
						|
        return this.createToken(tt, value, startLine, startCol);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Produces a string token based on the given character
 | 
						|
     * and location in the stream. Since strings may be indicated
 | 
						|
     * by single or double quotes, a failure to match starting
 | 
						|
     * and ending quotes results in an INVALID token being generated.
 | 
						|
     * The first character in the string is passed in and then
 | 
						|
     * the rest are read up to and including the final quotation mark.
 | 
						|
     * @param {String} first The first character in the string.
 | 
						|
     * @param {int} startLine The beginning line for the character.
 | 
						|
     * @param {int} startCol The beginning column for the character.
 | 
						|
     * @return {Object} A token object.
 | 
						|
     * @method stringToken
 | 
						|
     */
 | 
						|
    stringToken: function(first, startLine, startCol) {
 | 
						|
        var delim   = first,
 | 
						|
            string  = first,
 | 
						|
            reader  = this._reader,
 | 
						|
            tt      = Tokens.STRING,
 | 
						|
            c       = reader.read(),
 | 
						|
            i;
 | 
						|
 | 
						|
        while (c) {
 | 
						|
            string += c;
 | 
						|
 | 
						|
            if (c === "\\") {
 | 
						|
                c = reader.read();
 | 
						|
                if (c === null) {
 | 
						|
                    break; // premature EOF after backslash
 | 
						|
                } else if (/[^\r\n\f0-9a-f]/i.test(c)) {
 | 
						|
                    // single-character escape
 | 
						|
                    string += c;
 | 
						|
                } else {
 | 
						|
                    // read up to six hex digits
 | 
						|
                    for (i=0; isHexDigit(c) && i<6; i++) {
 | 
						|
                        string += c;
 | 
						|
                        c = reader.read();
 | 
						|
                    }
 | 
						|
                    // swallow trailing newline or space
 | 
						|
                    if (c === "\r" && reader.peek() === "\n") {
 | 
						|
                        string += c;
 | 
						|
                        c = reader.read();
 | 
						|
                    }
 | 
						|
                    if (isWhitespace(c)) {
 | 
						|
                        string += c;
 | 
						|
                    } else {
 | 
						|
                        // This character is null or not part of the escape;
 | 
						|
                        // jump back to the top to process it.
 | 
						|
                        continue;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            } else if (c === delim) {
 | 
						|
                break; // delimiter found.
 | 
						|
            } else if (isNewLine(reader.peek())) {
 | 
						|
                // newline without an escapement: it's an invalid string
 | 
						|
                tt = Tokens.INVALID;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            c = reader.read();
 | 
						|
        }
 | 
						|
 | 
						|
        //if c is null, that means we're out of input and the string was never closed
 | 
						|
        if (c === null) {
 | 
						|
            tt = Tokens.INVALID;
 | 
						|
        }
 | 
						|
 | 
						|
        return this.createToken(tt, string, startLine, startCol);
 | 
						|
    },
 | 
						|
 | 
						|
    unicodeRangeToken: function(first, startLine, startCol) {
 | 
						|
        var reader  = this._reader,
 | 
						|
            value   = first,
 | 
						|
            temp,
 | 
						|
            tt      = Tokens.CHAR;
 | 
						|
 | 
						|
        //then it should be a unicode range
 | 
						|
        if (reader.peek() === "+") {
 | 
						|
            reader.mark();
 | 
						|
            value += reader.read();
 | 
						|
            value += this.readUnicodeRangePart(true);
 | 
						|
 | 
						|
            //ensure there's an actual unicode range here
 | 
						|
            if (value.length === 2) {
 | 
						|
                reader.reset();
 | 
						|
            } else {
 | 
						|
 | 
						|
                tt = Tokens.UNICODE_RANGE;
 | 
						|
 | 
						|
                //if there's a ? in the first part, there can't be a second part
 | 
						|
                if (value.indexOf("?") === -1) {
 | 
						|
 | 
						|
                    if (reader.peek() === "-") {
 | 
						|
                        reader.mark();
 | 
						|
                        temp = reader.read();
 | 
						|
                        temp += this.readUnicodeRangePart(false);
 | 
						|
 | 
						|
                        //if there's not another value, back up and just take the first
 | 
						|
                        if (temp.length === 1) {
 | 
						|
                            reader.reset();
 | 
						|
                        } else {
 | 
						|
                            value += temp;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return this.createToken(tt, value, startLine, startCol);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Produces a S token based on the specified information. Since whitespace
 | 
						|
     * may have multiple characters, this consumes all whitespace characters
 | 
						|
     * into a single token.
 | 
						|
     * @param {String} first The first character in the token.
 | 
						|
     * @param {int} startLine The beginning line for the character.
 | 
						|
     * @param {int} startCol The beginning column for the character.
 | 
						|
     * @return {Object} A token object.
 | 
						|
     * @method whitespaceToken
 | 
						|
     */
 | 
						|
    whitespaceToken: function(first, startLine, startCol) {
 | 
						|
        var value   = first + this.readWhitespace();
 | 
						|
        return this.createToken(Tokens.S, value, startLine, startCol);
 | 
						|
    },
 | 
						|
 | 
						|
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
    // Methods to read values from the string stream
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
 | 
						|
    readUnicodeRangePart: function(allowQuestionMark) {
 | 
						|
        var reader  = this._reader,
 | 
						|
            part = "",
 | 
						|
            c       = reader.peek();
 | 
						|
 | 
						|
        //first read hex digits
 | 
						|
        while (isHexDigit(c) && part.length < 6) {
 | 
						|
            reader.read();
 | 
						|
            part += c;
 | 
						|
            c = reader.peek();
 | 
						|
        }
 | 
						|
 | 
						|
        //then read question marks if allowed
 | 
						|
        if (allowQuestionMark) {
 | 
						|
            while (c === "?" && part.length < 6) {
 | 
						|
                reader.read();
 | 
						|
                part += c;
 | 
						|
                c = reader.peek();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        //there can't be any other characters after this point
 | 
						|
 | 
						|
        return part;
 | 
						|
    },
 | 
						|
 | 
						|
    readWhitespace: function() {
 | 
						|
        var reader  = this._reader,
 | 
						|
            whitespace = "",
 | 
						|
            c       = reader.peek();
 | 
						|
 | 
						|
        while (isWhitespace(c)) {
 | 
						|
            reader.read();
 | 
						|
            whitespace += c;
 | 
						|
            c = reader.peek();
 | 
						|
        }
 | 
						|
 | 
						|
        return whitespace;
 | 
						|
    },
 | 
						|
    readNumber: function(first) {
 | 
						|
        var reader  = this._reader,
 | 
						|
            number  = first,
 | 
						|
            hasDot  = (first === "."),
 | 
						|
            c       = reader.peek();
 | 
						|
 | 
						|
 | 
						|
        while (c) {
 | 
						|
            if (isDigit(c)) {
 | 
						|
                number += reader.read();
 | 
						|
            } else if (c === ".") {
 | 
						|
                if (hasDot) {
 | 
						|
                    break;
 | 
						|
                } else {
 | 
						|
                    hasDot = true;
 | 
						|
                    number += reader.read();
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            c = reader.peek();
 | 
						|
        }
 | 
						|
 | 
						|
        return number;
 | 
						|
    },
 | 
						|
 | 
						|
    // returns null w/o resetting reader if string is invalid.
 | 
						|
    readString: function() {
 | 
						|
        var token = this.stringToken(this._reader.read(), 0, 0);
 | 
						|
        return token.type === Tokens.INVALID ? null : token.value;
 | 
						|
    },
 | 
						|
 | 
						|
    // returns null w/o resetting reader if URI is invalid.
 | 
						|
    readURI: function(first) {
 | 
						|
        var reader  = this._reader,
 | 
						|
            uri     = first,
 | 
						|
            inner   = "",
 | 
						|
            c       = reader.peek();
 | 
						|
 | 
						|
        //skip whitespace before
 | 
						|
        while (c && isWhitespace(c)) {
 | 
						|
            reader.read();
 | 
						|
            c = reader.peek();
 | 
						|
        }
 | 
						|
 | 
						|
        //it's a string
 | 
						|
        if (c === "'" || c === "\"") {
 | 
						|
            inner = this.readString();
 | 
						|
            if (inner !== null) {
 | 
						|
                inner = PropertyValuePart.parseString(inner);
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            inner = this.readUnquotedURL();
 | 
						|
        }
 | 
						|
 | 
						|
        c = reader.peek();
 | 
						|
 | 
						|
        //skip whitespace after
 | 
						|
        while (c && isWhitespace(c)) {
 | 
						|
            reader.read();
 | 
						|
            c = reader.peek();
 | 
						|
        }
 | 
						|
 | 
						|
        //if there was no inner value or the next character isn't closing paren, it's not a URI
 | 
						|
        if (inner === null || c !== ")") {
 | 
						|
            uri = null;
 | 
						|
        } else {
 | 
						|
            // Ensure argument to URL is always double-quoted
 | 
						|
            // (This simplifies later processing in PropertyValuePart.)
 | 
						|
            uri += PropertyValuePart.serializeString(inner) + reader.read();
 | 
						|
        }
 | 
						|
 | 
						|
        return uri;
 | 
						|
    },
 | 
						|
    // This method never fails, although it may return an empty string.
 | 
						|
    readUnquotedURL: function(first) {
 | 
						|
        var reader  = this._reader,
 | 
						|
            url     = first || "",
 | 
						|
            c;
 | 
						|
 | 
						|
        for (c = reader.peek(); c; c = reader.peek()) {
 | 
						|
            // Note that the grammar at
 | 
						|
            // https://www.w3.org/TR/CSS2/grammar.html#scanner
 | 
						|
            // incorrectly includes the backslash character in the
 | 
						|
            // `url` production, although it is correctly omitted in
 | 
						|
            // the `baduri1` production.
 | 
						|
            if (nonascii.test(c) || /^[\-!#$%&*-\[\]-~]$/.test(c)) {
 | 
						|
                url += c;
 | 
						|
                reader.read();
 | 
						|
            } else if (c === "\\") {
 | 
						|
                if (/^[^\r\n\f]$/.test(reader.peek(2))) {
 | 
						|
                    url += this.readEscape(reader.read(), true);
 | 
						|
                } else {
 | 
						|
                    break; // bad escape sequence.
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                break; // bad character
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return url;
 | 
						|
    },
 | 
						|
 | 
						|
    readName: function(first) {
 | 
						|
        var reader  = this._reader,
 | 
						|
            ident   = first || "",
 | 
						|
            c;
 | 
						|
 | 
						|
        for (c = reader.peek(); c; c = reader.peek()) {
 | 
						|
            if (c === "\\") {
 | 
						|
                if (/^[^\r\n\f]$/.test(reader.peek(2))) {
 | 
						|
                    ident += this.readEscape(reader.read(), true);
 | 
						|
                } else {
 | 
						|
                    // Bad escape sequence.
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            } else if (isNameChar(c)) {
 | 
						|
                ident += reader.read();
 | 
						|
            } else {
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return ident;
 | 
						|
    },
 | 
						|
 | 
						|
    readEscape: function(first, unescape) {
 | 
						|
        var reader  = this._reader,
 | 
						|
            cssEscape = first || "",
 | 
						|
            i       = 0,
 | 
						|
            c       = reader.peek();
 | 
						|
 | 
						|
        if (isHexDigit(c)) {
 | 
						|
            do {
 | 
						|
                cssEscape += reader.read();
 | 
						|
                c = reader.peek();
 | 
						|
            } while (c && isHexDigit(c) && ++i < 6);
 | 
						|
        }
 | 
						|
 | 
						|
        if (cssEscape.length === 1) {
 | 
						|
            if (/^[^\r\n\f0-9a-f]$/.test(c)) {
 | 
						|
                reader.read();
 | 
						|
                if (unescape) {
 | 
						|
                    return c;
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                // We should never get here (readName won't call readEscape
 | 
						|
                // if the escape sequence is bad).
 | 
						|
                throw new Error("Bad escape sequence.");
 | 
						|
            }
 | 
						|
        } else if (c === "\r") {
 | 
						|
            reader.read();
 | 
						|
            if (reader.peek() === "\n") {
 | 
						|
                c += reader.read();
 | 
						|
            }
 | 
						|
        } else if (/^[ \t\n\f]$/.test(c)) {
 | 
						|
            reader.read();
 | 
						|
        } else {
 | 
						|
            c = "";
 | 
						|
        }
 | 
						|
 | 
						|
        if (unescape) {
 | 
						|
            var cp = parseInt(cssEscape.slice(first.length), 16);
 | 
						|
            return String.fromCodePoint ? String.fromCodePoint(cp) :
 | 
						|
                String.fromCharCode(cp);
 | 
						|
        }
 | 
						|
        return cssEscape + c;
 | 
						|
    },
 | 
						|
 | 
						|
    readComment: function(first) {
 | 
						|
        var reader  = this._reader,
 | 
						|
            comment = first || "",
 | 
						|
            c       = reader.read();
 | 
						|
 | 
						|
        if (c === "*") {
 | 
						|
            while (c) {
 | 
						|
                comment += c;
 | 
						|
 | 
						|
                //look for end of comment
 | 
						|
                if (comment.length > 2 && c === "*" && reader.peek() === "/") {
 | 
						|
                    comment += reader.read();
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
 | 
						|
                c = reader.read();
 | 
						|
            }
 | 
						|
 | 
						|
            return comment;
 | 
						|
        } else {
 | 
						|
            return "";
 | 
						|
        }
 | 
						|
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
},{"../util/TokenStreamBase":27,"./PropertyValuePart":11,"./Tokens":18}],18:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
var Tokens = module.exports = [
 | 
						|
 | 
						|
    /*
 | 
						|
     * The following token names are defined in CSS3 Grammar: https://www.w3.org/TR/css3-syntax/#lexical
 | 
						|
     */
 | 
						|
 | 
						|
    // HTML-style comments
 | 
						|
    { name: "CDO" },
 | 
						|
    { name: "CDC" },
 | 
						|
 | 
						|
    // ignorables
 | 
						|
    { name: "S", whitespace: true/*, channel: "ws"*/ },
 | 
						|
    { name: "COMMENT", comment: true, hide: true, channel: "comment" },
 | 
						|
 | 
						|
    // attribute equality
 | 
						|
    { name: "INCLUDES", text: "~=" },
 | 
						|
    { name: "DASHMATCH", text: "|=" },
 | 
						|
    { name: "PREFIXMATCH", text: "^=" },
 | 
						|
    { name: "SUFFIXMATCH", text: "$=" },
 | 
						|
    { name: "SUBSTRINGMATCH", text: "*=" },
 | 
						|
 | 
						|
    // identifier types
 | 
						|
    { name: "STRING" },
 | 
						|
    { name: "IDENT" },
 | 
						|
    { name: "HASH" },
 | 
						|
 | 
						|
    // at-keywords
 | 
						|
    { name: "IMPORT_SYM", text: "@import" },
 | 
						|
    { name: "PAGE_SYM", text: "@page" },
 | 
						|
    { name: "MEDIA_SYM", text: "@media" },
 | 
						|
    { name: "FONT_FACE_SYM", text: "@font-face" },
 | 
						|
    { name: "CHARSET_SYM", text: "@charset" },
 | 
						|
    { name: "NAMESPACE_SYM", text: "@namespace" },
 | 
						|
    { name: "SUPPORTS_SYM", text: "@supports" },
 | 
						|
    { name: "VIEWPORT_SYM", text: ["@viewport", "@-ms-viewport", "@-o-viewport"] },
 | 
						|
    { name: "DOCUMENT_SYM", text: ["@document", "@-moz-document"] },
 | 
						|
    { name: "UNKNOWN_SYM" },
 | 
						|
    //{ name: "ATKEYWORD"},
 | 
						|
 | 
						|
    // CSS3 animations
 | 
						|
    { name: "KEYFRAMES_SYM", text: [ "@keyframes", "@-webkit-keyframes", "@-moz-keyframes", "@-o-keyframes" ] },
 | 
						|
 | 
						|
    // important symbol
 | 
						|
    { name: "IMPORTANT_SYM" },
 | 
						|
 | 
						|
    // measurements
 | 
						|
    { name: "LENGTH" },
 | 
						|
    { name: "ANGLE" },
 | 
						|
    { name: "TIME" },
 | 
						|
    { name: "FREQ" },
 | 
						|
    { name: "DIMENSION" },
 | 
						|
    { name: "PERCENTAGE" },
 | 
						|
    { name: "NUMBER" },
 | 
						|
 | 
						|
    // functions
 | 
						|
    { name: "URI" },
 | 
						|
    { name: "FUNCTION" },
 | 
						|
 | 
						|
    // Unicode ranges
 | 
						|
    { name: "UNICODE_RANGE" },
 | 
						|
 | 
						|
    /*
 | 
						|
     * The following token names are defined in CSS3 Selectors: https://www.w3.org/TR/css3-selectors/#selector-syntax
 | 
						|
     */
 | 
						|
 | 
						|
    // invalid string
 | 
						|
    { name: "INVALID" },
 | 
						|
 | 
						|
    // combinators
 | 
						|
    { name: "PLUS", text: "+" },
 | 
						|
    { name: "GREATER", text: ">" },
 | 
						|
    { name: "COMMA", text: "," },
 | 
						|
    { name: "TILDE", text: "~" },
 | 
						|
 | 
						|
    // modifier
 | 
						|
    { name: "NOT" },
 | 
						|
 | 
						|
    /*
 | 
						|
     * Defined in CSS3 Paged Media
 | 
						|
     */
 | 
						|
    { name: "TOPLEFTCORNER_SYM", text: "@top-left-corner" },
 | 
						|
    { name: "TOPLEFT_SYM", text: "@top-left" },
 | 
						|
    { name: "TOPCENTER_SYM", text: "@top-center" },
 | 
						|
    { name: "TOPRIGHT_SYM", text: "@top-right" },
 | 
						|
    { name: "TOPRIGHTCORNER_SYM", text: "@top-right-corner" },
 | 
						|
    { name: "BOTTOMLEFTCORNER_SYM", text: "@bottom-left-corner" },
 | 
						|
    { name: "BOTTOMLEFT_SYM", text: "@bottom-left" },
 | 
						|
    { name: "BOTTOMCENTER_SYM", text: "@bottom-center" },
 | 
						|
    { name: "BOTTOMRIGHT_SYM", text: "@bottom-right" },
 | 
						|
    { name: "BOTTOMRIGHTCORNER_SYM", text: "@bottom-right-corner" },
 | 
						|
    { name: "LEFTTOP_SYM", text: "@left-top" },
 | 
						|
    { name: "LEFTMIDDLE_SYM", text: "@left-middle" },
 | 
						|
    { name: "LEFTBOTTOM_SYM", text: "@left-bottom" },
 | 
						|
    { name: "RIGHTTOP_SYM", text: "@right-top" },
 | 
						|
    { name: "RIGHTMIDDLE_SYM", text: "@right-middle" },
 | 
						|
    { name: "RIGHTBOTTOM_SYM", text: "@right-bottom" },
 | 
						|
 | 
						|
    /*
 | 
						|
     * The following token names are defined in CSS3 Media Queries: https://www.w3.org/TR/css3-mediaqueries/#syntax
 | 
						|
     */
 | 
						|
    /*{ name: "MEDIA_ONLY", state: "media"},
 | 
						|
    { name: "MEDIA_NOT", state: "media"},
 | 
						|
    { name: "MEDIA_AND", state: "media"},*/
 | 
						|
    { name: "RESOLUTION", state: "media" },
 | 
						|
 | 
						|
    /*
 | 
						|
     * The following token names are not defined in any CSS specification but are used by the lexer.
 | 
						|
     */
 | 
						|
 | 
						|
    // not a real token, but useful for stupid IE filters
 | 
						|
    { name: "IE_FUNCTION" },
 | 
						|
 | 
						|
    // part of CSS3 grammar but not the Flex code
 | 
						|
    { name: "CHAR" },
 | 
						|
 | 
						|
    // TODO: Needed?
 | 
						|
    // Not defined as tokens, but might as well be
 | 
						|
    {
 | 
						|
        name: "PIPE",
 | 
						|
        text: "|"
 | 
						|
    },
 | 
						|
    {
 | 
						|
        name: "SLASH",
 | 
						|
        text: "/"
 | 
						|
    },
 | 
						|
    {
 | 
						|
        name: "MINUS",
 | 
						|
        text: "-"
 | 
						|
    },
 | 
						|
    {
 | 
						|
        name: "STAR",
 | 
						|
        text: "*"
 | 
						|
    },
 | 
						|
 | 
						|
    {
 | 
						|
        name: "LBRACE",
 | 
						|
        endChar: "}",
 | 
						|
        text: "{"
 | 
						|
    },
 | 
						|
    {
 | 
						|
        name: "RBRACE",
 | 
						|
        text: "}"
 | 
						|
    },
 | 
						|
    {
 | 
						|
        name: "LBRACKET",
 | 
						|
        endChar: "]",
 | 
						|
        text: "["
 | 
						|
    },
 | 
						|
    {
 | 
						|
        name: "RBRACKET",
 | 
						|
        text: "]"
 | 
						|
    },
 | 
						|
    {
 | 
						|
        name: "EQUALS",
 | 
						|
        text: "="
 | 
						|
    },
 | 
						|
    {
 | 
						|
        name: "COLON",
 | 
						|
        text: ":"
 | 
						|
    },
 | 
						|
    {
 | 
						|
        name: "SEMICOLON",
 | 
						|
        text: ";"
 | 
						|
    },
 | 
						|
    {
 | 
						|
        name: "LPAREN",
 | 
						|
        endChar: ")",
 | 
						|
        text: "("
 | 
						|
    },
 | 
						|
    {
 | 
						|
        name: "RPAREN",
 | 
						|
        text: ")"
 | 
						|
    },
 | 
						|
    {
 | 
						|
        name: "DOT",
 | 
						|
        text: "."
 | 
						|
    }
 | 
						|
];
 | 
						|
 | 
						|
(function() {
 | 
						|
    var nameMap = [],
 | 
						|
        typeMap = Object.create(null);
 | 
						|
 | 
						|
    Tokens.UNKNOWN = -1;
 | 
						|
    Tokens.unshift({ name:"EOF" });
 | 
						|
    for (var i=0, len = Tokens.length; i < len; i++) {
 | 
						|
        nameMap.push(Tokens[i].name);
 | 
						|
        Tokens[Tokens[i].name] = i;
 | 
						|
        if (Tokens[i].text) {
 | 
						|
            if (Tokens[i].text instanceof Array) {
 | 
						|
                for (var j=0; j < Tokens[i].text.length; j++) {
 | 
						|
                    typeMap[Tokens[i].text[j]] = i;
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                typeMap[Tokens[i].text] = i;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    Tokens.name = function(tt) {
 | 
						|
        return nameMap[tt];
 | 
						|
    };
 | 
						|
 | 
						|
    Tokens.type = function(c) {
 | 
						|
        return typeMap[c] || -1;
 | 
						|
    };
 | 
						|
})();
 | 
						|
 | 
						|
},{}],19:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
/* exported Validation */
 | 
						|
 | 
						|
var Matcher = require("./Matcher");
 | 
						|
var Properties = require("./Properties");
 | 
						|
var ValidationTypes = require("./ValidationTypes");
 | 
						|
var ValidationError = require("./ValidationError");
 | 
						|
var PropertyValueIterator = require("./PropertyValueIterator");
 | 
						|
 | 
						|
var Validation = module.exports = {
 | 
						|
 | 
						|
    validate: function(property, value) {
 | 
						|
 | 
						|
        //normalize name
 | 
						|
        var name        = property.toString().toLowerCase(),
 | 
						|
            expression  = new PropertyValueIterator(value),
 | 
						|
            spec        = Properties[name],
 | 
						|
            part;
 | 
						|
 | 
						|
        if (!spec) {
 | 
						|
            if (name.indexOf("-") !== 0) {    //vendor prefixed are ok
 | 
						|
                throw new ValidationError("Unknown property '" + property + "'.", property.line, property.col);
 | 
						|
            }
 | 
						|
        } else if (typeof spec !== "number") {
 | 
						|
 | 
						|
            // All properties accept some CSS-wide values.
 | 
						|
            // https://drafts.csswg.org/css-values-3/#common-keywords
 | 
						|
            if (ValidationTypes.isAny(expression, "inherit | initial | unset")) {
 | 
						|
                if (expression.hasNext()) {
 | 
						|
                    part = expression.next();
 | 
						|
                    throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
 | 
						|
                }
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            // Property-specific validation.
 | 
						|
            this.singleProperty(spec, expression);
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
    },
 | 
						|
 | 
						|
    singleProperty: function(types, expression) {
 | 
						|
 | 
						|
        var result      = false,
 | 
						|
            value       = expression.value,
 | 
						|
            part;
 | 
						|
 | 
						|
        result = Matcher.parse(types).match(expression);
 | 
						|
 | 
						|
        if (!result) {
 | 
						|
            if (expression.hasNext() && !expression.isFirst()) {
 | 
						|
                part = expression.peek();
 | 
						|
                throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
 | 
						|
            } else {
 | 
						|
                throw new ValidationError("Expected (" + ValidationTypes.describe(types) + ") but found '" + value + "'.", value.line, value.col);
 | 
						|
            }
 | 
						|
        } else if (expression.hasNext()) {
 | 
						|
            part = expression.next();
 | 
						|
            throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
 | 
						|
        }
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
},{"./Matcher":3,"./Properties":7,"./PropertyValueIterator":10,"./ValidationError":20,"./ValidationTypes":21}],20:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = ValidationError;
 | 
						|
 | 
						|
/**
 | 
						|
 * Type to use when a validation error occurs.
 | 
						|
 * @class ValidationError
 | 
						|
 * @namespace parserlib.util
 | 
						|
 * @constructor
 | 
						|
 * @param {String} message The error message.
 | 
						|
 * @param {int} line The line at which the error occurred.
 | 
						|
 * @param {int} col The column at which the error occurred.
 | 
						|
 */
 | 
						|
function ValidationError(message, line, col) {
 | 
						|
 | 
						|
    /**
 | 
						|
     * The column at which the error occurred.
 | 
						|
     * @type int
 | 
						|
     * @property col
 | 
						|
     */
 | 
						|
    this.col = col;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The line at which the error occurred.
 | 
						|
     * @type int
 | 
						|
     * @property line
 | 
						|
     */
 | 
						|
    this.line = line;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The text representation of the unit.
 | 
						|
     * @type String
 | 
						|
     * @property text
 | 
						|
     */
 | 
						|
    this.message = message;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
//inherit from Error
 | 
						|
ValidationError.prototype = new Error();
 | 
						|
 | 
						|
},{}],21:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
var ValidationTypes = module.exports;
 | 
						|
 | 
						|
var Matcher = require("./Matcher");
 | 
						|
 | 
						|
function copy(to, from) {
 | 
						|
    Object.keys(from).forEach(function(prop) {
 | 
						|
        to[prop] = from[prop];
 | 
						|
    });
 | 
						|
}
 | 
						|
copy(ValidationTypes, {
 | 
						|
 | 
						|
    isLiteral: function (part, literals) {
 | 
						|
        var text = part.text.toString().toLowerCase(),
 | 
						|
            args = literals.split(" | "),
 | 
						|
            i, len, found = false;
 | 
						|
 | 
						|
        for (i=0, len=args.length; i < len && !found; i++) {
 | 
						|
            if (args[i].charAt(0) === "<") {
 | 
						|
                found = this.simple[args[i]](part);
 | 
						|
            } else if (args[i].slice(-2) === "()") {
 | 
						|
                found = (part.type === "function" &&
 | 
						|
                         part.name === args[i].slice(0, -2));
 | 
						|
            } else if (text === args[i].toLowerCase()) {
 | 
						|
                found = true;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return found;
 | 
						|
    },
 | 
						|
 | 
						|
    isSimple: function(type) {
 | 
						|
        return Boolean(this.simple[type]);
 | 
						|
    },
 | 
						|
 | 
						|
    isComplex: function(type) {
 | 
						|
        return Boolean(this.complex[type]);
 | 
						|
    },
 | 
						|
 | 
						|
    describe: function(type) {
 | 
						|
        if (this.complex[type] instanceof Matcher) {
 | 
						|
            return this.complex[type].toString(0);
 | 
						|
        }
 | 
						|
        return type;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Determines if the next part(s) of the given expression
 | 
						|
     * are any of the given types.
 | 
						|
     */
 | 
						|
    isAny: function (expression, types) {
 | 
						|
        var args = types.split(" | "),
 | 
						|
            i, len, found = false;
 | 
						|
 | 
						|
        for (i=0, len=args.length; i < len && !found && expression.hasNext(); i++) {
 | 
						|
            found = this.isType(expression, args[i]);
 | 
						|
        }
 | 
						|
 | 
						|
        return found;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Determines if the next part(s) of the given expression
 | 
						|
     * are one of a group.
 | 
						|
     */
 | 
						|
    isAnyOfGroup: function(expression, types) {
 | 
						|
        var args = types.split(" || "),
 | 
						|
            i, len, found = false;
 | 
						|
 | 
						|
        for (i=0, len=args.length; i < len && !found; i++) {
 | 
						|
            found = this.isType(expression, args[i]);
 | 
						|
        }
 | 
						|
 | 
						|
        return found ? args[i-1] : false;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Determines if the next part(s) of the given expression
 | 
						|
     * are of a given type.
 | 
						|
     */
 | 
						|
    isType: function (expression, type) {
 | 
						|
        var part = expression.peek(),
 | 
						|
            result = false;
 | 
						|
 | 
						|
        if (type.charAt(0) !== "<") {
 | 
						|
            result = this.isLiteral(part, type);
 | 
						|
            if (result) {
 | 
						|
                expression.next();
 | 
						|
            }
 | 
						|
        } else if (this.simple[type]) {
 | 
						|
            result = this.simple[type](part);
 | 
						|
            if (result) {
 | 
						|
                expression.next();
 | 
						|
            }
 | 
						|
        } else if (this.complex[type] instanceof Matcher) {
 | 
						|
            result = this.complex[type].match(expression);
 | 
						|
        } else {
 | 
						|
            result = this.complex[type](expression);
 | 
						|
        }
 | 
						|
 | 
						|
        return result;
 | 
						|
    },
 | 
						|
 | 
						|
 | 
						|
    simple: {
 | 
						|
        __proto__: null,
 | 
						|
 | 
						|
        "<absolute-size>":
 | 
						|
            "xx-small | x-small | small | medium | large | x-large | xx-large",
 | 
						|
 | 
						|
        "<animateable-feature>":
 | 
						|
            "scroll-position | contents | <animateable-feature-name>",
 | 
						|
 | 
						|
        "<animateable-feature-name>": function(part) {
 | 
						|
            return this["<ident>"](part) &&
 | 
						|
                !/^(unset|initial|inherit|will-change|auto|scroll-position|contents)$/i.test(part);
 | 
						|
        },
 | 
						|
 | 
						|
        "<angle>": function(part) {
 | 
						|
            return part.type === "angle";
 | 
						|
        },
 | 
						|
 | 
						|
        "<attachment>": "scroll | fixed | local",
 | 
						|
 | 
						|
        "<attr>": "attr()",
 | 
						|
 | 
						|
        // inset() = inset( <shape-arg>{1,4} [round <border-radius>]? )
 | 
						|
        // circle() = circle( [<shape-radius>]? [at <position>]? )
 | 
						|
        // ellipse() = ellipse( [<shape-radius>{2}]? [at <position>]? )
 | 
						|
        // polygon() = polygon( [<fill-rule>,]? [<shape-arg> <shape-arg>]# )
 | 
						|
        "<basic-shape>": "inset() | circle() | ellipse() | polygon()",
 | 
						|
 | 
						|
        "<bg-image>": "<image> | <gradient> | none",
 | 
						|
 | 
						|
        "<border-style>":
 | 
						|
            "none | hidden | dotted | dashed | solid | double | groove | " +
 | 
						|
            "ridge | inset | outset",
 | 
						|
 | 
						|
        "<border-width>": "<length> | thin | medium | thick",
 | 
						|
 | 
						|
        "<box>": "padding-box | border-box | content-box",
 | 
						|
 | 
						|
        "<clip-source>": "<uri>",
 | 
						|
 | 
						|
        "<color>": function(part) {
 | 
						|
            return part.type === "color" || String(part) === "transparent" || String(part) === "currentColor";
 | 
						|
        },
 | 
						|
 | 
						|
        // The SVG <color> spec doesn't include "currentColor" or "transparent" as a color.
 | 
						|
        "<color-svg>": function(part) {
 | 
						|
            return part.type === "color";
 | 
						|
        },
 | 
						|
 | 
						|
        "<content>": "content()",
 | 
						|
 | 
						|
        // https://www.w3.org/TR/css3-sizing/#width-height-keywords
 | 
						|
        "<content-sizing>":
 | 
						|
            "fill-available | -moz-available | -webkit-fill-available | " +
 | 
						|
            "max-content | -moz-max-content | -webkit-max-content | " +
 | 
						|
            "min-content | -moz-min-content | -webkit-min-content | " +
 | 
						|
            "fit-content | -moz-fit-content | -webkit-fit-content",
 | 
						|
 | 
						|
        "<feature-tag-value>": function(part) {
 | 
						|
            return part.type === "function" && /^[A-Z0-9]{4}$/i.test(part);
 | 
						|
        },
 | 
						|
 | 
						|
        // custom() isn't actually in the spec
 | 
						|
        "<filter-function>":
 | 
						|
            "blur() | brightness() | contrast() | custom() | " +
 | 
						|
            "drop-shadow() | grayscale() | hue-rotate() | invert() | " +
 | 
						|
            "opacity() | saturate() | sepia()",
 | 
						|
 | 
						|
        "<flex-basis>": "<width>",
 | 
						|
 | 
						|
        "<flex-direction>": "row | row-reverse | column | column-reverse",
 | 
						|
 | 
						|
        "<flex-grow>": "<number>",
 | 
						|
 | 
						|
        "<flex-shrink>": "<number>",
 | 
						|
 | 
						|
        "<flex-wrap>": "nowrap | wrap | wrap-reverse",
 | 
						|
 | 
						|
        "<font-size>":
 | 
						|
            "<absolute-size> | <relative-size> | <length> | <percentage>",
 | 
						|
 | 
						|
        "<font-stretch>":
 | 
						|
            "normal | ultra-condensed | extra-condensed | condensed | " +
 | 
						|
            "semi-condensed | semi-expanded | expanded | extra-expanded | " +
 | 
						|
            "ultra-expanded",
 | 
						|
 | 
						|
        "<font-style>": "normal | italic | oblique",
 | 
						|
 | 
						|
        "<font-variant-caps>":
 | 
						|
            "small-caps | all-small-caps | petite-caps | all-petite-caps | " +
 | 
						|
            "unicase | titling-caps",
 | 
						|
 | 
						|
        "<font-variant-css21>": "normal | small-caps",
 | 
						|
 | 
						|
        "<font-weight>":
 | 
						|
            "normal | bold | bolder | lighter | " +
 | 
						|
            "100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900",
 | 
						|
 | 
						|
        "<generic-family>":
 | 
						|
            "serif | sans-serif | cursive | fantasy | monospace",
 | 
						|
 | 
						|
        "<geometry-box>": "<shape-box> | fill-box | stroke-box | view-box",
 | 
						|
 | 
						|
        "<glyph-angle>": function(part) {
 | 
						|
            return part.type === "angle" && part.units === "deg";
 | 
						|
        },
 | 
						|
 | 
						|
        "<gradient>": function(part) {
 | 
						|
            return part.type === "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?(?:repeating\-)?(?:radial\-|linear\-)?gradient/i.test(part);
 | 
						|
        },
 | 
						|
 | 
						|
        "<icccolor>":
 | 
						|
            "cielab() | cielch() | cielchab() | " +
 | 
						|
            "icc-color() | icc-named-color()",
 | 
						|
 | 
						|
        //any identifier
 | 
						|
        "<ident>": function(part) {
 | 
						|
            return part.type === "identifier" || part.wasIdent;
 | 
						|
        },
 | 
						|
 | 
						|
        "<ident-not-generic-family>": function(part) {
 | 
						|
            return this["<ident>"](part) && !this["<generic-family>"](part);
 | 
						|
        },
 | 
						|
 | 
						|
        "<image>": "<uri>",
 | 
						|
 | 
						|
        "<integer>": function(part) {
 | 
						|
            return part.type === "integer";
 | 
						|
        },
 | 
						|
 | 
						|
        "<length>": function(part) {
 | 
						|
            if (part.type === "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?calc/i.test(part)) {
 | 
						|
                return true;
 | 
						|
            } else {
 | 
						|
                return part.type === "length" || part.type === "number" || part.type === "integer" || String(part) === "0";
 | 
						|
            }
 | 
						|
        },
 | 
						|
 | 
						|
        "<line>": function(part) {
 | 
						|
            return part.type === "integer";
 | 
						|
        },
 | 
						|
 | 
						|
        "<line-height>": "<number> | <length> | <percentage> | normal",
 | 
						|
 | 
						|
        "<margin-width>": "<length> | <percentage> | auto",
 | 
						|
 | 
						|
        "<miterlimit>": function(part) {
 | 
						|
            return this["<number>"](part) && part.value >= 1;
 | 
						|
        },
 | 
						|
 | 
						|
        "<nonnegative-length-or-percentage>": function(part) {
 | 
						|
            return (this["<length>"](part) || this["<percentage>"](part)) &&
 | 
						|
                (String(part) === "0" || part.type === "function" || (part.value) >= 0);
 | 
						|
        },
 | 
						|
 | 
						|
        "<nonnegative-number-or-percentage>": function(part) {
 | 
						|
            return (this["<number>"](part) || this["<percentage>"](part)) &&
 | 
						|
                (String(part) === "0" || part.type === "function" || (part.value) >= 0);
 | 
						|
        },
 | 
						|
 | 
						|
        "<number>": function(part) {
 | 
						|
            return part.type === "number" || this["<integer>"](part);
 | 
						|
        },
 | 
						|
 | 
						|
        "<opacity-value>": function(part) {
 | 
						|
            return this["<number>"](part) && part.value >= 0 && part.value <= 1;
 | 
						|
        },
 | 
						|
 | 
						|
        "<padding-width>": "<nonnegative-length-or-percentage>",
 | 
						|
 | 
						|
        "<percentage>": function(part) {
 | 
						|
            return part.type === "percentage" || String(part) === "0";
 | 
						|
        },
 | 
						|
 | 
						|
        "<relative-size>": "smaller | larger",
 | 
						|
 | 
						|
        "<shape>": "rect() | inset-rect()",
 | 
						|
 | 
						|
        "<shape-box>": "<box> | margin-box",
 | 
						|
 | 
						|
        "<single-animation-direction>":
 | 
						|
            "normal | reverse | alternate | alternate-reverse",
 | 
						|
 | 
						|
        "<single-animation-name>": function(part) {
 | 
						|
            return this["<ident>"](part) &&
 | 
						|
                /^-?[a-z_][-a-z0-9_]+$/i.test(part) &&
 | 
						|
                !/^(none|unset|initial|inherit)$/i.test(part);
 | 
						|
        },
 | 
						|
 | 
						|
        "<string>": function(part) {
 | 
						|
            return part.type === "string";
 | 
						|
        },
 | 
						|
 | 
						|
        "<time>": function(part) {
 | 
						|
            return part.type === "time";
 | 
						|
        },
 | 
						|
 | 
						|
        "<uri>": function(part) {
 | 
						|
            return part.type === "uri";
 | 
						|
        },
 | 
						|
 | 
						|
        "<width>": "<margin-width>"
 | 
						|
    },
 | 
						|
 | 
						|
    complex: {
 | 
						|
        __proto__: null,
 | 
						|
 | 
						|
        "<azimuth>":
 | 
						|
            "<angle>" +
 | 
						|
            " | " +
 | 
						|
            "[ [ left-side | far-left | left | center-left | center | " +
 | 
						|
            "center-right | right | far-right | right-side ] || behind ]" +
 | 
						|
            " | "+
 | 
						|
            "leftwards | rightwards",
 | 
						|
 | 
						|
        "<bg-position>": "<position>#",
 | 
						|
 | 
						|
        "<bg-size>":
 | 
						|
            "[ <length> | <percentage> | auto ]{1,2} | cover | contain",
 | 
						|
 | 
						|
        "<border-image-slice>":
 | 
						|
        // [<number> | <percentage>]{1,4} && fill?
 | 
						|
        // *but* fill can appear between any of the numbers
 | 
						|
        Matcher.many([true /* first element is required */],
 | 
						|
                     Matcher.cast("<nonnegative-number-or-percentage>"),
 | 
						|
                     Matcher.cast("<nonnegative-number-or-percentage>"),
 | 
						|
                     Matcher.cast("<nonnegative-number-or-percentage>"),
 | 
						|
                     Matcher.cast("<nonnegative-number-or-percentage>"),
 | 
						|
                     "fill"),
 | 
						|
 | 
						|
        "<border-radius>":
 | 
						|
            "<nonnegative-length-or-percentage>{1,4} " +
 | 
						|
            "[ / <nonnegative-length-or-percentage>{1,4} ]?",
 | 
						|
 | 
						|
        "<box-shadow>": "none | <shadow>#",
 | 
						|
 | 
						|
        "<clip-path>": "<basic-shape> || <geometry-box>",
 | 
						|
 | 
						|
        "<dasharray>":
 | 
						|
        // "list of comma and/or white space separated <length>s and
 | 
						|
        // <percentage>s".  There is a non-negative constraint.
 | 
						|
        Matcher.cast("<nonnegative-length-or-percentage>")
 | 
						|
            .braces(1, Infinity, "#", Matcher.cast(",").question()),
 | 
						|
 | 
						|
        "<family-name>":
 | 
						|
            // <string> | <IDENT>+
 | 
						|
            "<string> | <ident-not-generic-family> <ident>*",
 | 
						|
 | 
						|
        "<filter-function-list>": "[ <filter-function> | <uri> ]+",
 | 
						|
 | 
						|
        // https://www.w3.org/TR/2014/WD-css-flexbox-1-20140325/#flex-property
 | 
						|
        "<flex>":
 | 
						|
            "none | [ <flex-grow> <flex-shrink>? || <flex-basis> ]",
 | 
						|
 | 
						|
        "<font-family>": "[ <generic-family> | <family-name> ]#",
 | 
						|
 | 
						|
        "<font-shorthand>":
 | 
						|
            "[ <font-style> || <font-variant-css21> || " +
 | 
						|
            "<font-weight> || <font-stretch> ]? <font-size> " +
 | 
						|
            "[ / <line-height> ]? <font-family>",
 | 
						|
 | 
						|
        "<font-variant-alternates>":
 | 
						|
            // stylistic(<feature-value-name>)
 | 
						|
            "stylistic() || " +
 | 
						|
            "historical-forms || " +
 | 
						|
            // styleset(<feature-value-name> #)
 | 
						|
            "styleset() || " +
 | 
						|
            // character-variant(<feature-value-name> #)
 | 
						|
            "character-variant() || " +
 | 
						|
            // swash(<feature-value-name>)
 | 
						|
            "swash() || " +
 | 
						|
            // ornaments(<feature-value-name>)
 | 
						|
            "ornaments() || " +
 | 
						|
            // annotation(<feature-value-name>)
 | 
						|
            "annotation()",
 | 
						|
 | 
						|
        "<font-variant-ligatures>":
 | 
						|
            // <common-lig-values>
 | 
						|
            "[ common-ligatures | no-common-ligatures ] || " +
 | 
						|
            // <discretionary-lig-values>
 | 
						|
            "[ discretionary-ligatures | no-discretionary-ligatures ] || " +
 | 
						|
            // <historical-lig-values>
 | 
						|
            "[ historical-ligatures | no-historical-ligatures ] || " +
 | 
						|
            // <contextual-alt-values>
 | 
						|
            "[ contextual | no-contextual ]",
 | 
						|
 | 
						|
        "<font-variant-numeric>":
 | 
						|
            // <numeric-figure-values>
 | 
						|
            "[ lining-nums | oldstyle-nums ] || " +
 | 
						|
            // <numeric-spacing-values>
 | 
						|
            "[ proportional-nums | tabular-nums ] || " +
 | 
						|
            // <numeric-fraction-values>
 | 
						|
            "[ diagonal-fractions | stacked-fractions ] || " +
 | 
						|
            "ordinal || slashed-zero",
 | 
						|
 | 
						|
        "<font-variant-east-asian>":
 | 
						|
            // <east-asian-variant-values>
 | 
						|
            "[ jis78 | jis83 | jis90 | jis04 | simplified | traditional ] || " +
 | 
						|
            // <east-asian-width-values>
 | 
						|
            "[ full-width | proportional-width ] || " +
 | 
						|
            "ruby",
 | 
						|
 | 
						|
        // Note that <color> here is "as defined in the SVG spec", which
 | 
						|
        // is more restrictive that the <color> defined in the CSS spec.
 | 
						|
        // none | currentColor | <color> [<icccolor>]? |
 | 
						|
        // <funciri> [ none | currentColor | <color> [<icccolor>]? ]?
 | 
						|
        "<paint>": "<paint-basic> | <uri> <paint-basic>?",
 | 
						|
 | 
						|
        // Helper definition for <paint> above.
 | 
						|
        "<paint-basic>": "none | currentColor | <color-svg> <icccolor>?",
 | 
						|
 | 
						|
        "<position>":
 | 
						|
            // Because our `alt` combinator is ordered, we need to test these
 | 
						|
            // in order from longest possible match to shortest.
 | 
						|
            "[ center | [ left | right ] [ <percentage> | <length> ]? ] && " +
 | 
						|
            "[ center | [ top | bottom ] [ <percentage> | <length> ]? ]" +
 | 
						|
            " | " +
 | 
						|
            "[ left | center | right | <percentage> | <length> ] " +
 | 
						|
            "[ top | center | bottom | <percentage> | <length> ]" +
 | 
						|
            " | " +
 | 
						|
            "[ left | center | right | top | bottom | <percentage> | <length> ]",
 | 
						|
 | 
						|
        "<repeat-style>":
 | 
						|
            "repeat-x | repeat-y | [ repeat | space | round | no-repeat ]{1,2}",
 | 
						|
 | 
						|
        "<shadow>":
 | 
						|
        //inset? && [ <length>{2,4} && <color>? ]
 | 
						|
        Matcher.many([true /* length is required */],
 | 
						|
                     Matcher.cast("<length>").braces(2, 4), "inset", "<color>"),
 | 
						|
 | 
						|
        "<text-decoration-color>":
 | 
						|
           "<color>",
 | 
						|
 | 
						|
        "<text-decoration-line>":
 | 
						|
            "none | [ underline || overline || line-through || blink ]",
 | 
						|
 | 
						|
        "<text-decoration-style>":
 | 
						|
            "solid | double | dotted | dashed | wavy",
 | 
						|
 | 
						|
        "<will-change>":
 | 
						|
            "auto | <animateable-feature>#",
 | 
						|
 | 
						|
        "<x-one-radius>":
 | 
						|
            //[ <length> | <percentage> ] [ <length> | <percentage> ]?
 | 
						|
            "[ <length> | <percentage> ]{1,2}"
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
Object.keys(ValidationTypes.simple).forEach(function(nt) {
 | 
						|
    var rule = ValidationTypes.simple[nt];
 | 
						|
    if (typeof rule === "string") {
 | 
						|
        ValidationTypes.simple[nt] = function(part) {
 | 
						|
            return ValidationTypes.isLiteral(part, rule);
 | 
						|
        };
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
Object.keys(ValidationTypes.complex).forEach(function(nt) {
 | 
						|
    var rule = ValidationTypes.complex[nt];
 | 
						|
    if (typeof rule === "string") {
 | 
						|
        ValidationTypes.complex[nt] = Matcher.parse(rule);
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
// Because this is defined relative to other complex validation types,
 | 
						|
// we need to define it *after* the rest of the types are initialized.
 | 
						|
ValidationTypes.complex["<font-variant>"] =
 | 
						|
    Matcher.oror({ expand: "<font-variant-ligatures>" },
 | 
						|
                 { expand: "<font-variant-alternates>" },
 | 
						|
                 "<font-variant-caps>",
 | 
						|
                 { expand: "<font-variant-numeric>" },
 | 
						|
                 { expand: "<font-variant-east-asian>" });
 | 
						|
 | 
						|
},{"./Matcher":3}],22:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = {
 | 
						|
    Colors            : require("./Colors"),
 | 
						|
    Combinator        : require("./Combinator"),
 | 
						|
    Parser            : require("./Parser"),
 | 
						|
    PropertyName      : require("./PropertyName"),
 | 
						|
    PropertyValue     : require("./PropertyValue"),
 | 
						|
    PropertyValuePart : require("./PropertyValuePart"),
 | 
						|
    Matcher           : require("./Matcher"),
 | 
						|
    MediaFeature      : require("./MediaFeature"),
 | 
						|
    MediaQuery        : require("./MediaQuery"),
 | 
						|
    Selector          : require("./Selector"),
 | 
						|
    SelectorPart      : require("./SelectorPart"),
 | 
						|
    SelectorSubPart   : require("./SelectorSubPart"),
 | 
						|
    Specificity       : require("./Specificity"),
 | 
						|
    TokenStream       : require("./TokenStream"),
 | 
						|
    Tokens            : require("./Tokens"),
 | 
						|
    ValidationError   : require("./ValidationError")
 | 
						|
};
 | 
						|
 | 
						|
},{"./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){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = EventTarget;
 | 
						|
 | 
						|
/**
 | 
						|
 * A generic base to inherit from for any object
 | 
						|
 * that needs event handling.
 | 
						|
 * @class EventTarget
 | 
						|
 * @constructor
 | 
						|
 */
 | 
						|
function EventTarget() {
 | 
						|
 | 
						|
    /**
 | 
						|
     * The array of listeners for various events.
 | 
						|
     * @type Object
 | 
						|
     * @property _listeners
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    this._listeners = Object.create(null);
 | 
						|
}
 | 
						|
 | 
						|
EventTarget.prototype = {
 | 
						|
 | 
						|
    //restore constructor
 | 
						|
    constructor: EventTarget,
 | 
						|
 | 
						|
    /**
 | 
						|
     * Adds a listener for a given event type.
 | 
						|
     * @param {String} type The type of event to add a listener for.
 | 
						|
     * @param {Function} listener The function to call when the event occurs.
 | 
						|
     * @return {void}
 | 
						|
     * @method addListener
 | 
						|
     */
 | 
						|
    addListener: function(type, listener) {
 | 
						|
        if (!this._listeners[type]) {
 | 
						|
            this._listeners[type] = [];
 | 
						|
        }
 | 
						|
 | 
						|
        this._listeners[type].push(listener);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Fires an event based on the passed-in object.
 | 
						|
     * @param {Object|String} event An object with at least a 'type' attribute
 | 
						|
     *      or a string indicating the event name.
 | 
						|
     * @return {void}
 | 
						|
     * @method fire
 | 
						|
     */
 | 
						|
    fire: function(event) {
 | 
						|
        if (typeof event === "string") {
 | 
						|
            event = { type: event };
 | 
						|
        }
 | 
						|
        if (typeof event.target !== "undefined") {
 | 
						|
            event.target = this;
 | 
						|
        }
 | 
						|
 | 
						|
        if (typeof event.type === "undefined") {
 | 
						|
            throw new Error("Event object missing 'type' property.");
 | 
						|
        }
 | 
						|
 | 
						|
        if (this._listeners[event.type]) {
 | 
						|
 | 
						|
            //create a copy of the array and use that so listeners can't chane
 | 
						|
            var listeners = this._listeners[event.type].concat();
 | 
						|
            for (var i=0, len=listeners.length; i < len; i++) {
 | 
						|
                listeners[i].call(this, event);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Removes a listener for a given event type.
 | 
						|
     * @param {String} type The type of event to remove a listener from.
 | 
						|
     * @param {Function} listener The function to remove from the event.
 | 
						|
     * @return {void}
 | 
						|
     * @method removeListener
 | 
						|
     */
 | 
						|
    removeListener: function(type, listener) {
 | 
						|
        if (this._listeners[type]) {
 | 
						|
            var listeners = this._listeners[type];
 | 
						|
            for (var i=0, len=listeners.length; i < len; i++) {
 | 
						|
                if (listeners[i] === listener) {
 | 
						|
                    listeners.splice(i, 1);
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
 | 
						|
        }
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
},{}],24:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = StringReader;
 | 
						|
 | 
						|
/**
 | 
						|
 * Convenient way to read through strings.
 | 
						|
 * @namespace parserlib.util
 | 
						|
 * @class StringReader
 | 
						|
 * @constructor
 | 
						|
 * @param {String} text The text to read.
 | 
						|
 */
 | 
						|
function StringReader(text) {
 | 
						|
 | 
						|
    /**
 | 
						|
     * The input text with line endings normalized.
 | 
						|
     * @property _input
 | 
						|
     * @type String
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    this._input = text.replace(/(\r\n?|\n)/g, "\n");
 | 
						|
 | 
						|
 | 
						|
    /**
 | 
						|
     * The row for the character to be read next.
 | 
						|
     * @property _line
 | 
						|
     * @type int
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    this._line = 1;
 | 
						|
 | 
						|
 | 
						|
    /**
 | 
						|
     * The column for the character to be read next.
 | 
						|
     * @property _col
 | 
						|
     * @type int
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    this._col = 1;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The index of the character in the input to be read next.
 | 
						|
     * @property _cursor
 | 
						|
     * @type int
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    this._cursor = 0;
 | 
						|
}
 | 
						|
 | 
						|
StringReader.prototype = {
 | 
						|
 | 
						|
    // restore constructor
 | 
						|
    constructor: StringReader,
 | 
						|
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
    // Position info
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the column of the character to be read next.
 | 
						|
     * @return {int} The column of the character to be read next.
 | 
						|
     * @method getCol
 | 
						|
     */
 | 
						|
    getCol: function() {
 | 
						|
        return this._col;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the row of the character to be read next.
 | 
						|
     * @return {int} The row of the character to be read next.
 | 
						|
     * @method getLine
 | 
						|
     */
 | 
						|
    getLine: function() {
 | 
						|
        return this._line;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Determines if you're at the end of the input.
 | 
						|
     * @return {Boolean} True if there's no more input, false otherwise.
 | 
						|
     * @method eof
 | 
						|
     */
 | 
						|
    eof: function() {
 | 
						|
        return this._cursor === this._input.length;
 | 
						|
    },
 | 
						|
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
    // Basic reading
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
 | 
						|
    /**
 | 
						|
     * Reads the next character without advancing the cursor.
 | 
						|
     * @param {int} count How many characters to look ahead (default is 1).
 | 
						|
     * @return {String} The next character or null if there is no next character.
 | 
						|
     * @method peek
 | 
						|
     */
 | 
						|
    peek: function(count) {
 | 
						|
        var c = null;
 | 
						|
        count = typeof count === "undefined" ? 1 : count;
 | 
						|
 | 
						|
        // if we're not at the end of the input...
 | 
						|
        if (this._cursor < this._input.length) {
 | 
						|
 | 
						|
            // get character and increment cursor and column
 | 
						|
            c = this._input.charAt(this._cursor + count - 1);
 | 
						|
        }
 | 
						|
 | 
						|
        return c;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Reads the next character from the input and adjusts the row and column
 | 
						|
     * accordingly.
 | 
						|
     * @return {String} The next character or null if there is no next character.
 | 
						|
     * @method read
 | 
						|
     */
 | 
						|
    read: function() {
 | 
						|
        var c = null;
 | 
						|
 | 
						|
        // if we're not at the end of the input...
 | 
						|
        if (this._cursor < this._input.length) {
 | 
						|
 | 
						|
            // if the last character was a newline, increment row count
 | 
						|
            // and reset column count
 | 
						|
            if (this._input.charAt(this._cursor) === "\n") {
 | 
						|
                this._line++;
 | 
						|
                this._col=1;
 | 
						|
            } else {
 | 
						|
                this._col++;
 | 
						|
            }
 | 
						|
 | 
						|
            // get character and increment cursor and column
 | 
						|
            c = this._input.charAt(this._cursor++);
 | 
						|
        }
 | 
						|
 | 
						|
        return c;
 | 
						|
    },
 | 
						|
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
    // Misc
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
 | 
						|
    /**
 | 
						|
     * Saves the current location so it can be returned to later.
 | 
						|
     * @method mark
 | 
						|
     * @return {void}
 | 
						|
     */
 | 
						|
    mark: function() {
 | 
						|
        this._bookmark = {
 | 
						|
            cursor: this._cursor,
 | 
						|
            line:   this._line,
 | 
						|
            col:    this._col
 | 
						|
        };
 | 
						|
    },
 | 
						|
 | 
						|
    reset: function() {
 | 
						|
        if (this._bookmark) {
 | 
						|
            this._cursor = this._bookmark.cursor;
 | 
						|
            this._line = this._bookmark.line;
 | 
						|
            this._col = this._bookmark.col;
 | 
						|
            delete this._bookmark;
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
    // Advanced reading
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
 | 
						|
    /**
 | 
						|
     * Reads up to and including the given string. Throws an error if that
 | 
						|
     * string is not found.
 | 
						|
     * @param {String} pattern The string to read.
 | 
						|
     * @return {String} The string when it is found.
 | 
						|
     * @throws Error when the string pattern is not found.
 | 
						|
     * @method readTo
 | 
						|
     */
 | 
						|
    readTo: function(pattern) {
 | 
						|
 | 
						|
        var buffer = "",
 | 
						|
            c;
 | 
						|
 | 
						|
        /*
 | 
						|
         * First, buffer must be the same length as the pattern.
 | 
						|
         * Then, buffer must end with the pattern or else reach the
 | 
						|
         * end of the input.
 | 
						|
         */
 | 
						|
        while (buffer.length < pattern.length || buffer.lastIndexOf(pattern) !== buffer.length - pattern.length) {
 | 
						|
            c = this.read();
 | 
						|
            if (c) {
 | 
						|
                buffer += c;
 | 
						|
            } else {
 | 
						|
                throw new Error("Expected \"" + pattern + "\" at line " + this._line  + ", col " + this._col + ".");
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return buffer;
 | 
						|
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Reads characters while each character causes the given
 | 
						|
     * filter function to return true. The function is passed
 | 
						|
     * in each character and either returns true to continue
 | 
						|
     * reading or false to stop.
 | 
						|
     * @param {Function} filter The function to read on each character.
 | 
						|
     * @return {String} The string made up of all characters that passed the
 | 
						|
     *      filter check.
 | 
						|
     * @method readWhile
 | 
						|
     */
 | 
						|
    readWhile: function(filter) {
 | 
						|
 | 
						|
        var buffer = "",
 | 
						|
            c = this.peek();
 | 
						|
 | 
						|
        while (c !== null && filter(c)) {
 | 
						|
            buffer += this.read();
 | 
						|
            c = this.peek();
 | 
						|
        }
 | 
						|
 | 
						|
        return buffer;
 | 
						|
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Reads characters that match either text or a regular expression and
 | 
						|
     * returns those characters. If a match is found, the row and column
 | 
						|
     * are adjusted; if no match is found, the reader's state is unchanged.
 | 
						|
     * reading or false to stop.
 | 
						|
     * @param {String|RegExp} matcher If a string, then the literal string
 | 
						|
     *      value is searched for. If a regular expression, then any string
 | 
						|
     *      matching the pattern is search for.
 | 
						|
     * @return {String} The string made up of all characters that matched or
 | 
						|
     *      null if there was no match.
 | 
						|
     * @method readMatch
 | 
						|
     */
 | 
						|
    readMatch: function(matcher) {
 | 
						|
 | 
						|
        var source = this._input.substring(this._cursor),
 | 
						|
            value = null;
 | 
						|
 | 
						|
        // if it's a string, just do a straight match
 | 
						|
        if (typeof matcher === "string") {
 | 
						|
            if (source.slice(0, matcher.length) === matcher) {
 | 
						|
                value = this.readCount(matcher.length);
 | 
						|
            }
 | 
						|
        } else if (matcher instanceof RegExp) {
 | 
						|
            if (matcher.test(source)) {
 | 
						|
                value = this.readCount(RegExp.lastMatch.length);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return value;
 | 
						|
    },
 | 
						|
 | 
						|
 | 
						|
    /**
 | 
						|
     * Reads a given number of characters. If the end of the input is reached,
 | 
						|
     * it reads only the remaining characters and does not throw an error.
 | 
						|
     * @param {int} count The number of characters to read.
 | 
						|
     * @return {String} The string made up the read characters.
 | 
						|
     * @method readCount
 | 
						|
     */
 | 
						|
    readCount: function(count) {
 | 
						|
        var buffer = "";
 | 
						|
 | 
						|
        while (count--) {
 | 
						|
            buffer += this.read();
 | 
						|
        }
 | 
						|
 | 
						|
        return buffer;
 | 
						|
    }
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
},{}],25:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = SyntaxError;
 | 
						|
 | 
						|
/**
 | 
						|
 * Type to use when a syntax error occurs.
 | 
						|
 * @class SyntaxError
 | 
						|
 * @namespace parserlib.util
 | 
						|
 * @constructor
 | 
						|
 * @param {String} message The error message.
 | 
						|
 * @param {int} line The line at which the error occurred.
 | 
						|
 * @param {int} col The column at which the error occurred.
 | 
						|
 */
 | 
						|
function SyntaxError(message, line, col) {
 | 
						|
    Error.call(this);
 | 
						|
    this.name = this.constructor.name;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The column at which the error occurred.
 | 
						|
     * @type int
 | 
						|
     * @property col
 | 
						|
     */
 | 
						|
    this.col = col;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The line at which the error occurred.
 | 
						|
     * @type int
 | 
						|
     * @property line
 | 
						|
     */
 | 
						|
    this.line = line;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The text representation of the unit.
 | 
						|
     * @type String
 | 
						|
     * @property text
 | 
						|
     */
 | 
						|
    this.message = message;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
//inherit from Error
 | 
						|
SyntaxError.prototype = Object.create(Error.prototype); // jshint ignore:line
 | 
						|
SyntaxError.prototype.constructor = SyntaxError; // jshint ignore:line
 | 
						|
 | 
						|
},{}],26:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = SyntaxUnit;
 | 
						|
 | 
						|
/**
 | 
						|
 * Base type to represent a single syntactic unit.
 | 
						|
 * @class SyntaxUnit
 | 
						|
 * @namespace parserlib.util
 | 
						|
 * @constructor
 | 
						|
 * @param {String} text The text of the unit.
 | 
						|
 * @param {int} line The line of text on which the unit resides.
 | 
						|
 * @param {int} col The column of text on which the unit resides.
 | 
						|
 */
 | 
						|
function SyntaxUnit(text, line, col, type) {
 | 
						|
 | 
						|
 | 
						|
    /**
 | 
						|
     * The column of text on which the unit resides.
 | 
						|
     * @type int
 | 
						|
     * @property col
 | 
						|
     */
 | 
						|
    this.col = col;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The line of text on which the unit resides.
 | 
						|
     * @type int
 | 
						|
     * @property line
 | 
						|
     */
 | 
						|
    this.line = line;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The text representation of the unit.
 | 
						|
     * @type String
 | 
						|
     * @property text
 | 
						|
     */
 | 
						|
    this.text = text;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The type of syntax unit.
 | 
						|
     * @type int
 | 
						|
     * @property type
 | 
						|
     */
 | 
						|
    this.type = type;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Create a new syntax unit based solely on the given token.
 | 
						|
 * Convenience method for creating a new syntax unit when
 | 
						|
 * it represents a single token instead of multiple.
 | 
						|
 * @param {Object} token The token object to represent.
 | 
						|
 * @return {parserlib.util.SyntaxUnit} The object representing the token.
 | 
						|
 * @static
 | 
						|
 * @method fromToken
 | 
						|
 */
 | 
						|
SyntaxUnit.fromToken = function(token) {
 | 
						|
    return new SyntaxUnit(token.value, token.startLine, token.startCol);
 | 
						|
};
 | 
						|
 | 
						|
SyntaxUnit.prototype = {
 | 
						|
 | 
						|
    //restore constructor
 | 
						|
    constructor: SyntaxUnit,
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the text representation of the unit.
 | 
						|
     * @return {String} The text representation of the unit.
 | 
						|
     * @method valueOf
 | 
						|
     */
 | 
						|
    valueOf: function() {
 | 
						|
        return this.toString();
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the text representation of the unit.
 | 
						|
     * @return {String} The text representation of the unit.
 | 
						|
     * @method toString
 | 
						|
     */
 | 
						|
    toString: function() {
 | 
						|
        return this.text;
 | 
						|
    }
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
},{}],27:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = TokenStreamBase;
 | 
						|
 | 
						|
var StringReader = require("./StringReader");
 | 
						|
var SyntaxError = require("./SyntaxError");
 | 
						|
 | 
						|
/**
 | 
						|
 * Generic TokenStream providing base functionality.
 | 
						|
 * @class TokenStreamBase
 | 
						|
 * @namespace parserlib.util
 | 
						|
 * @constructor
 | 
						|
 * @param {String|StringReader} input The text to tokenize or a reader from
 | 
						|
 *      which to read the input.
 | 
						|
 */
 | 
						|
function TokenStreamBase(input, tokenData) {
 | 
						|
 | 
						|
    /**
 | 
						|
     * The string reader for easy access to the text.
 | 
						|
     * @type StringReader
 | 
						|
     * @property _reader
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    this._reader = new StringReader(input ? input.toString() : "");
 | 
						|
 | 
						|
    /**
 | 
						|
     * Token object for the last consumed token.
 | 
						|
     * @type Token
 | 
						|
     * @property _token
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    this._token = null;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The array of token information.
 | 
						|
     * @type Array
 | 
						|
     * @property _tokenData
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    this._tokenData = tokenData;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Lookahead token buffer.
 | 
						|
     * @type Array
 | 
						|
     * @property _lt
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    this._lt = [];
 | 
						|
 | 
						|
    /**
 | 
						|
     * Lookahead token buffer index.
 | 
						|
     * @type int
 | 
						|
     * @property _ltIndex
 | 
						|
     * @private
 | 
						|
     */
 | 
						|
    this._ltIndex = 0;
 | 
						|
 | 
						|
    this._ltIndexCache = [];
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Accepts an array of token information and outputs
 | 
						|
 * an array of token data containing key-value mappings
 | 
						|
 * and matching functions that the TokenStream needs.
 | 
						|
 * @param {Array} tokens An array of token descriptors.
 | 
						|
 * @return {Array} An array of processed token data.
 | 
						|
 * @method createTokenData
 | 
						|
 * @static
 | 
						|
 */
 | 
						|
TokenStreamBase.createTokenData = function(tokens) {
 | 
						|
 | 
						|
    var nameMap     = [],
 | 
						|
        typeMap     = Object.create(null),
 | 
						|
        tokenData     = tokens.concat([]),
 | 
						|
        i            = 0,
 | 
						|
        len            = tokenData.length+1;
 | 
						|
 | 
						|
    tokenData.UNKNOWN = -1;
 | 
						|
    tokenData.unshift({ name:"EOF" });
 | 
						|
 | 
						|
    for (; i < len; i++) {
 | 
						|
        nameMap.push(tokenData[i].name);
 | 
						|
        tokenData[tokenData[i].name] = i;
 | 
						|
        if (tokenData[i].text) {
 | 
						|
            typeMap[tokenData[i].text] = i;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    tokenData.name = function(tt) {
 | 
						|
        return nameMap[tt];
 | 
						|
    };
 | 
						|
 | 
						|
    tokenData.type = function(c) {
 | 
						|
        return typeMap[c];
 | 
						|
    };
 | 
						|
 | 
						|
    return tokenData;
 | 
						|
};
 | 
						|
 | 
						|
TokenStreamBase.prototype = {
 | 
						|
 | 
						|
    //restore constructor
 | 
						|
    constructor: TokenStreamBase,
 | 
						|
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
    // Matching methods
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
 | 
						|
    /**
 | 
						|
     * Determines if the next token matches the given token type.
 | 
						|
     * If so, that token is consumed; if not, the token is placed
 | 
						|
     * back onto the token stream. You can pass in any number of
 | 
						|
     * token types and this will return true if any of the token
 | 
						|
     * types is found.
 | 
						|
     * @param {int|int[]} tokenTypes Either a single token type or an array of
 | 
						|
     *      token types that the next token might be. If an array is passed,
 | 
						|
     *      it's assumed that the token can be any of these.
 | 
						|
     * @param {variant} channel (Optional) The channel to read from. If not
 | 
						|
     *      provided, reads from the default (unnamed) channel.
 | 
						|
     * @return {Boolean} True if the token type matches, false if not.
 | 
						|
     * @method match
 | 
						|
     */
 | 
						|
    match: function(tokenTypes, channel) {
 | 
						|
 | 
						|
        //always convert to an array, makes things easier
 | 
						|
        if (!(tokenTypes instanceof Array)) {
 | 
						|
            tokenTypes = [tokenTypes];
 | 
						|
        }
 | 
						|
 | 
						|
        var tt  = this.get(channel),
 | 
						|
            i   = 0,
 | 
						|
            len = tokenTypes.length;
 | 
						|
 | 
						|
        while (i < len) {
 | 
						|
            if (tt === tokenTypes[i++]) {
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        //no match found, put the token back
 | 
						|
        this.unget();
 | 
						|
        return false;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Determines if the next token matches the given token type.
 | 
						|
     * If so, that token is consumed; if not, an error is thrown.
 | 
						|
     * @param {int|int[]} tokenTypes Either a single token type or an array of
 | 
						|
     *      token types that the next token should be. If an array is passed,
 | 
						|
     *      it's assumed that the token must be one of these.
 | 
						|
     * @return {void}
 | 
						|
     * @method mustMatch
 | 
						|
     */
 | 
						|
    mustMatch: function(tokenTypes) {
 | 
						|
 | 
						|
        var token;
 | 
						|
 | 
						|
        //always convert to an array, makes things easier
 | 
						|
        if (!(tokenTypes instanceof Array)) {
 | 
						|
            tokenTypes = [tokenTypes];
 | 
						|
        }
 | 
						|
 | 
						|
        if (!this.match.apply(this, arguments)) {
 | 
						|
            token = this.LT(1);
 | 
						|
            throw new SyntaxError("Expected " + this._tokenData[tokenTypes[0]].name +
 | 
						|
                " at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
    // Consuming methods
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
 | 
						|
    /**
 | 
						|
     * Keeps reading from the token stream until either one of the specified
 | 
						|
     * token types is found or until the end of the input is reached.
 | 
						|
     * @param {int|int[]} tokenTypes Either a single token type or an array of
 | 
						|
     *      token types that the next token should be. If an array is passed,
 | 
						|
     *      it's assumed that the token must be one of these.
 | 
						|
     * @param {variant} channel (Optional) The channel to read from. If not
 | 
						|
     *      provided, reads from the default (unnamed) channel.
 | 
						|
     * @return {void}
 | 
						|
     * @method advance
 | 
						|
     */
 | 
						|
    advance: function(tokenTypes, channel) {
 | 
						|
 | 
						|
        while (this.LA(0) !== 0 && !this.match(tokenTypes, channel)) {
 | 
						|
            this.get();
 | 
						|
        }
 | 
						|
 | 
						|
        return this.LA(0);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Consumes the next token from the token stream.
 | 
						|
     * @return {int} The token type of the token that was just consumed.
 | 
						|
     * @method get
 | 
						|
     */
 | 
						|
    get: function(channel) {
 | 
						|
 | 
						|
        var tokenInfo   = this._tokenData,
 | 
						|
            i           =0,
 | 
						|
            token,
 | 
						|
            info;
 | 
						|
 | 
						|
        //check the lookahead buffer first
 | 
						|
        if (this._lt.length && this._ltIndex >= 0 && this._ltIndex < this._lt.length) {
 | 
						|
 | 
						|
            i++;
 | 
						|
            this._token = this._lt[this._ltIndex++];
 | 
						|
            info = tokenInfo[this._token.type];
 | 
						|
 | 
						|
            //obey channels logic
 | 
						|
            while ((info.channel !== undefined && channel !== info.channel) &&
 | 
						|
                    this._ltIndex < this._lt.length) {
 | 
						|
                this._token = this._lt[this._ltIndex++];
 | 
						|
                info = tokenInfo[this._token.type];
 | 
						|
                i++;
 | 
						|
            }
 | 
						|
 | 
						|
            //here be dragons
 | 
						|
            if ((info.channel === undefined || channel === info.channel) &&
 | 
						|
                    this._ltIndex <= this._lt.length) {
 | 
						|
                this._ltIndexCache.push(i);
 | 
						|
                return this._token.type;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        //call token retriever method
 | 
						|
        token = this._getToken();
 | 
						|
 | 
						|
        //if it should be hidden, don't save a token
 | 
						|
        if (token.type > -1 && !tokenInfo[token.type].hide) {
 | 
						|
 | 
						|
            //apply token channel
 | 
						|
            token.channel = tokenInfo[token.type].channel;
 | 
						|
 | 
						|
            //save for later
 | 
						|
            this._token = token;
 | 
						|
            this._lt.push(token);
 | 
						|
 | 
						|
            //save space that will be moved (must be done before array is truncated)
 | 
						|
            this._ltIndexCache.push(this._lt.length - this._ltIndex + i);
 | 
						|
 | 
						|
            //keep the buffer under 5 items
 | 
						|
            if (this._lt.length > 5) {
 | 
						|
                this._lt.shift();
 | 
						|
            }
 | 
						|
 | 
						|
            //also keep the shift buffer under 5 items
 | 
						|
            if (this._ltIndexCache.length > 5) {
 | 
						|
                this._ltIndexCache.shift();
 | 
						|
            }
 | 
						|
 | 
						|
            //update lookahead index
 | 
						|
            this._ltIndex = this._lt.length;
 | 
						|
        }
 | 
						|
 | 
						|
        /*
 | 
						|
         * Skip to the next token if:
 | 
						|
         * 1. The token type is marked as hidden.
 | 
						|
         * 2. The token type has a channel specified and it isn't the current channel.
 | 
						|
         */
 | 
						|
        info = tokenInfo[token.type];
 | 
						|
        if (info &&
 | 
						|
                (info.hide ||
 | 
						|
                (info.channel !== undefined && channel !== info.channel))) {
 | 
						|
            return this.get(channel);
 | 
						|
        } else {
 | 
						|
            //return just the type
 | 
						|
            return token.type;
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Looks ahead a certain number of tokens and returns the token type at
 | 
						|
     * that position. This will throw an error if you lookahead past the
 | 
						|
     * end of input, past the size of the lookahead buffer, or back past
 | 
						|
     * the first token in the lookahead buffer.
 | 
						|
     * @param {int} The index of the token type to retrieve. 0 for the
 | 
						|
     *      current token, 1 for the next, -1 for the previous, etc.
 | 
						|
     * @return {int} The token type of the token in the given position.
 | 
						|
     * @method LA
 | 
						|
     */
 | 
						|
    LA: function(index) {
 | 
						|
        var total = index,
 | 
						|
            tt;
 | 
						|
        if (index > 0) {
 | 
						|
            //TODO: Store 5 somewhere
 | 
						|
            if (index > 5) {
 | 
						|
                throw new Error("Too much lookahead.");
 | 
						|
            }
 | 
						|
 | 
						|
            //get all those tokens
 | 
						|
            while (total) {
 | 
						|
                tt = this.get();
 | 
						|
                total--;
 | 
						|
            }
 | 
						|
 | 
						|
            //unget all those tokens
 | 
						|
            while (total < index) {
 | 
						|
                this.unget();
 | 
						|
                total++;
 | 
						|
            }
 | 
						|
        } else if (index < 0) {
 | 
						|
 | 
						|
            if (this._lt[this._ltIndex+index]) {
 | 
						|
                tt = this._lt[this._ltIndex+index].type;
 | 
						|
            } else {
 | 
						|
                throw new Error("Too much lookbehind.");
 | 
						|
            }
 | 
						|
 | 
						|
        } else {
 | 
						|
            tt = this._token.type;
 | 
						|
        }
 | 
						|
 | 
						|
        return tt;
 | 
						|
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Looks ahead a certain number of tokens and returns the token at
 | 
						|
     * that position. This will throw an error if you lookahead past the
 | 
						|
     * end of input, past the size of the lookahead buffer, or back past
 | 
						|
     * the first token in the lookahead buffer.
 | 
						|
     * @param {int} The index of the token type to retrieve. 0 for the
 | 
						|
     *      current token, 1 for the next, -1 for the previous, etc.
 | 
						|
     * @return {Object} The token of the token in the given position.
 | 
						|
     * @method LA
 | 
						|
     */
 | 
						|
    LT: function(index) {
 | 
						|
 | 
						|
        //lookahead first to prime the token buffer
 | 
						|
        this.LA(index);
 | 
						|
 | 
						|
        //now find the token, subtract one because _ltIndex is already at the next index
 | 
						|
        return this._lt[this._ltIndex+index-1];
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the token type for the next token in the stream without
 | 
						|
     * consuming it.
 | 
						|
     * @return {int} The token type of the next token in the stream.
 | 
						|
     * @method peek
 | 
						|
     */
 | 
						|
    peek: function() {
 | 
						|
        return this.LA(1);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the actual token object for the last consumed token.
 | 
						|
     * @return {Token} The token object for the last consumed token.
 | 
						|
     * @method token
 | 
						|
     */
 | 
						|
    token: function() {
 | 
						|
        return this._token;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the name of the token for the given token type.
 | 
						|
     * @param {int} tokenType The type of token to get the name of.
 | 
						|
     * @return {String} The name of the token or "UNKNOWN_TOKEN" for any
 | 
						|
     *      invalid token type.
 | 
						|
     * @method tokenName
 | 
						|
     */
 | 
						|
    tokenName: function(tokenType) {
 | 
						|
        if (tokenType < 0 || tokenType > this._tokenData.length) {
 | 
						|
            return "UNKNOWN_TOKEN";
 | 
						|
        } else {
 | 
						|
            return this._tokenData[tokenType].name;
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the token type value for the given token name.
 | 
						|
     * @param {String} tokenName The name of the token whose value should be returned.
 | 
						|
     * @return {int} The token type value for the given token name or -1
 | 
						|
     *      for an unknown token.
 | 
						|
     * @method tokenName
 | 
						|
     */
 | 
						|
    tokenType: function(tokenName) {
 | 
						|
        return this._tokenData[tokenName] || -1;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the last consumed token to the token stream.
 | 
						|
     * @method unget
 | 
						|
     */
 | 
						|
    unget: function() {
 | 
						|
        //if (this._ltIndex > -1) {
 | 
						|
        if (this._ltIndexCache.length) {
 | 
						|
            this._ltIndex -= this._ltIndexCache.pop();//--;
 | 
						|
            this._token = this._lt[this._ltIndex - 1];
 | 
						|
        } else {
 | 
						|
            throw new Error("Too much lookahead.");
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
},{"./StringReader":24,"./SyntaxError":25}],28:[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = {
 | 
						|
    StringReader    : require("./StringReader"),
 | 
						|
    SyntaxError     : require("./SyntaxError"),
 | 
						|
    SyntaxUnit      : require("./SyntaxUnit"),
 | 
						|
    EventTarget     : require("./EventTarget"),
 | 
						|
    TokenStreamBase : require("./TokenStreamBase")
 | 
						|
};
 | 
						|
 | 
						|
},{"./EventTarget":23,"./StringReader":24,"./SyntaxError":25,"./SyntaxUnit":26,"./TokenStreamBase":27}],"parserlib":[function(require,module,exports){
 | 
						|
"use strict";
 | 
						|
 | 
						|
module.exports = {
 | 
						|
    css  : require("./css"),
 | 
						|
    util : require("./util")
 | 
						|
};
 | 
						|
 | 
						|
},{"./css":22,"./util":28}]},{},[]);
 | 
						|
 | 
						|
return require('parserlib');
 | 
						|
})();
 | 
						|
var clone = (function() {
 | 
						|
'use strict';
 | 
						|
 | 
						|
function _instanceof(obj, type) {
 | 
						|
  return type != null && obj instanceof type;
 | 
						|
}
 | 
						|
 | 
						|
var nativeMap;
 | 
						|
try {
 | 
						|
  nativeMap = Map;
 | 
						|
} catch(_) {
 | 
						|
  // maybe a reference error because no `Map`. Give it a dummy value that no
 | 
						|
  // value will ever be an instanceof.
 | 
						|
  nativeMap = function() {};
 | 
						|
}
 | 
						|
 | 
						|
var nativeSet;
 | 
						|
try {
 | 
						|
  nativeSet = Set;
 | 
						|
} catch(_) {
 | 
						|
  nativeSet = function() {};
 | 
						|
}
 | 
						|
 | 
						|
var nativePromise;
 | 
						|
try {
 | 
						|
  nativePromise = Promise;
 | 
						|
} catch(_) {
 | 
						|
  nativePromise = function() {};
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Clones (copies) an Object using deep copying.
 | 
						|
 *
 | 
						|
 * This function supports circular references by default, but if you are certain
 | 
						|
 * there are no circular references in your object, you can save some CPU time
 | 
						|
 * by calling clone(obj, false).
 | 
						|
 *
 | 
						|
 * Caution: if `circular` is false and `parent` contains circular references,
 | 
						|
 * your program may enter an infinite loop and crash.
 | 
						|
 *
 | 
						|
 * @param `parent` - the object to be cloned
 | 
						|
 * @param `circular` - set to true if the object to be cloned may contain
 | 
						|
 *    circular references. (optional - true by default)
 | 
						|
 * @param `depth` - set to a number if the object is only to be cloned to
 | 
						|
 *    a particular depth. (optional - defaults to Infinity)
 | 
						|
 * @param `prototype` - sets the prototype to be used when cloning an object.
 | 
						|
 *    (optional - defaults to parent prototype).
 | 
						|
 * @param `includeNonEnumerable` - set to true if the non-enumerable properties
 | 
						|
 *    should be cloned as well. Non-enumerable properties on the prototype
 | 
						|
 *    chain will be ignored. (optional - false by default)
 | 
						|
*/
 | 
						|
function clone(parent, circular, depth, prototype, includeNonEnumerable) {
 | 
						|
  if (typeof circular === 'object') {
 | 
						|
    depth = circular.depth;
 | 
						|
    prototype = circular.prototype;
 | 
						|
    includeNonEnumerable = circular.includeNonEnumerable;
 | 
						|
    circular = circular.circular;
 | 
						|
  }
 | 
						|
  // maintain two arrays for circular references, where corresponding parents
 | 
						|
  // and children have the same index
 | 
						|
  var allParents = [];
 | 
						|
  var allChildren = [];
 | 
						|
 | 
						|
  var useBuffer = typeof Buffer != 'undefined';
 | 
						|
 | 
						|
  if (typeof circular == 'undefined')
 | 
						|
    circular = true;
 | 
						|
 | 
						|
  if (typeof depth == 'undefined')
 | 
						|
    depth = Infinity;
 | 
						|
 | 
						|
  // recurse this function so we don't reset allParents and allChildren
 | 
						|
  function _clone(parent, depth) {
 | 
						|
    // cloning null always returns null
 | 
						|
    if (parent === null)
 | 
						|
      return null;
 | 
						|
 | 
						|
    if (depth === 0)
 | 
						|
      return parent;
 | 
						|
 | 
						|
    var child;
 | 
						|
    var proto;
 | 
						|
    if (typeof parent != 'object') {
 | 
						|
      return parent;
 | 
						|
    }
 | 
						|
 | 
						|
    if (_instanceof(parent, nativeMap)) {
 | 
						|
      child = new nativeMap();
 | 
						|
    } else if (_instanceof(parent, nativeSet)) {
 | 
						|
      child = new nativeSet();
 | 
						|
    } else if (_instanceof(parent, nativePromise)) {
 | 
						|
      child = new nativePromise(function (resolve, reject) {
 | 
						|
        parent.then(function(value) {
 | 
						|
          resolve(_clone(value, depth - 1));
 | 
						|
        }, function(err) {
 | 
						|
          reject(_clone(err, depth - 1));
 | 
						|
        });
 | 
						|
      });
 | 
						|
    } else if (clone.__isArray(parent)) {
 | 
						|
      child = [];
 | 
						|
    } else if (clone.__isRegExp(parent)) {
 | 
						|
      child = new RegExp(parent.source, __getRegExpFlags(parent));
 | 
						|
      if (parent.lastIndex) child.lastIndex = parent.lastIndex;
 | 
						|
    } else if (clone.__isDate(parent)) {
 | 
						|
      child = new Date(parent.getTime());
 | 
						|
    } else if (useBuffer && Buffer.isBuffer(parent)) {
 | 
						|
      child = new Buffer(parent.length);
 | 
						|
      parent.copy(child);
 | 
						|
      return child;
 | 
						|
    } else if (_instanceof(parent, Error)) {
 | 
						|
      child = Object.create(parent);
 | 
						|
    } else {
 | 
						|
      if (typeof prototype == 'undefined') {
 | 
						|
        proto = Object.getPrototypeOf(parent);
 | 
						|
        child = Object.create(proto);
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        child = Object.create(prototype);
 | 
						|
        proto = prototype;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (circular) {
 | 
						|
      var index = allParents.indexOf(parent);
 | 
						|
 | 
						|
      if (index != -1) {
 | 
						|
        return allChildren[index];
 | 
						|
      }
 | 
						|
      allParents.push(parent);
 | 
						|
      allChildren.push(child);
 | 
						|
    }
 | 
						|
 | 
						|
    if (_instanceof(parent, nativeMap)) {
 | 
						|
      parent.forEach(function(value, key) {
 | 
						|
        var keyChild = _clone(key, depth - 1);
 | 
						|
        var valueChild = _clone(value, depth - 1);
 | 
						|
        child.set(keyChild, valueChild);
 | 
						|
      });
 | 
						|
    }
 | 
						|
    if (_instanceof(parent, nativeSet)) {
 | 
						|
      parent.forEach(function(value) {
 | 
						|
        var entryChild = _clone(value, depth - 1);
 | 
						|
        child.add(entryChild);
 | 
						|
      });
 | 
						|
    }
 | 
						|
 | 
						|
    for (var i in parent) {
 | 
						|
      var attrs;
 | 
						|
      if (proto) {
 | 
						|
        attrs = Object.getOwnPropertyDescriptor(proto, i);
 | 
						|
      }
 | 
						|
 | 
						|
      if (attrs && attrs.set == null) {
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
      child[i] = _clone(parent[i], depth - 1);
 | 
						|
    }
 | 
						|
 | 
						|
    if (Object.getOwnPropertySymbols) {
 | 
						|
      var symbols = Object.getOwnPropertySymbols(parent);
 | 
						|
      for (var i = 0; i < symbols.length; i++) {
 | 
						|
        // Don't need to worry about cloning a symbol because it is a primitive,
 | 
						|
        // like a number or string.
 | 
						|
        var symbol = symbols[i];
 | 
						|
        var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
 | 
						|
        if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
 | 
						|
          continue;
 | 
						|
        }
 | 
						|
        child[symbol] = _clone(parent[symbol], depth - 1);
 | 
						|
        if (!descriptor.enumerable) {
 | 
						|
          Object.defineProperty(child, symbol, {
 | 
						|
            enumerable: false
 | 
						|
          });
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (includeNonEnumerable) {
 | 
						|
      var allPropertyNames = Object.getOwnPropertyNames(parent);
 | 
						|
      for (var i = 0; i < allPropertyNames.length; i++) {
 | 
						|
        var propertyName = allPropertyNames[i];
 | 
						|
        var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
 | 
						|
        if (descriptor && descriptor.enumerable) {
 | 
						|
          continue;
 | 
						|
        }
 | 
						|
        child[propertyName] = _clone(parent[propertyName], depth - 1);
 | 
						|
        Object.defineProperty(child, propertyName, {
 | 
						|
          enumerable: false
 | 
						|
        });
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return child;
 | 
						|
  }
 | 
						|
 | 
						|
  return _clone(parent, depth);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Simple flat clone using prototype, accepts only objects, usefull for property
 | 
						|
 * override on FLAT configuration object (no nested props).
 | 
						|
 *
 | 
						|
 * USE WITH CAUTION! This may not behave as you wish if you do not know how this
 | 
						|
 * works.
 | 
						|
 */
 | 
						|
clone.clonePrototype = function clonePrototype(parent) {
 | 
						|
  if (parent === null)
 | 
						|
    return null;
 | 
						|
 | 
						|
  var c = function () {};
 | 
						|
  c.prototype = parent;
 | 
						|
  return new c();
 | 
						|
};
 | 
						|
 | 
						|
// private utility functions
 | 
						|
 | 
						|
function __objToStr(o) {
 | 
						|
  return Object.prototype.toString.call(o);
 | 
						|
}
 | 
						|
clone.__objToStr = __objToStr;
 | 
						|
 | 
						|
function __isDate(o) {
 | 
						|
  return typeof o === 'object' && __objToStr(o) === '[object Date]';
 | 
						|
}
 | 
						|
clone.__isDate = __isDate;
 | 
						|
 | 
						|
function __isArray(o) {
 | 
						|
  return typeof o === 'object' && __objToStr(o) === '[object Array]';
 | 
						|
}
 | 
						|
clone.__isArray = __isArray;
 | 
						|
 | 
						|
function __isRegExp(o) {
 | 
						|
  return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
 | 
						|
}
 | 
						|
clone.__isRegExp = __isRegExp;
 | 
						|
 | 
						|
function __getRegExpFlags(re) {
 | 
						|
  var flags = '';
 | 
						|
  if (re.global) flags += 'g';
 | 
						|
  if (re.ignoreCase) flags += 'i';
 | 
						|
  if (re.multiline) flags += 'm';
 | 
						|
  return flags;
 | 
						|
}
 | 
						|
clone.__getRegExpFlags = __getRegExpFlags;
 | 
						|
 | 
						|
return clone;
 | 
						|
})();
 | 
						|
 | 
						|
if (typeof module === 'object' && module.exports) {
 | 
						|
  module.exports = clone;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Main CSSLint object.
 | 
						|
 * @class CSSLint
 | 
						|
 * @static
 | 
						|
 * @extends parserlib.util.EventTarget
 | 
						|
 */
 | 
						|
 | 
						|
/* global parserlib, clone, Reporter */
 | 
						|
/* exported CSSLint */
 | 
						|
 | 
						|
var CSSLint = (function() {
 | 
						|
    "use strict";
 | 
						|
 | 
						|
    var rules           = [],
 | 
						|
        formatters      = [],
 | 
						|
        embeddedRuleset = /\/\*\s*csslint([^\*]*)\*\//,
 | 
						|
        api             = new parserlib.util.EventTarget();
 | 
						|
 | 
						|
    api.version = "1.0.5";
 | 
						|
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
    // Rule Management
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
 | 
						|
    /**
 | 
						|
     * Adds a new rule to the engine.
 | 
						|
     * @param {Object} rule The rule to add.
 | 
						|
     * @method addRule
 | 
						|
     */
 | 
						|
    api.addRule = function(rule) {
 | 
						|
        rules.push(rule);
 | 
						|
        rules[rule.id] = rule;
 | 
						|
    };
 | 
						|
 | 
						|
    /**
 | 
						|
     * Clears all rule from the engine.
 | 
						|
     * @method clearRules
 | 
						|
     */
 | 
						|
    api.clearRules = function() {
 | 
						|
        rules = [];
 | 
						|
    };
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the rule objects.
 | 
						|
     * @return An array of rule objects.
 | 
						|
     * @method getRules
 | 
						|
     */
 | 
						|
    api.getRules = function() {
 | 
						|
        return [].concat(rules).sort(function(a, b) {
 | 
						|
            return a.id > b.id ? 1 : 0;
 | 
						|
        });
 | 
						|
    };
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns a ruleset configuration object with all current rules.
 | 
						|
     * @return A ruleset object.
 | 
						|
     * @method getRuleset
 | 
						|
     */
 | 
						|
    api.getRuleset = function() {
 | 
						|
        var ruleset = {},
 | 
						|
            i = 0,
 | 
						|
            len = rules.length;
 | 
						|
 | 
						|
        while (i < len) {
 | 
						|
            ruleset[rules[i++].id] = 1;    // by default, everything is a warning
 | 
						|
        }
 | 
						|
 | 
						|
        return ruleset;
 | 
						|
    };
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns a ruleset object based on embedded rules.
 | 
						|
     * @param {String} text A string of css containing embedded rules.
 | 
						|
     * @param {Object} ruleset A ruleset object to modify.
 | 
						|
     * @return {Object} A ruleset object.
 | 
						|
     * @method getEmbeddedRuleset
 | 
						|
     */
 | 
						|
    function applyEmbeddedRuleset(text, ruleset) {
 | 
						|
        var valueMap,
 | 
						|
            embedded = text && text.match(embeddedRuleset),
 | 
						|
            rules = embedded && embedded[1];
 | 
						|
 | 
						|
        if (rules) {
 | 
						|
            valueMap = {
 | 
						|
                "true": 2,  // true is error
 | 
						|
                "": 1,      // blank is warning
 | 
						|
                "false": 0, // false is ignore
 | 
						|
 | 
						|
                "2": 2,     // explicit error
 | 
						|
                "1": 1,     // explicit warning
 | 
						|
                "0": 0      // explicit ignore
 | 
						|
            };
 | 
						|
 | 
						|
            rules.toLowerCase().split(",").forEach(function(rule) {
 | 
						|
                var pair = rule.split(":"),
 | 
						|
                    property = pair[0] || "",
 | 
						|
                    value = pair[1] || "";
 | 
						|
 | 
						|
                ruleset[property.trim()] = valueMap[value.trim()];
 | 
						|
            });
 | 
						|
        }
 | 
						|
 | 
						|
        return ruleset;
 | 
						|
    }
 | 
						|
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
    // Formatters
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
 | 
						|
    /**
 | 
						|
     * Adds a new formatter to the engine.
 | 
						|
     * @param {Object} formatter The formatter to add.
 | 
						|
     * @method addFormatter
 | 
						|
     */
 | 
						|
    api.addFormatter = function(formatter) {
 | 
						|
        // formatters.push(formatter);
 | 
						|
        formatters[formatter.id] = formatter;
 | 
						|
    };
 | 
						|
 | 
						|
    /**
 | 
						|
     * Retrieves a formatter for use.
 | 
						|
     * @param {String} formatId The name of the format to retrieve.
 | 
						|
     * @return {Object} The formatter or undefined.
 | 
						|
     * @method getFormatter
 | 
						|
     */
 | 
						|
    api.getFormatter = function(formatId) {
 | 
						|
        return formatters[formatId];
 | 
						|
    };
 | 
						|
 | 
						|
    /**
 | 
						|
     * Formats the results in a particular format for a single file.
 | 
						|
     * @param {Object} result The results returned from CSSLint.verify().
 | 
						|
     * @param {String} filename The filename for which the results apply.
 | 
						|
     * @param {String} formatId The name of the formatter to use.
 | 
						|
     * @param {Object} options (Optional) for special output handling.
 | 
						|
     * @return {String} A formatted string for the results.
 | 
						|
     * @method format
 | 
						|
     */
 | 
						|
    api.format = function(results, filename, formatId, options) {
 | 
						|
        var formatter = api.getFormatter(formatId),
 | 
						|
            result = null;
 | 
						|
 | 
						|
        if (formatter) {
 | 
						|
            result = formatter.startFormat();
 | 
						|
            result += formatter.formatResults(results, filename, options || {});
 | 
						|
            result += formatter.endFormat();
 | 
						|
        }
 | 
						|
 | 
						|
        return result;
 | 
						|
    };
 | 
						|
 | 
						|
    /**
 | 
						|
     * Indicates if the given format is supported.
 | 
						|
     * @param {String} formatId The ID of the format to check.
 | 
						|
     * @return {Boolean} True if the format exists, false if not.
 | 
						|
     * @method hasFormat
 | 
						|
     */
 | 
						|
    api.hasFormat = function(formatId) {
 | 
						|
        return formatters.hasOwnProperty(formatId);
 | 
						|
    };
 | 
						|
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
    // Verification
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
 | 
						|
    /**
 | 
						|
     * Starts the verification process for the given CSS text.
 | 
						|
     * @param {String} text The CSS text to verify.
 | 
						|
     * @param {Object} ruleset (Optional) List of rules to apply. If null, then
 | 
						|
     *      all rules are used. If a rule has a value of 1 then it's a warning,
 | 
						|
     *      a value of 2 means it's an error.
 | 
						|
     * @return {Object} Results of the verification.
 | 
						|
     * @method verify
 | 
						|
     */
 | 
						|
    api.verify = function(text, ruleset) {
 | 
						|
 | 
						|
        var i = 0,
 | 
						|
            reporter,
 | 
						|
            lines,
 | 
						|
            allow = {},
 | 
						|
            ignore = [],
 | 
						|
            report,
 | 
						|
            parser = new parserlib.css.Parser({
 | 
						|
                starHack: true,
 | 
						|
                ieFilters: true,
 | 
						|
                underscoreHack: true,
 | 
						|
                strict: false
 | 
						|
            });
 | 
						|
 | 
						|
        // normalize line endings
 | 
						|
        lines = text.replace(/\n\r?/g, "$split$").split("$split$");
 | 
						|
 | 
						|
        // find 'allow' comments
 | 
						|
        CSSLint.Util.forEach(lines, function (line, lineno) {
 | 
						|
            var allowLine = line && line.match(/\/\*[ \t]*csslint[ \t]+allow:[ \t]*([^\*]*)\*\//i),
 | 
						|
                allowRules = allowLine && allowLine[1],
 | 
						|
                allowRuleset = {};
 | 
						|
 | 
						|
            if (allowRules) {
 | 
						|
                allowRules.toLowerCase().split(",").forEach(function(allowRule) {
 | 
						|
                    allowRuleset[allowRule.trim()] = true;
 | 
						|
                });
 | 
						|
                if (Object.keys(allowRuleset).length > 0) {
 | 
						|
                    allow[lineno + 1] = allowRuleset;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        });
 | 
						|
 | 
						|
        var ignoreStart = null,
 | 
						|
            ignoreEnd = null;
 | 
						|
        CSSLint.Util.forEach(lines, function (line, lineno) {
 | 
						|
            // Keep oldest, "unclosest" ignore:start
 | 
						|
            if (ignoreStart === null && line.match(/\/\*[ \t]*csslint[ \t]+ignore:start[ \t]*\*\//i)) {
 | 
						|
                ignoreStart = lineno;
 | 
						|
            }
 | 
						|
 | 
						|
            if (line.match(/\/\*[ \t]*csslint[ \t]+ignore:end[ \t]*\*\//i)) {
 | 
						|
                ignoreEnd = lineno;
 | 
						|
            }
 | 
						|
 | 
						|
            if (ignoreStart !== null && ignoreEnd !== null) {
 | 
						|
                ignore.push([ignoreStart, ignoreEnd]);
 | 
						|
                ignoreStart = ignoreEnd = null;
 | 
						|
            }
 | 
						|
        });
 | 
						|
 | 
						|
        // Close remaining ignore block, if any
 | 
						|
        if (ignoreStart !== null) {
 | 
						|
            ignore.push([ignoreStart, lines.length]);
 | 
						|
        }
 | 
						|
 | 
						|
        if (!ruleset) {
 | 
						|
            ruleset = api.getRuleset();
 | 
						|
        }
 | 
						|
 | 
						|
        if (embeddedRuleset.test(text)) {
 | 
						|
            // defensively copy so that caller's version does not get modified
 | 
						|
            ruleset = clone(ruleset);
 | 
						|
            ruleset = applyEmbeddedRuleset(text, ruleset);
 | 
						|
        }
 | 
						|
 | 
						|
        reporter = new Reporter(lines, ruleset, allow, ignore);
 | 
						|
 | 
						|
        ruleset.errors = 2;       // always report parsing errors as errors
 | 
						|
        for (i in ruleset) {
 | 
						|
            if (ruleset.hasOwnProperty(i) && ruleset[i]) {
 | 
						|
                if (rules[i]) {
 | 
						|
                    rules[i].init(parser, reporter);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        // capture most horrible error type
 | 
						|
        try {
 | 
						|
            parser.parse(text);
 | 
						|
        } catch (ex) {
 | 
						|
            reporter.error("Fatal error, cannot continue: " + ex.message, ex.line, ex.col, {});
 | 
						|
        }
 | 
						|
 | 
						|
        report = {
 | 
						|
            messages    : reporter.messages,
 | 
						|
            stats       : reporter.stats,
 | 
						|
            ruleset     : reporter.ruleset,
 | 
						|
            allow       : reporter.allow,
 | 
						|
            ignore      : reporter.ignore
 | 
						|
        };
 | 
						|
 | 
						|
        // sort by line numbers, rollups at the bottom
 | 
						|
        report.messages.sort(function (a, b) {
 | 
						|
            if (a.rollup && !b.rollup) {
 | 
						|
                return 1;
 | 
						|
            } else if (!a.rollup && b.rollup) {
 | 
						|
                return -1;
 | 
						|
            } else {
 | 
						|
                return a.line - b.line;
 | 
						|
            }
 | 
						|
        });
 | 
						|
 | 
						|
        return report;
 | 
						|
    };
 | 
						|
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
    // Publish the API
 | 
						|
    //-------------------------------------------------------------------------
 | 
						|
 | 
						|
    return api;
 | 
						|
 | 
						|
})();
 | 
						|
 | 
						|
/**
 | 
						|
 * An instance of Report is used to report results of the
 | 
						|
 * verification back to the main API.
 | 
						|
 * @class Reporter
 | 
						|
 * @constructor
 | 
						|
 * @param {String[]} lines The text lines of the source.
 | 
						|
 * @param {Object} ruleset The set of rules to work with, including if
 | 
						|
 *      they are errors or warnings.
 | 
						|
 * @param {Object} explicitly allowed lines
 | 
						|
 * @param {[][]} ingore list of line ranges to be ignored
 | 
						|
 */
 | 
						|
function Reporter(lines, ruleset, allow, ignore) {
 | 
						|
    "use strict";
 | 
						|
 | 
						|
    /**
 | 
						|
     * List of messages being reported.
 | 
						|
     * @property messages
 | 
						|
     * @type String[]
 | 
						|
     */
 | 
						|
    this.messages = [];
 | 
						|
 | 
						|
    /**
 | 
						|
     * List of statistics being reported.
 | 
						|
     * @property stats
 | 
						|
     * @type String[]
 | 
						|
     */
 | 
						|
    this.stats = [];
 | 
						|
 | 
						|
    /**
 | 
						|
     * Lines of code being reported on. Used to provide contextual information
 | 
						|
     * for messages.
 | 
						|
     * @property lines
 | 
						|
     * @type String[]
 | 
						|
     */
 | 
						|
    this.lines = lines;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Information about the rules. Used to determine whether an issue is an
 | 
						|
     * error or warning.
 | 
						|
     * @property ruleset
 | 
						|
     * @type Object
 | 
						|
     */
 | 
						|
    this.ruleset = ruleset;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Lines with specific rule messages to leave out of the report.
 | 
						|
     * @property allow
 | 
						|
     * @type Object
 | 
						|
     */
 | 
						|
    this.allow = allow;
 | 
						|
    if (!this.allow) {
 | 
						|
        this.allow = {};
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Linesets not to include in the report.
 | 
						|
     * @property ignore
 | 
						|
     * @type [][]
 | 
						|
     */
 | 
						|
    this.ignore = ignore;
 | 
						|
    if (!this.ignore) {
 | 
						|
        this.ignore = [];
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
Reporter.prototype = {
 | 
						|
 | 
						|
    // restore constructor
 | 
						|
    constructor: Reporter,
 | 
						|
 | 
						|
    /**
 | 
						|
     * Report an error.
 | 
						|
     * @param {String} message The message to store.
 | 
						|
     * @param {int} line The line number.
 | 
						|
     * @param {int} col The column number.
 | 
						|
     * @param {Object} rule The rule this message relates to.
 | 
						|
     * @method error
 | 
						|
     */
 | 
						|
    error: function(message, line, col, rule) {
 | 
						|
        "use strict";
 | 
						|
        this.messages.push({
 | 
						|
            type    : "error",
 | 
						|
            line    : line,
 | 
						|
            col     : col,
 | 
						|
            message : message,
 | 
						|
            evidence: this.lines[line-1],
 | 
						|
            rule    : rule || {}
 | 
						|
        });
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Report an warning.
 | 
						|
     * @param {String} message The message to store.
 | 
						|
     * @param {int} line The line number.
 | 
						|
     * @param {int} col The column number.
 | 
						|
     * @param {Object} rule The rule this message relates to.
 | 
						|
     * @method warn
 | 
						|
     * @deprecated Use report instead.
 | 
						|
     */
 | 
						|
    warn: function(message, line, col, rule) {
 | 
						|
        "use strict";
 | 
						|
        this.report(message, line, col, rule);
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Report an issue.
 | 
						|
     * @param {String} message The message to store.
 | 
						|
     * @param {int} line The line number.
 | 
						|
     * @param {int} col The column number.
 | 
						|
     * @param {Object} rule The rule this message relates to.
 | 
						|
     * @method report
 | 
						|
     */
 | 
						|
    report: function(message, line, col, rule) {
 | 
						|
        "use strict";
 | 
						|
 | 
						|
        // Check if rule violation should be allowed
 | 
						|
        if (this.allow.hasOwnProperty(line) && this.allow[line].hasOwnProperty(rule.id)) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        var ignore = false;
 | 
						|
        CSSLint.Util.forEach(this.ignore, function (range) {
 | 
						|
            if (range[0] <= line && line <= range[1]) {
 | 
						|
                ignore = true;
 | 
						|
            }
 | 
						|
        });
 | 
						|
        if (ignore) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        this.messages.push({
 | 
						|
            type    : this.ruleset[rule.id] === 2 ? "error" : "warning",
 | 
						|
            line    : line,
 | 
						|
            col     : col,
 | 
						|
            message : message,
 | 
						|
            evidence: this.lines[line-1],
 | 
						|
            rule    : rule
 | 
						|
        });
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Report some informational text.
 | 
						|
     * @param {String} message The message to store.
 | 
						|
     * @param {int} line The line number.
 | 
						|
     * @param {int} col The column number.
 | 
						|
     * @param {Object} rule The rule this message relates to.
 | 
						|
     * @method info
 | 
						|
     */
 | 
						|
    info: function(message, line, col, rule) {
 | 
						|
        "use strict";
 | 
						|
        this.messages.push({
 | 
						|
            type    : "info",
 | 
						|
            line    : line,
 | 
						|
            col     : col,
 | 
						|
            message : message,
 | 
						|
            evidence: this.lines[line-1],
 | 
						|
            rule    : rule
 | 
						|
        });
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Report some rollup error information.
 | 
						|
     * @param {String} message The message to store.
 | 
						|
     * @param {Object} rule The rule this message relates to.
 | 
						|
     * @method rollupError
 | 
						|
     */
 | 
						|
    rollupError: function(message, rule) {
 | 
						|
        "use strict";
 | 
						|
        this.messages.push({
 | 
						|
            type    : "error",
 | 
						|
            rollup  : true,
 | 
						|
            message : message,
 | 
						|
            rule    : rule
 | 
						|
        });
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Report some rollup warning information.
 | 
						|
     * @param {String} message The message to store.
 | 
						|
     * @param {Object} rule The rule this message relates to.
 | 
						|
     * @method rollupWarn
 | 
						|
     */
 | 
						|
    rollupWarn: function(message, rule) {
 | 
						|
        "use strict";
 | 
						|
        this.messages.push({
 | 
						|
            type    : "warning",
 | 
						|
            rollup  : true,
 | 
						|
            message : message,
 | 
						|
            rule    : rule
 | 
						|
        });
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Report a statistic.
 | 
						|
     * @param {String} name The name of the stat to store.
 | 
						|
     * @param {Variant} value The value of the stat.
 | 
						|
     * @method stat
 | 
						|
     */
 | 
						|
    stat: function(name, value) {
 | 
						|
        "use strict";
 | 
						|
        this.stats[name] = value;
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
// expose for testing purposes
 | 
						|
CSSLint._Reporter = Reporter;
 | 
						|
 | 
						|
/*
 | 
						|
 * Utility functions that make life easier.
 | 
						|
 */
 | 
						|
CSSLint.Util = {
 | 
						|
    /*
 | 
						|
     * Adds all properties from supplier onto receiver,
 | 
						|
     * overwriting if the same name already exists on
 | 
						|
     * receiver.
 | 
						|
     * @param {Object} The object to receive the properties.
 | 
						|
     * @param {Object} The object to provide the properties.
 | 
						|
     * @return {Object} The receiver
 | 
						|
     */
 | 
						|
    mix: function(receiver, supplier) {
 | 
						|
        "use strict";
 | 
						|
        var prop;
 | 
						|
 | 
						|
        for (prop in supplier) {
 | 
						|
            if (supplier.hasOwnProperty(prop)) {
 | 
						|
                receiver[prop] = supplier[prop];
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return prop;
 | 
						|
    },
 | 
						|
 | 
						|
    /*
 | 
						|
     * Polyfill for array indexOf() method.
 | 
						|
     * @param {Array} values The array to search.
 | 
						|
     * @param {Variant} value The value to search for.
 | 
						|
     * @return {int} The index of the value if found, -1 if not.
 | 
						|
     */
 | 
						|
    indexOf: function(values, value) {
 | 
						|
        "use strict";
 | 
						|
        if (values.indexOf) {
 | 
						|
            return values.indexOf(value);
 | 
						|
        } else {
 | 
						|
            for (var i=0, len=values.length; i < len; i++) {
 | 
						|
                if (values[i] === value) {
 | 
						|
                    return i;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
    },
 | 
						|
 | 
						|
    /*
 | 
						|
     * Polyfill for array forEach() method.
 | 
						|
     * @param {Array} values The array to operate on.
 | 
						|
     * @param {Function} func The function to call on each item.
 | 
						|
     * @return {void}
 | 
						|
     */
 | 
						|
    forEach: function(values, func) {
 | 
						|
        "use strict";
 | 
						|
        if (values.forEach) {
 | 
						|
            return values.forEach(func);
 | 
						|
        } else {
 | 
						|
            for (var i=0, len=values.length; i < len; i++) {
 | 
						|
                func(values[i], i, values);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Don't use adjoining classes (.foo.bar).
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "adjoining-classes",
 | 
						|
    name: "Disallow adjoining classes",
 | 
						|
    desc: "Don't use adjoining classes.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-adjoining-classes",
 | 
						|
    browsers: "IE6",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this;
 | 
						|
        parser.addListener("startrule", function(event) {
 | 
						|
            var selectors = event.selectors,
 | 
						|
                selector,
 | 
						|
                part,
 | 
						|
                modifier,
 | 
						|
                classCount,
 | 
						|
                i, j, k;
 | 
						|
 | 
						|
            for (i=0; i < selectors.length; i++) {
 | 
						|
                selector = selectors[i];
 | 
						|
                for (j=0; j < selector.parts.length; j++) {
 | 
						|
                    part = selector.parts[j];
 | 
						|
                    if (part.type === parser.SELECTOR_PART_TYPE) {
 | 
						|
                        classCount = 0;
 | 
						|
                        for (k=0; k < part.modifiers.length; k++) {
 | 
						|
                            modifier = part.modifiers[k];
 | 
						|
                            if (modifier.type === "class") {
 | 
						|
                                classCount++;
 | 
						|
                            }
 | 
						|
                            if (classCount > 1){
 | 
						|
                                reporter.report("Adjoining classes: "+selectors[i].text, part.line, part.col, rule);
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Don't use width or height when using padding or border.
 | 
						|
 */
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "box-model",
 | 
						|
    name: "Beware of broken box size",
 | 
						|
    desc: "Don't use width or height when using padding or border.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Beware-of-box-model-size",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this,
 | 
						|
            widthProperties = {
 | 
						|
                border: 1,
 | 
						|
                "border-left": 1,
 | 
						|
                "border-right": 1,
 | 
						|
                padding: 1,
 | 
						|
                "padding-left": 1,
 | 
						|
                "padding-right": 1
 | 
						|
            },
 | 
						|
            heightProperties = {
 | 
						|
                border: 1,
 | 
						|
                "border-bottom": 1,
 | 
						|
                "border-top": 1,
 | 
						|
                padding: 1,
 | 
						|
                "padding-bottom": 1,
 | 
						|
                "padding-top": 1
 | 
						|
            },
 | 
						|
            properties,
 | 
						|
            boxSizing = false;
 | 
						|
 | 
						|
        function startRule() {
 | 
						|
            properties = {};
 | 
						|
            boxSizing = false;
 | 
						|
        }
 | 
						|
 | 
						|
        function endRule() {
 | 
						|
            var prop, value;
 | 
						|
 | 
						|
            if (!boxSizing) {
 | 
						|
                if (properties.height) {
 | 
						|
                    for (prop in heightProperties) {
 | 
						|
                        if (heightProperties.hasOwnProperty(prop) && properties[prop]) {
 | 
						|
                            value = properties[prop].value;
 | 
						|
                            // special case for padding
 | 
						|
                            if (!(prop === "padding" && value.parts.length === 2 && value.parts[0].value === 0)) {
 | 
						|
                                reporter.report("Using height with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                if (properties.width) {
 | 
						|
                    for (prop in widthProperties) {
 | 
						|
                        if (widthProperties.hasOwnProperty(prop) && properties[prop]) {
 | 
						|
                            value = properties[prop].value;
 | 
						|
 | 
						|
                            if (!(prop === "padding" && value.parts.length === 2 && value.parts[1].value === 0)) {
 | 
						|
                                reporter.report("Using width with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        parser.addListener("startrule", startRule);
 | 
						|
        parser.addListener("startfontface", startRule);
 | 
						|
        parser.addListener("startpage", startRule);
 | 
						|
        parser.addListener("startpagemargin", startRule);
 | 
						|
        parser.addListener("startkeyframerule", startRule);
 | 
						|
        parser.addListener("startviewport", startRule);
 | 
						|
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
            var name = event.property.text.toLowerCase();
 | 
						|
 | 
						|
            if (heightProperties[name] || widthProperties[name]) {
 | 
						|
                if (!/^0\S*$/.test(event.value) && !(name === "border" && event.value.toString() === "none")) {
 | 
						|
                    properties[name] = {
 | 
						|
                        line: event.property.line,
 | 
						|
                        col: event.property.col,
 | 
						|
                        value: event.value
 | 
						|
                    };
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                if (/^(width|height)/i.test(name) && /^(length|percentage)/.test(event.value.parts[0].type)) {
 | 
						|
                    properties[name] = 1;
 | 
						|
                } else if (name === "box-sizing") {
 | 
						|
                    boxSizing = true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("endrule", endRule);
 | 
						|
        parser.addListener("endfontface", endRule);
 | 
						|
        parser.addListener("endpage", endRule);
 | 
						|
        parser.addListener("endpagemargin", endRule);
 | 
						|
        parser.addListener("endkeyframerule", endRule);
 | 
						|
        parser.addListener("endviewport", endRule);
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: box-sizing doesn't work in IE6 and IE7.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "box-sizing",
 | 
						|
    name: "Disallow use of box-sizing",
 | 
						|
    desc: "The box-sizing properties isn't supported in IE6 and IE7.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-box-sizing",
 | 
						|
    browsers: "IE6, IE7",
 | 
						|
    tags: ["Compatibility"],
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this;
 | 
						|
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
            var name = event.property.text.toLowerCase();
 | 
						|
 | 
						|
            if (name === "box-sizing") {
 | 
						|
                reporter.report("The box-sizing property isn't supported in IE6 and IE7.", event.line, event.col, rule);
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Use the bulletproof @font-face syntax to avoid 404's in old IE
 | 
						|
 * (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax)
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "bulletproof-font-face",
 | 
						|
    name: "Use the bulletproof @font-face syntax",
 | 
						|
    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).",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Bulletproof-font-face",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this,
 | 
						|
            fontFaceRule = false,
 | 
						|
            firstSrc = true,
 | 
						|
            ruleFailed = false,
 | 
						|
            line, col;
 | 
						|
 | 
						|
        // Mark the start of a @font-face declaration so we only test properties inside it
 | 
						|
        parser.addListener("startfontface", function() {
 | 
						|
            fontFaceRule = true;
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
            // If we aren't inside an @font-face declaration then just return
 | 
						|
            if (!fontFaceRule) {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            var propertyName = event.property.toString().toLowerCase(),
 | 
						|
                value = event.value.toString();
 | 
						|
 | 
						|
            // Set the line and col numbers for use in the endfontface listener
 | 
						|
            line = event.line;
 | 
						|
            col = event.col;
 | 
						|
 | 
						|
            // This is the property that we care about, we can ignore the rest
 | 
						|
            if (propertyName === "src") {
 | 
						|
                var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i;
 | 
						|
 | 
						|
                // We need to handle the advanced syntax with two src properties
 | 
						|
                if (!value.match(regex) && firstSrc) {
 | 
						|
                    ruleFailed = true;
 | 
						|
                    firstSrc = false;
 | 
						|
                } else if (value.match(regex) && !firstSrc) {
 | 
						|
                    ruleFailed = false;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
 | 
						|
        });
 | 
						|
 | 
						|
        // Back to normal rules that we don't need to test
 | 
						|
        parser.addListener("endfontface", function() {
 | 
						|
            fontFaceRule = false;
 | 
						|
 | 
						|
            if (ruleFailed) {
 | 
						|
                reporter.report("@font-face declaration doesn't follow the fontspring bulletproof syntax.", line, col, rule);
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Include all compatible vendor prefixes to reach a wider
 | 
						|
 * range of users.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "compatible-vendor-prefixes",
 | 
						|
    name: "Require compatible vendor prefixes",
 | 
						|
    desc: "Include all compatible vendor prefixes to reach a wider range of users.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Require-compatible-vendor-prefixes",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function (parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this,
 | 
						|
            compatiblePrefixes,
 | 
						|
            properties,
 | 
						|
            prop,
 | 
						|
            variations,
 | 
						|
            prefixed,
 | 
						|
            i,
 | 
						|
            len,
 | 
						|
            inKeyFrame = false,
 | 
						|
            arrayPush = Array.prototype.push,
 | 
						|
            applyTo = [];
 | 
						|
 | 
						|
        // See http://peter.sh/experiments/vendor-prefixed-css-property-overview/ for details
 | 
						|
        compatiblePrefixes = {
 | 
						|
            "animation"                  : "webkit",
 | 
						|
            "animation-delay"            : "webkit",
 | 
						|
            "animation-direction"        : "webkit",
 | 
						|
            "animation-duration"         : "webkit",
 | 
						|
            "animation-fill-mode"        : "webkit",
 | 
						|
            "animation-iteration-count"  : "webkit",
 | 
						|
            "animation-name"             : "webkit",
 | 
						|
            "animation-play-state"       : "webkit",
 | 
						|
            "animation-timing-function"  : "webkit",
 | 
						|
            "appearance"                 : "webkit moz",
 | 
						|
            "border-end"                 : "webkit moz",
 | 
						|
            "border-end-color"           : "webkit moz",
 | 
						|
            "border-end-style"           : "webkit moz",
 | 
						|
            "border-end-width"           : "webkit moz",
 | 
						|
            "border-image"               : "webkit moz o",
 | 
						|
            "border-radius"              : "webkit",
 | 
						|
            "border-start"               : "webkit moz",
 | 
						|
            "border-start-color"         : "webkit moz",
 | 
						|
            "border-start-style"         : "webkit moz",
 | 
						|
            "border-start-width"         : "webkit moz",
 | 
						|
            "box-align"                  : "webkit moz",
 | 
						|
            "box-direction"              : "webkit moz",
 | 
						|
            "box-flex"                   : "webkit moz",
 | 
						|
            "box-lines"                  : "webkit",
 | 
						|
            "box-ordinal-group"          : "webkit moz",
 | 
						|
            "box-orient"                 : "webkit moz",
 | 
						|
            "box-pack"                   : "webkit moz",
 | 
						|
            "box-sizing"                 : "",
 | 
						|
            "box-shadow"                 : "",
 | 
						|
            "column-count"               : "webkit moz ms",
 | 
						|
            "column-gap"                 : "webkit moz ms",
 | 
						|
            "column-rule"                : "webkit moz ms",
 | 
						|
            "column-rule-color"          : "webkit moz ms",
 | 
						|
            "column-rule-style"          : "webkit moz ms",
 | 
						|
            "column-rule-width"          : "webkit moz ms",
 | 
						|
            "column-width"               : "webkit moz ms",
 | 
						|
            "flex"                       : "webkit ms",
 | 
						|
            "flex-basis"                 : "webkit",
 | 
						|
            "flex-direction"             : "webkit ms",
 | 
						|
            "flex-flow"                  : "webkit",
 | 
						|
            "flex-grow"                  : "webkit",
 | 
						|
            "flex-shrink"                : "webkit",
 | 
						|
            "hyphens"                    : "epub moz",
 | 
						|
            "line-break"                 : "webkit ms",
 | 
						|
            "margin-end"                 : "webkit moz",
 | 
						|
            "margin-start"               : "webkit moz",
 | 
						|
            "marquee-speed"              : "webkit wap",
 | 
						|
            "marquee-style"              : "webkit wap",
 | 
						|
            "padding-end"                : "webkit moz",
 | 
						|
            "padding-start"              : "webkit moz",
 | 
						|
            "tab-size"                   : "moz o",
 | 
						|
            "text-size-adjust"           : "webkit ms",
 | 
						|
            "transform"                  : "webkit ms",
 | 
						|
            "transform-origin"           : "webkit ms",
 | 
						|
            "transition"                 : "",
 | 
						|
            "transition-delay"           : "",
 | 
						|
            "transition-duration"        : "",
 | 
						|
            "transition-property"        : "",
 | 
						|
            "transition-timing-function" : "",
 | 
						|
            "user-modify"                : "webkit moz",
 | 
						|
            "user-select"                : "webkit moz ms",
 | 
						|
            "word-break"                 : "epub ms",
 | 
						|
            "writing-mode"               : "epub ms"
 | 
						|
        };
 | 
						|
 | 
						|
 | 
						|
        for (prop in compatiblePrefixes) {
 | 
						|
            if (compatiblePrefixes.hasOwnProperty(prop)) {
 | 
						|
                variations = [];
 | 
						|
                prefixed = compatiblePrefixes[prop].split(" ");
 | 
						|
                for (i = 0, len = prefixed.length; i < len; i++) {
 | 
						|
                    variations.push("-" + prefixed[i] + "-" + prop);
 | 
						|
                }
 | 
						|
                compatiblePrefixes[prop] = variations;
 | 
						|
                arrayPush.apply(applyTo, variations);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        parser.addListener("startrule", function () {
 | 
						|
            properties = [];
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("startkeyframes", function (event) {
 | 
						|
            inKeyFrame = event.prefix || true;
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("endkeyframes", function () {
 | 
						|
            inKeyFrame = false;
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("property", function (event) {
 | 
						|
            var name = event.property;
 | 
						|
            if (CSSLint.Util.indexOf(applyTo, name.text) > -1) {
 | 
						|
 | 
						|
                // e.g., -moz-transform is okay to be alone in @-moz-keyframes
 | 
						|
                if (!inKeyFrame || typeof inKeyFrame !== "string" ||
 | 
						|
                        name.text.indexOf("-" + inKeyFrame + "-") !== 0) {
 | 
						|
                    properties.push(name);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("endrule", function () {
 | 
						|
            if (!properties.length) {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            var propertyGroups = {},
 | 
						|
                i,
 | 
						|
                len,
 | 
						|
                name,
 | 
						|
                prop,
 | 
						|
                variations,
 | 
						|
                value,
 | 
						|
                full,
 | 
						|
                actual,
 | 
						|
                item,
 | 
						|
                propertiesSpecified;
 | 
						|
 | 
						|
            for (i = 0, len = properties.length; i < len; i++) {
 | 
						|
                name = properties[i];
 | 
						|
 | 
						|
                for (prop in compatiblePrefixes) {
 | 
						|
                    if (compatiblePrefixes.hasOwnProperty(prop)) {
 | 
						|
                        variations = compatiblePrefixes[prop];
 | 
						|
                        if (CSSLint.Util.indexOf(variations, name.text) > -1) {
 | 
						|
                            if (!propertyGroups[prop]) {
 | 
						|
                                propertyGroups[prop] = {
 | 
						|
                                    full: variations.slice(0),
 | 
						|
                                    actual: [],
 | 
						|
                                    actualNodes: []
 | 
						|
                                };
 | 
						|
                            }
 | 
						|
                            if (CSSLint.Util.indexOf(propertyGroups[prop].actual, name.text) === -1) {
 | 
						|
                                propertyGroups[prop].actual.push(name.text);
 | 
						|
                                propertyGroups[prop].actualNodes.push(name);
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            for (prop in propertyGroups) {
 | 
						|
                if (propertyGroups.hasOwnProperty(prop)) {
 | 
						|
                    value = propertyGroups[prop];
 | 
						|
                    full = value.full;
 | 
						|
                    actual = value.actual;
 | 
						|
 | 
						|
                    if (full.length > actual.length) {
 | 
						|
                        for (i = 0, len = full.length; i < len; i++) {
 | 
						|
                            item = full[i];
 | 
						|
                            if (CSSLint.Util.indexOf(actual, item) === -1) {
 | 
						|
                                propertiesSpecified = (actual.length === 1) ? actual[0] : (actual.length === 2) ? actual.join(" and ") : actual.join(", ");
 | 
						|
                                reporter.report("The property " + item + " is compatible with " + propertiesSpecified + " and should be included as well.", value.actualNodes[0].line, value.actualNodes[0].col, rule);
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Certain properties don't play well with certain display values.
 | 
						|
 * - float should not be used with inline-block
 | 
						|
 * - height, width, margin-top, margin-bottom, float should not be used with inline
 | 
						|
 * - vertical-align should not be used with block
 | 
						|
 * - margin, float should not be used with table-*
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "display-property-grouping",
 | 
						|
    name: "Require properties appropriate for display",
 | 
						|
    desc: "Certain properties shouldn't be used with certain display property values.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Require-properties-appropriate-for-display",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this;
 | 
						|
 | 
						|
        var propertiesToCheck = {
 | 
						|
                display: 1,
 | 
						|
                "float": "none",
 | 
						|
                height: 1,
 | 
						|
                width: 1,
 | 
						|
                margin: 1,
 | 
						|
                "margin-left": 1,
 | 
						|
                "margin-right": 1,
 | 
						|
                "margin-bottom": 1,
 | 
						|
                "margin-top": 1,
 | 
						|
                padding: 1,
 | 
						|
                "padding-left": 1,
 | 
						|
                "padding-right": 1,
 | 
						|
                "padding-bottom": 1,
 | 
						|
                "padding-top": 1,
 | 
						|
                "vertical-align": 1
 | 
						|
            },
 | 
						|
            properties;
 | 
						|
 | 
						|
        function reportProperty(name, display, msg) {
 | 
						|
            if (properties[name]) {
 | 
						|
                if (typeof propertiesToCheck[name] !== "string" || properties[name].value.toLowerCase() !== propertiesToCheck[name]) {
 | 
						|
                    reporter.report(msg || name + " can't be used with display: " + display + ".", properties[name].line, properties[name].col, rule);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        function startRule() {
 | 
						|
            properties = {};
 | 
						|
        }
 | 
						|
 | 
						|
        function endRule() {
 | 
						|
 | 
						|
            var display = properties.display ? properties.display.value : null;
 | 
						|
            if (display) {
 | 
						|
                switch (display) {
 | 
						|
 | 
						|
                    case "inline":
 | 
						|
                        // height, width, margin-top, margin-bottom, float should not be used with inline
 | 
						|
                        reportProperty("height", display);
 | 
						|
                        reportProperty("width", display);
 | 
						|
                        reportProperty("margin", display);
 | 
						|
                        reportProperty("margin-top", display);
 | 
						|
                        reportProperty("margin-bottom", display);
 | 
						|
                        reportProperty("float", display, "display:inline has no effect on floated elements (but may be used to fix the IE6 double-margin bug).");
 | 
						|
                        break;
 | 
						|
 | 
						|
                    case "block":
 | 
						|
                        // vertical-align should not be used with block
 | 
						|
                        reportProperty("vertical-align", display);
 | 
						|
                        break;
 | 
						|
 | 
						|
                    case "inline-block":
 | 
						|
                        // float should not be used with inline-block
 | 
						|
                        reportProperty("float", display);
 | 
						|
                        break;
 | 
						|
 | 
						|
                    default:
 | 
						|
                        // margin, float should not be used with table
 | 
						|
                        if (display.indexOf("table-") === 0) {
 | 
						|
                            reportProperty("margin", display);
 | 
						|
                            reportProperty("margin-left", display);
 | 
						|
                            reportProperty("margin-right", display);
 | 
						|
                            reportProperty("margin-top", display);
 | 
						|
                            reportProperty("margin-bottom", display);
 | 
						|
                            reportProperty("float", display);
 | 
						|
                        }
 | 
						|
 | 
						|
                        // otherwise do nothing
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        parser.addListener("startrule", startRule);
 | 
						|
        parser.addListener("startfontface", startRule);
 | 
						|
        parser.addListener("startkeyframerule", startRule);
 | 
						|
        parser.addListener("startpagemargin", startRule);
 | 
						|
        parser.addListener("startpage", startRule);
 | 
						|
        parser.addListener("startviewport", startRule);
 | 
						|
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
            var name = event.property.text.toLowerCase();
 | 
						|
 | 
						|
            if (propertiesToCheck[name]) {
 | 
						|
                properties[name] = {
 | 
						|
                    value: event.value.text,
 | 
						|
                    line: event.property.line,
 | 
						|
                    col: event.property.col
 | 
						|
                };
 | 
						|
            }
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("endrule", endRule);
 | 
						|
        parser.addListener("endfontface", endRule);
 | 
						|
        parser.addListener("endkeyframerule", endRule);
 | 
						|
        parser.addListener("endpagemargin", endRule);
 | 
						|
        parser.addListener("endpage", endRule);
 | 
						|
        parser.addListener("endviewport", endRule);
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Disallow duplicate background-images (using url).
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "duplicate-background-images",
 | 
						|
    name: "Disallow duplicate background images",
 | 
						|
    desc: "Every background-image should be unique. Use a common class for e.g. sprites.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-background-images",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this,
 | 
						|
            stack = {};
 | 
						|
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
            var name = event.property.text,
 | 
						|
                value = event.value,
 | 
						|
                i, len;
 | 
						|
 | 
						|
            if (name.match(/background/i)) {
 | 
						|
                for (i=0, len=value.parts.length; i < len; i++) {
 | 
						|
                    if (value.parts[i].type === "uri") {
 | 
						|
                        if (typeof stack[value.parts[i].uri] === "undefined") {
 | 
						|
                            stack[value.parts[i].uri] = event;
 | 
						|
                        } else {
 | 
						|
                            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);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Duplicate properties must appear one after the other. If an already-defined
 | 
						|
 * property appears somewhere else in the rule, then it's likely an error.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "duplicate-properties",
 | 
						|
    name: "Disallow duplicate properties",
 | 
						|
    desc: "Duplicate properties must appear one after the other.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-properties",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this,
 | 
						|
            properties,
 | 
						|
            lastProperty;
 | 
						|
 | 
						|
        function startRule() {
 | 
						|
            properties = {};
 | 
						|
        }
 | 
						|
 | 
						|
        parser.addListener("startrule", startRule);
 | 
						|
        parser.addListener("startfontface", startRule);
 | 
						|
        parser.addListener("startpage", startRule);
 | 
						|
        parser.addListener("startpagemargin", startRule);
 | 
						|
        parser.addListener("startkeyframerule", startRule);
 | 
						|
        parser.addListener("startviewport", startRule);
 | 
						|
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
            var property = event.property,
 | 
						|
                name = property.text.toLowerCase();
 | 
						|
 | 
						|
            if (properties[name] && (lastProperty !== name || properties[name] === event.value.text)) {
 | 
						|
                reporter.report("Duplicate property '" + event.property + "' found.", event.line, event.col, rule);
 | 
						|
            }
 | 
						|
 | 
						|
            properties[name] = event.value.text;
 | 
						|
            lastProperty = name;
 | 
						|
 | 
						|
        });
 | 
						|
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Style rules without any properties defined should be removed.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "empty-rules",
 | 
						|
    name: "Disallow empty rules",
 | 
						|
    desc: "Rules without any properties specified should be removed.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-empty-rules",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this,
 | 
						|
            count = 0;
 | 
						|
 | 
						|
        parser.addListener("startrule", function() {
 | 
						|
            count=0;
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("property", function() {
 | 
						|
            count++;
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("endrule", function(event) {
 | 
						|
            var selectors = event.selectors;
 | 
						|
            if (count === 0) {
 | 
						|
                reporter.report("Rule is empty.", selectors[0].line, selectors[0].col, rule);
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: There should be no syntax errors. (Duh.)
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "errors",
 | 
						|
    name: "Parsing Errors",
 | 
						|
    desc: "This rule looks for recoverable syntax errors.",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this;
 | 
						|
 | 
						|
        parser.addListener("error", function(event) {
 | 
						|
            reporter.error(event.message, event.line, event.col, rule);
 | 
						|
        });
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "fallback-colors",
 | 
						|
    name: "Require fallback colors",
 | 
						|
    desc: "For older browsers that don't support RGBA, HSL, or HSLA, provide a fallback color.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Require-fallback-colors",
 | 
						|
    browsers: "IE6,IE7,IE8",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this,
 | 
						|
            lastProperty,
 | 
						|
            propertiesToCheck = {
 | 
						|
                color: 1,
 | 
						|
                background: 1,
 | 
						|
                "border-color": 1,
 | 
						|
                "border-top-color": 1,
 | 
						|
                "border-right-color": 1,
 | 
						|
                "border-bottom-color": 1,
 | 
						|
                "border-left-color": 1,
 | 
						|
                border: 1,
 | 
						|
                "border-top": 1,
 | 
						|
                "border-right": 1,
 | 
						|
                "border-bottom": 1,
 | 
						|
                "border-left": 1,
 | 
						|
                "background-color": 1
 | 
						|
            };
 | 
						|
 | 
						|
        function startRule() {
 | 
						|
            lastProperty = null;
 | 
						|
        }
 | 
						|
 | 
						|
        parser.addListener("startrule", startRule);
 | 
						|
        parser.addListener("startfontface", startRule);
 | 
						|
        parser.addListener("startpage", startRule);
 | 
						|
        parser.addListener("startpagemargin", startRule);
 | 
						|
        parser.addListener("startkeyframerule", startRule);
 | 
						|
        parser.addListener("startviewport", startRule);
 | 
						|
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
            var property = event.property,
 | 
						|
                name = property.text.toLowerCase(),
 | 
						|
                parts = event.value.parts,
 | 
						|
                i = 0,
 | 
						|
                colorType = "",
 | 
						|
                len = parts.length;
 | 
						|
 | 
						|
            if (propertiesToCheck[name]) {
 | 
						|
                while (i < len) {
 | 
						|
                    if (parts[i].type === "color") {
 | 
						|
                        if ("alpha" in parts[i] || "hue" in parts[i]) {
 | 
						|
 | 
						|
                            if (/([^\)]+)\(/.test(parts[i])) {
 | 
						|
                                colorType = RegExp.$1.toUpperCase();
 | 
						|
                            }
 | 
						|
 | 
						|
                            if (!lastProperty || (lastProperty.property.text.toLowerCase() !== name || lastProperty.colorType !== "compat")) {
 | 
						|
                                reporter.report("Fallback " + name + " (hex or RGB) should precede " + colorType + " " + name + ".", event.line, event.col, rule);
 | 
						|
                            }
 | 
						|
                        } else {
 | 
						|
                            event.colorType = "compat";
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
 | 
						|
                    i++;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            lastProperty = event;
 | 
						|
        });
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: You shouldn't use more than 10 floats. If you do, there's probably
 | 
						|
 * room for some abstraction.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "floats",
 | 
						|
    name: "Disallow too many floats",
 | 
						|
    desc: "This rule tests if the float property is used too many times",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-too-many-floats",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this;
 | 
						|
        var count = 0;
 | 
						|
 | 
						|
        // count how many times "float" is used
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
            if (event.property.text.toLowerCase() === "float" &&
 | 
						|
                    event.value.text.toLowerCase() !== "none") {
 | 
						|
                count++;
 | 
						|
            }
 | 
						|
        });
 | 
						|
 | 
						|
        // report the results
 | 
						|
        parser.addListener("endstylesheet", function() {
 | 
						|
            reporter.stat("floats", count);
 | 
						|
            if (count >= 10) {
 | 
						|
                reporter.rollupWarn("Too many floats (" + count + "), you're probably using them for layout. Consider using a grid system instead.", rule);
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Avoid too many @font-face declarations in the same stylesheet.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "font-faces",
 | 
						|
    name: "Don't use too many web fonts",
 | 
						|
    desc: "Too many different web fonts in the same stylesheet.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-web-fonts",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this,
 | 
						|
            count = 0;
 | 
						|
 | 
						|
 | 
						|
        parser.addListener("startfontface", function() {
 | 
						|
            count++;
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("endstylesheet", function() {
 | 
						|
            if (count > 5) {
 | 
						|
                reporter.rollupWarn("Too many @font-face declarations (" + count + ").", rule);
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: You shouldn't need more than 9 font-size declarations.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "font-sizes",
 | 
						|
    name: "Disallow too many font sizes",
 | 
						|
    desc: "Checks the number of font-size declarations.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-font-size-declarations",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this,
 | 
						|
            count = 0;
 | 
						|
 | 
						|
        // check for use of "font-size"
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
            if (event.property.toString() === "font-size") {
 | 
						|
                count++;
 | 
						|
            }
 | 
						|
        });
 | 
						|
 | 
						|
        // report the results
 | 
						|
        parser.addListener("endstylesheet", function() {
 | 
						|
            reporter.stat("font-sizes", count);
 | 
						|
            if (count >= 10) {
 | 
						|
                reporter.rollupWarn("Too many font-size declarations (" + count + "), abstraction needed.", rule);
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: When using a vendor-prefixed gradient, make sure to use them all.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "gradients",
 | 
						|
    name: "Require all gradient definitions",
 | 
						|
    desc: "When using a vendor-prefixed gradient, make sure to use them all.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Require-all-gradient-definitions",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this,
 | 
						|
            gradients;
 | 
						|
 | 
						|
        parser.addListener("startrule", function() {
 | 
						|
            gradients = {
 | 
						|
                moz: 0,
 | 
						|
                webkit: 0,
 | 
						|
                oldWebkit: 0,
 | 
						|
                o: 0
 | 
						|
            };
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
 | 
						|
            if (/\-(moz|o|webkit)(?:\-(?:linear|radial))\-gradient/i.test(event.value)) {
 | 
						|
                gradients[RegExp.$1] = 1;
 | 
						|
            } else if (/\-webkit\-gradient/i.test(event.value)) {
 | 
						|
                gradients.oldWebkit = 1;
 | 
						|
            }
 | 
						|
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("endrule", function(event) {
 | 
						|
            var missing = [];
 | 
						|
 | 
						|
            if (!gradients.moz) {
 | 
						|
                missing.push("Firefox 3.6+");
 | 
						|
            }
 | 
						|
 | 
						|
            if (!gradients.webkit) {
 | 
						|
                missing.push("Webkit (Safari 5+, Chrome)");
 | 
						|
            }
 | 
						|
 | 
						|
            if (!gradients.oldWebkit) {
 | 
						|
                missing.push("Old Webkit (Safari 4+, Chrome)");
 | 
						|
            }
 | 
						|
 | 
						|
            if (!gradients.o) {
 | 
						|
                missing.push("Opera 11.1+");
 | 
						|
            }
 | 
						|
 | 
						|
            if (missing.length && missing.length < 4) {
 | 
						|
                reporter.report("Missing vendor-prefixed CSS gradients for " + missing.join(", ") + ".", event.selectors[0].line, event.selectors[0].col, rule);
 | 
						|
            }
 | 
						|
 | 
						|
        });
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Don't use IDs for selectors.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "ids",
 | 
						|
    name: "Disallow IDs in selectors",
 | 
						|
    desc: "Selectors should not contain IDs.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-IDs-in-selectors",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this;
 | 
						|
        parser.addListener("startrule", function(event) {
 | 
						|
            var selectors = event.selectors,
 | 
						|
                selector,
 | 
						|
                part,
 | 
						|
                modifier,
 | 
						|
                idCount,
 | 
						|
                i, j, k;
 | 
						|
 | 
						|
            for (i=0; i < selectors.length; i++) {
 | 
						|
                selector = selectors[i];
 | 
						|
                idCount = 0;
 | 
						|
 | 
						|
                for (j=0; j < selector.parts.length; j++) {
 | 
						|
                    part = selector.parts[j];
 | 
						|
                    if (part.type === parser.SELECTOR_PART_TYPE) {
 | 
						|
                        for (k=0; k < part.modifiers.length; k++) {
 | 
						|
                            modifier = part.modifiers[k];
 | 
						|
                            if (modifier.type === "id") {
 | 
						|
                                idCount++;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                if (idCount === 1) {
 | 
						|
                    reporter.report("Don't use IDs in selectors.", selector.line, selector.col, rule);
 | 
						|
                } else if (idCount > 1) {
 | 
						|
                    reporter.report(idCount + " IDs in the selector, really?", selector.line, selector.col, rule);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: IE6-9 supports up to 31 stylesheet import.
 | 
						|
 * Reference:
 | 
						|
 * http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/internet-explorer-stylesheet-rule-selector-import-sheet-limit-maximum.aspx
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "import-ie-limit",
 | 
						|
    name: "@import limit on IE6-IE9",
 | 
						|
    desc: "IE6-9 supports up to 31 @import per stylesheet",
 | 
						|
    browsers: "IE6, IE7, IE8, IE9",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this,
 | 
						|
            MAX_IMPORT_COUNT = 31,
 | 
						|
            count = 0;
 | 
						|
 | 
						|
        function startPage() {
 | 
						|
            count = 0;
 | 
						|
        }
 | 
						|
 | 
						|
        parser.addListener("startpage", startPage);
 | 
						|
 | 
						|
        parser.addListener("import", function() {
 | 
						|
            count++;
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("endstylesheet", function() {
 | 
						|
            if (count > MAX_IMPORT_COUNT) {
 | 
						|
                reporter.rollupError(
 | 
						|
                    "Too many @import rules (" + count + "). IE6-9 supports up to 31 import per stylesheet.",
 | 
						|
                    rule
 | 
						|
                );
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Don't use @import, use <link> instead.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "import",
 | 
						|
    name: "Disallow @import",
 | 
						|
    desc: "Don't use @import, use <link> instead.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-%40import",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this;
 | 
						|
 | 
						|
        parser.addListener("import", function(event) {
 | 
						|
            reporter.report("@import prevents parallel downloads, use <link> instead.", event.line, event.col, rule);
 | 
						|
        });
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Make sure !important is not overused, this could lead to specificity
 | 
						|
 * war. Display a warning on !important declarations, an error if it's
 | 
						|
 * used more at least 10 times.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "important",
 | 
						|
    name: "Disallow !important",
 | 
						|
    desc: "Be careful when using !important declaration",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-%21important",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this,
 | 
						|
            count = 0;
 | 
						|
 | 
						|
        // warn that important is used and increment the declaration counter
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
            if (event.important === true) {
 | 
						|
                count++;
 | 
						|
                reporter.report("Use of !important", event.line, event.col, rule);
 | 
						|
            }
 | 
						|
        });
 | 
						|
 | 
						|
        // if there are more than 10, show an error
 | 
						|
        parser.addListener("endstylesheet", function() {
 | 
						|
            reporter.stat("important", count);
 | 
						|
            if (count >= 10) {
 | 
						|
                reporter.rollupWarn("Too many !important declarations (" + count + "), try to use less than 10 to avoid specificity issues.", rule);
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Properties should be known (listed in CSS3 specification) or
 | 
						|
 * be a vendor-prefixed property.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "known-properties",
 | 
						|
    name: "Require use of known properties",
 | 
						|
    desc: "Properties should be known (listed in CSS3 specification) or be a vendor-prefixed property.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Require-use-of-known-properties",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this;
 | 
						|
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
 | 
						|
            // the check is handled entirely by the parser-lib (https://github.com/nzakas/parser-lib)
 | 
						|
            if (event.invalid) {
 | 
						|
                reporter.report(event.invalid.message, event.line, event.col, rule);
 | 
						|
            }
 | 
						|
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: All properties should be in alphabetical order.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "order-alphabetical",
 | 
						|
    name: "Alphabetical order",
 | 
						|
    desc: "Assure properties are in alphabetical order",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this,
 | 
						|
            properties;
 | 
						|
 | 
						|
        var startRule = function () {
 | 
						|
            properties = [];
 | 
						|
        };
 | 
						|
 | 
						|
        var endRule = function(event) {
 | 
						|
            var currentProperties = properties.join(","),
 | 
						|
                expectedProperties = properties.sort().join(",");
 | 
						|
 | 
						|
            if (currentProperties !== expectedProperties) {
 | 
						|
                reporter.report("Rule doesn't have all its properties in alphabetical order.", event.line, event.col, rule);
 | 
						|
            }
 | 
						|
        };
 | 
						|
 | 
						|
        parser.addListener("startrule", startRule);
 | 
						|
        parser.addListener("startfontface", startRule);
 | 
						|
        parser.addListener("startpage", startRule);
 | 
						|
        parser.addListener("startpagemargin", startRule);
 | 
						|
        parser.addListener("startkeyframerule", startRule);
 | 
						|
        parser.addListener("startviewport", startRule);
 | 
						|
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
            var name = event.property.text,
 | 
						|
                lowerCasePrefixLessName = name.toLowerCase().replace(/^-.*?-/, "");
 | 
						|
 | 
						|
            properties.push(lowerCasePrefixLessName);
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("endrule", endRule);
 | 
						|
        parser.addListener("endfontface", endRule);
 | 
						|
        parser.addListener("endpage", endRule);
 | 
						|
        parser.addListener("endpagemargin", endRule);
 | 
						|
        parser.addListener("endkeyframerule", endRule);
 | 
						|
        parser.addListener("endviewport", endRule);
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: outline: none or outline: 0 should only be used in a :focus rule
 | 
						|
 *       and only if there are other properties in the same rule.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "outline-none",
 | 
						|
    name: "Disallow outline: none",
 | 
						|
    desc: "Use of outline: none or outline: 0 should be limited to :focus rules.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-outline%3Anone",
 | 
						|
    browsers: "All",
 | 
						|
    tags: ["Accessibility"],
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this,
 | 
						|
            lastRule;
 | 
						|
 | 
						|
        function startRule(event) {
 | 
						|
            if (event.selectors) {
 | 
						|
                lastRule = {
 | 
						|
                    line: event.line,
 | 
						|
                    col: event.col,
 | 
						|
                    selectors: event.selectors,
 | 
						|
                    propCount: 0,
 | 
						|
                    outline: false
 | 
						|
                };
 | 
						|
            } else {
 | 
						|
                lastRule = null;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        function endRule() {
 | 
						|
            if (lastRule) {
 | 
						|
                if (lastRule.outline) {
 | 
						|
                    if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") === -1) {
 | 
						|
                        reporter.report("Outlines should only be modified using :focus.", lastRule.line, lastRule.col, rule);
 | 
						|
                    } else if (lastRule.propCount === 1) {
 | 
						|
                        reporter.report("Outlines shouldn't be hidden unless other visual changes are made.", lastRule.line, lastRule.col, rule);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        parser.addListener("startrule", startRule);
 | 
						|
        parser.addListener("startfontface", startRule);
 | 
						|
        parser.addListener("startpage", startRule);
 | 
						|
        parser.addListener("startpagemargin", startRule);
 | 
						|
        parser.addListener("startkeyframerule", startRule);
 | 
						|
        parser.addListener("startviewport", startRule);
 | 
						|
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
            var name = event.property.text.toLowerCase(),
 | 
						|
                value = event.value;
 | 
						|
 | 
						|
            if (lastRule) {
 | 
						|
                lastRule.propCount++;
 | 
						|
                if (name === "outline" && (value.toString() === "none" || value.toString() === "0")) {
 | 
						|
                    lastRule.outline = true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("endrule", endRule);
 | 
						|
        parser.addListener("endfontface", endRule);
 | 
						|
        parser.addListener("endpage", endRule);
 | 
						|
        parser.addListener("endpagemargin", endRule);
 | 
						|
        parser.addListener("endkeyframerule", endRule);
 | 
						|
        parser.addListener("endviewport", endRule);
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Don't use classes or IDs with elements (a.foo or a#foo).
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "overqualified-elements",
 | 
						|
    name: "Disallow overqualified elements",
 | 
						|
    desc: "Don't use classes or IDs with elements (a.foo or a#foo).",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-overqualified-elements",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this,
 | 
						|
            classes = {};
 | 
						|
 | 
						|
        parser.addListener("startrule", function(event) {
 | 
						|
            var selectors = event.selectors,
 | 
						|
                selector,
 | 
						|
                part,
 | 
						|
                modifier,
 | 
						|
                i, j, k;
 | 
						|
 | 
						|
            for (i=0; i < selectors.length; i++) {
 | 
						|
                selector = selectors[i];
 | 
						|
 | 
						|
                for (j=0; j < selector.parts.length; j++) {
 | 
						|
                    part = selector.parts[j];
 | 
						|
                    if (part.type === parser.SELECTOR_PART_TYPE) {
 | 
						|
                        for (k=0; k < part.modifiers.length; k++) {
 | 
						|
                            modifier = part.modifiers[k];
 | 
						|
                            if (part.elementName && modifier.type === "id") {
 | 
						|
                                reporter.report("Element (" + part + ") is overqualified, just use " + modifier + " without element name.", part.line, part.col, rule);
 | 
						|
                            } else if (modifier.type === "class") {
 | 
						|
 | 
						|
                                if (!classes[modifier]) {
 | 
						|
                                    classes[modifier] = [];
 | 
						|
                                }
 | 
						|
                                classes[modifier].push({
 | 
						|
                                    modifier: modifier,
 | 
						|
                                    part: part
 | 
						|
                                });
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("endstylesheet", function() {
 | 
						|
 | 
						|
            var prop;
 | 
						|
            for (prop in classes) {
 | 
						|
                if (classes.hasOwnProperty(prop)) {
 | 
						|
 | 
						|
                    // one use means that this is overqualified
 | 
						|
                    if (classes[prop].length === 1 && classes[prop][0].part.elementName) {
 | 
						|
                        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);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
  id: "performant-transitions",
 | 
						|
  name: "Allow only performant transisitons",
 | 
						|
  desc: "Only allow transitions that trigger compositing for performant, 60fps transformations.",
 | 
						|
  url: "",
 | 
						|
  browsers: "All",
 | 
						|
 | 
						|
  init: function(parser, reporter){
 | 
						|
    "use strict";
 | 
						|
    var rule = this;
 | 
						|
 | 
						|
    var transitionProperties = ["transition-property", "transition", "-webkit-transition", "-o-transition"];
 | 
						|
    var allowedTransitions = [/-webkit-transform/g, /-ms-transform/g, /transform/g, /opacity/g];
 | 
						|
 | 
						|
    parser.addListener("property", function(event) {
 | 
						|
      var propertyName    = event.property.toString().toLowerCase(),
 | 
						|
          propertyValue           = event.value.toString(),
 | 
						|
          line            = event.line,
 | 
						|
          col             = event.col;
 | 
						|
 | 
						|
      var values = propertyValue.split(",");
 | 
						|
      if (transitionProperties.indexOf(propertyName) !== -1) {
 | 
						|
        var reportValues = values.filter(function(value) {
 | 
						|
          var didMatch = [];
 | 
						|
          for (var i = 0; i < allowedTransitions.length; i++) {
 | 
						|
            if(value.match(allowedTransitions[i])) {
 | 
						|
              didMatch.push(i);
 | 
						|
            }
 | 
						|
          }
 | 
						|
          return didMatch.length === 0;
 | 
						|
        });
 | 
						|
        if(reportValues.length > 0) {
 | 
						|
            reporter.report("Unexpected transition property '"+reportValues.join(",").trim()+"'", line, col, rule);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    });
 | 
						|
  }
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Headings (h1-h6) should not be qualified (namespaced).
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "qualified-headings",
 | 
						|
    name: "Disallow qualified headings",
 | 
						|
    desc: "Headings should not be qualified (namespaced).",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-qualified-headings",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this;
 | 
						|
 | 
						|
        parser.addListener("startrule", function(event) {
 | 
						|
            var selectors = event.selectors,
 | 
						|
                selector,
 | 
						|
                part,
 | 
						|
                i, j;
 | 
						|
 | 
						|
            for (i=0; i < selectors.length; i++) {
 | 
						|
                selector = selectors[i];
 | 
						|
 | 
						|
                for (j=0; j < selector.parts.length; j++) {
 | 
						|
                    part = selector.parts[j];
 | 
						|
                    if (part.type === parser.SELECTOR_PART_TYPE) {
 | 
						|
                        if (part.elementName && /h[1-6]/.test(part.elementName.toString()) && j > 0) {
 | 
						|
                            reporter.report("Heading (" + part.elementName + ") should not be qualified.", part.line, part.col, rule);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Selectors that look like regular expressions are slow and should be avoided.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "regex-selectors",
 | 
						|
    name: "Disallow selectors that look like regexs",
 | 
						|
    desc: "Selectors that look like regular expressions are slow and should be avoided.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-selectors-that-look-like-regular-expressions",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this;
 | 
						|
 | 
						|
        parser.addListener("startrule", function(event) {
 | 
						|
            var selectors = event.selectors,
 | 
						|
                selector,
 | 
						|
                part,
 | 
						|
                modifier,
 | 
						|
                i, j, k;
 | 
						|
 | 
						|
            for (i=0; i < selectors.length; i++) {
 | 
						|
                selector = selectors[i];
 | 
						|
                for (j=0; j < selector.parts.length; j++) {
 | 
						|
                    part = selector.parts[j];
 | 
						|
                    if (part.type === parser.SELECTOR_PART_TYPE) {
 | 
						|
                        for (k=0; k < part.modifiers.length; k++) {
 | 
						|
                            modifier = part.modifiers[k];
 | 
						|
                            if (modifier.type === "attribute") {
 | 
						|
                                if (/([~\|\^\$\*]=)/.test(modifier)) {
 | 
						|
                                    reporter.report("Attribute selectors with " + RegExp.$1 + " are slow!", modifier.line, modifier.col, rule);
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Total number of rules should not exceed x.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "rules-count",
 | 
						|
    name: "Rules Count",
 | 
						|
    desc: "Track how many rules there are.",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var count = 0;
 | 
						|
 | 
						|
        // count each rule
 | 
						|
        parser.addListener("startrule", function() {
 | 
						|
            count++;
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("endstylesheet", function() {
 | 
						|
            reporter.stat("rule-count", count);
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Warn people with approaching the IE 4095 limit
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "selector-max-approaching",
 | 
						|
    name: "Warn when approaching the 4095 selector limit for IE",
 | 
						|
    desc: "Will warn when selector count is >= 3800 selectors.",
 | 
						|
    browsers: "IE",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this, count = 0;
 | 
						|
 | 
						|
        parser.addListener("startrule", function(event) {
 | 
						|
            count += event.selectors.length;
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("endstylesheet", function() {
 | 
						|
            if (count >= 3800) {
 | 
						|
                reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule);
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Warn people past the IE 4095 limit
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "selector-max",
 | 
						|
    name: "Error when past the 4095 selector limit for IE",
 | 
						|
    desc: "Will error when selector count is > 4095.",
 | 
						|
    browsers: "IE",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this, count = 0;
 | 
						|
 | 
						|
        parser.addListener("startrule", function(event) {
 | 
						|
            count += event.selectors.length;
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("endstylesheet", function() {
 | 
						|
            if (count > 4095) {
 | 
						|
                reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule);
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Avoid new-line characters in selectors.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "selector-newline",
 | 
						|
    name: "Disallow new-line characters in selectors",
 | 
						|
    desc: "New-line characters in selectors are usually a forgotten comma and not a descendant combinator.",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this;
 | 
						|
 | 
						|
        function startRule(event) {
 | 
						|
            var i, len, selector, p, n, pLen, part, part2, type, currentLine, nextLine,
 | 
						|
                selectors = event.selectors;
 | 
						|
 | 
						|
            for (i = 0, len = selectors.length; i < len; i++) {
 | 
						|
                selector = selectors[i];
 | 
						|
                for (p = 0, pLen = selector.parts.length; p < pLen; p++) {
 | 
						|
                    for (n = p + 1; n < pLen; n++) {
 | 
						|
                        part = selector.parts[p];
 | 
						|
                        part2 = selector.parts[n];
 | 
						|
                        type = part.type;
 | 
						|
                        currentLine = part.line;
 | 
						|
                        nextLine = part2.line;
 | 
						|
 | 
						|
                        if (type === "descendant" && nextLine > currentLine) {
 | 
						|
                            reporter.report("newline character found in selector (forgot a comma?)", currentLine, selectors[i].parts[0].col, rule);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        parser.addListener("startrule", startRule);
 | 
						|
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Use shorthand properties where possible.
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "shorthand",
 | 
						|
    name: "Require shorthand properties",
 | 
						|
    desc: "Use shorthand properties where possible.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Require-shorthand-properties",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this,
 | 
						|
            prop, i, len,
 | 
						|
            propertiesToCheck = {},
 | 
						|
            properties,
 | 
						|
            mapping = {
 | 
						|
                "margin": [
 | 
						|
                    "margin-top",
 | 
						|
                    "margin-bottom",
 | 
						|
                    "margin-left",
 | 
						|
                    "margin-right"
 | 
						|
                ],
 | 
						|
                "padding": [
 | 
						|
                    "padding-top",
 | 
						|
                    "padding-bottom",
 | 
						|
                    "padding-left",
 | 
						|
                    "padding-right"
 | 
						|
                ]
 | 
						|
            };
 | 
						|
 | 
						|
        // initialize propertiesToCheck
 | 
						|
        for (prop in mapping) {
 | 
						|
            if (mapping.hasOwnProperty(prop)) {
 | 
						|
                for (i=0, len=mapping[prop].length; i < len; i++) {
 | 
						|
                    propertiesToCheck[mapping[prop][i]] = prop;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        function startRule() {
 | 
						|
            properties = {};
 | 
						|
        }
 | 
						|
 | 
						|
        // event handler for end of rules
 | 
						|
        function endRule(event) {
 | 
						|
 | 
						|
            var prop, i, len, total;
 | 
						|
 | 
						|
            // check which properties this rule has
 | 
						|
            for (prop in mapping) {
 | 
						|
                if (mapping.hasOwnProperty(prop)) {
 | 
						|
                    total=0;
 | 
						|
 | 
						|
                    for (i=0, len=mapping[prop].length; i < len; i++) {
 | 
						|
                        total += properties[mapping[prop][i]] ? 1 : 0;
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (total === mapping[prop].length) {
 | 
						|
                        reporter.report("The properties " + mapping[prop].join(", ") + " can be replaced by " + prop + ".", event.line, event.col, rule);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        parser.addListener("startrule", startRule);
 | 
						|
        parser.addListener("startfontface", startRule);
 | 
						|
 | 
						|
        // check for use of "font-size"
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
            var name = event.property.toString().toLowerCase();
 | 
						|
 | 
						|
            if (propertiesToCheck[name]) {
 | 
						|
                properties[name] = 1;
 | 
						|
            }
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("endrule", endRule);
 | 
						|
        parser.addListener("endfontface", endRule);
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Don't use properties with a star prefix.
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "star-property-hack",
 | 
						|
    name: "Disallow properties with a star prefix",
 | 
						|
    desc: "Checks for the star property hack (targets IE6/7)",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-star-hack",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this;
 | 
						|
 | 
						|
        // check if property name starts with "*"
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
            var property = event.property;
 | 
						|
 | 
						|
            if (property.hack === "*") {
 | 
						|
                reporter.report("Property with star prefix found.", event.property.line, event.property.col, rule);
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Don't use text-indent for image replacement if you need to support rtl.
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "text-indent",
 | 
						|
    name: "Disallow negative text-indent",
 | 
						|
    desc: "Checks for text indent less than -99px",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-negative-text-indent",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this,
 | 
						|
            textIndent,
 | 
						|
            direction;
 | 
						|
 | 
						|
 | 
						|
        function startRule() {
 | 
						|
            textIndent = false;
 | 
						|
            direction = "inherit";
 | 
						|
        }
 | 
						|
 | 
						|
        // event handler for end of rules
 | 
						|
        function endRule() {
 | 
						|
            if (textIndent && direction !== "ltr") {
 | 
						|
                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);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        parser.addListener("startrule", startRule);
 | 
						|
        parser.addListener("startfontface", startRule);
 | 
						|
 | 
						|
        // check for use of "font-size"
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
            var name = event.property.toString().toLowerCase(),
 | 
						|
                value = event.value;
 | 
						|
 | 
						|
            if (name === "text-indent" && value.parts[0].value < -99) {
 | 
						|
                textIndent = event.property;
 | 
						|
            } else if (name === "direction" && value.toString() === "ltr") {
 | 
						|
                direction = "ltr";
 | 
						|
            }
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("endrule", endRule);
 | 
						|
        parser.addListener("endfontface", endRule);
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Don't use properties with a underscore prefix.
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "underscore-property-hack",
 | 
						|
    name: "Disallow properties with an underscore prefix",
 | 
						|
    desc: "Checks for the underscore property hack (targets IE6)",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-underscore-hack",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this;
 | 
						|
 | 
						|
        // check if property name starts with "_"
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
            var property = event.property;
 | 
						|
 | 
						|
            if (property.hack === "_") {
 | 
						|
                reporter.report("Property with underscore prefix found.", event.property.line, event.property.col, rule);
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Headings (h1-h6) should be defined only once.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "unique-headings",
 | 
						|
    name: "Headings should only be defined once",
 | 
						|
    desc: "Headings should be defined only once.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Headings-should-only-be-defined-once",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this;
 | 
						|
 | 
						|
        var headings = {
 | 
						|
            h1: 0,
 | 
						|
            h2: 0,
 | 
						|
            h3: 0,
 | 
						|
            h4: 0,
 | 
						|
            h5: 0,
 | 
						|
            h6: 0
 | 
						|
        };
 | 
						|
 | 
						|
        parser.addListener("startrule", function(event) {
 | 
						|
            var selectors = event.selectors,
 | 
						|
                selector,
 | 
						|
                part,
 | 
						|
                pseudo,
 | 
						|
                i, j;
 | 
						|
 | 
						|
            for (i=0; i < selectors.length; i++) {
 | 
						|
                selector = selectors[i];
 | 
						|
                part = selector.parts[selector.parts.length-1];
 | 
						|
 | 
						|
                if (part.elementName && /(h[1-6])/i.test(part.elementName.toString())) {
 | 
						|
 | 
						|
                    for (j=0; j < part.modifiers.length; j++) {
 | 
						|
                        if (part.modifiers[j].type === "pseudo") {
 | 
						|
                            pseudo = true;
 | 
						|
                            break;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (!pseudo) {
 | 
						|
                        headings[RegExp.$1]++;
 | 
						|
                        if (headings[RegExp.$1] > 1) {
 | 
						|
                            reporter.report("Heading (" + part.elementName + ") has already been defined.", part.line, part.col, rule);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("endstylesheet", function() {
 | 
						|
            var prop,
 | 
						|
                messages = [];
 | 
						|
 | 
						|
            for (prop in headings) {
 | 
						|
                if (headings.hasOwnProperty(prop)) {
 | 
						|
                    if (headings[prop] > 1) {
 | 
						|
                        messages.push(headings[prop] + " " + prop + "s");
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if (messages.length) {
 | 
						|
                reporter.rollupWarn("You have " + messages.join(", ") + " defined in this stylesheet.", rule);
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Don't use universal selector because it's slow.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "universal-selector",
 | 
						|
    name: "Disallow universal selector",
 | 
						|
    desc: "The universal selector (*) is known to be slow.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-universal-selector",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this;
 | 
						|
 | 
						|
        parser.addListener("startrule", function(event) {
 | 
						|
            var selectors = event.selectors,
 | 
						|
                selector,
 | 
						|
                part,
 | 
						|
                i;
 | 
						|
 | 
						|
            for (i=0; i < selectors.length; i++) {
 | 
						|
                selector = selectors[i];
 | 
						|
 | 
						|
                part = selector.parts[selector.parts.length-1];
 | 
						|
                if (part.elementName === "*") {
 | 
						|
                    reporter.report(rule.desc, part.line, part.col, rule);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: Don't use unqualified attribute selectors because they're just like universal selectors.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "unqualified-attributes",
 | 
						|
    name: "Disallow unqualified attribute selectors",
 | 
						|
    desc: "Unqualified attribute selectors are known to be slow.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-unqualified-attribute-selectors",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
 | 
						|
        var rule = this;
 | 
						|
 | 
						|
        parser.addListener("startrule", function(event) {
 | 
						|
 | 
						|
            var selectors = event.selectors,
 | 
						|
                selectorContainsClassOrId = false,
 | 
						|
                selector,
 | 
						|
                part,
 | 
						|
                modifier,
 | 
						|
                i, k;
 | 
						|
 | 
						|
            for (i=0; i < selectors.length; i++) {
 | 
						|
                selector = selectors[i];
 | 
						|
 | 
						|
                part = selector.parts[selector.parts.length-1];
 | 
						|
                if (part.type === parser.SELECTOR_PART_TYPE) {
 | 
						|
                    for (k=0; k < part.modifiers.length; k++) {
 | 
						|
                        modifier = part.modifiers[k];
 | 
						|
 | 
						|
                        if (modifier.type === "class" || modifier.type === "id") {
 | 
						|
                            selectorContainsClassOrId = true;
 | 
						|
                            break;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (!selectorContainsClassOrId) {
 | 
						|
                        for (k=0; k < part.modifiers.length; k++) {
 | 
						|
                            modifier = part.modifiers[k];
 | 
						|
                            if (modifier.type === "attribute" && (!part.elementName || part.elementName === "*")) {
 | 
						|
                                reporter.report(rule.desc, part.line, part.col, rule);
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: When using a vendor-prefixed property, make sure to
 | 
						|
 * include the standard one.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "vendor-prefix",
 | 
						|
    name: "Require standard property with vendor prefix",
 | 
						|
    desc: "When using a vendor-prefixed property, make sure to include the standard one.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Require-standard-property-with-vendor-prefix",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this,
 | 
						|
            properties,
 | 
						|
            num,
 | 
						|
            propertiesToCheck = {
 | 
						|
                "-webkit-border-radius": "border-radius",
 | 
						|
                "-webkit-border-top-left-radius": "border-top-left-radius",
 | 
						|
                "-webkit-border-top-right-radius": "border-top-right-radius",
 | 
						|
                "-webkit-border-bottom-left-radius": "border-bottom-left-radius",
 | 
						|
                "-webkit-border-bottom-right-radius": "border-bottom-right-radius",
 | 
						|
 | 
						|
                "-o-border-radius": "border-radius",
 | 
						|
                "-o-border-top-left-radius": "border-top-left-radius",
 | 
						|
                "-o-border-top-right-radius": "border-top-right-radius",
 | 
						|
                "-o-border-bottom-left-radius": "border-bottom-left-radius",
 | 
						|
                "-o-border-bottom-right-radius": "border-bottom-right-radius",
 | 
						|
 | 
						|
                "-moz-border-radius": "border-radius",
 | 
						|
                "-moz-border-radius-topleft": "border-top-left-radius",
 | 
						|
                "-moz-border-radius-topright": "border-top-right-radius",
 | 
						|
                "-moz-border-radius-bottomleft": "border-bottom-left-radius",
 | 
						|
                "-moz-border-radius-bottomright": "border-bottom-right-radius",
 | 
						|
 | 
						|
                "-moz-column-count": "column-count",
 | 
						|
                "-webkit-column-count": "column-count",
 | 
						|
 | 
						|
                "-moz-column-gap": "column-gap",
 | 
						|
                "-webkit-column-gap": "column-gap",
 | 
						|
 | 
						|
                "-moz-column-rule": "column-rule",
 | 
						|
                "-webkit-column-rule": "column-rule",
 | 
						|
 | 
						|
                "-moz-column-rule-style": "column-rule-style",
 | 
						|
                "-webkit-column-rule-style": "column-rule-style",
 | 
						|
 | 
						|
                "-moz-column-rule-color": "column-rule-color",
 | 
						|
                "-webkit-column-rule-color": "column-rule-color",
 | 
						|
 | 
						|
                "-moz-column-rule-width": "column-rule-width",
 | 
						|
                "-webkit-column-rule-width": "column-rule-width",
 | 
						|
 | 
						|
                "-moz-column-width": "column-width",
 | 
						|
                "-webkit-column-width": "column-width",
 | 
						|
 | 
						|
                "-webkit-column-span": "column-span",
 | 
						|
                "-webkit-columns": "columns",
 | 
						|
 | 
						|
                "-moz-box-shadow": "box-shadow",
 | 
						|
                "-webkit-box-shadow": "box-shadow",
 | 
						|
 | 
						|
                "-moz-transform": "transform",
 | 
						|
                "-webkit-transform": "transform",
 | 
						|
                "-o-transform": "transform",
 | 
						|
                "-ms-transform": "transform",
 | 
						|
 | 
						|
                "-moz-transform-origin": "transform-origin",
 | 
						|
                "-webkit-transform-origin": "transform-origin",
 | 
						|
                "-o-transform-origin": "transform-origin",
 | 
						|
                "-ms-transform-origin": "transform-origin",
 | 
						|
 | 
						|
                "-moz-box-sizing": "box-sizing",
 | 
						|
                "-webkit-box-sizing": "box-sizing"
 | 
						|
            };
 | 
						|
 | 
						|
        // event handler for beginning of rules
 | 
						|
        function startRule() {
 | 
						|
            properties = {};
 | 
						|
            num = 1;
 | 
						|
        }
 | 
						|
 | 
						|
        // event handler for end of rules
 | 
						|
        function endRule() {
 | 
						|
            var prop,
 | 
						|
                i,
 | 
						|
                len,
 | 
						|
                needed,
 | 
						|
                actual,
 | 
						|
                needsStandard = [];
 | 
						|
 | 
						|
            for (prop in properties) {
 | 
						|
                if (propertiesToCheck[prop]) {
 | 
						|
                    needsStandard.push({
 | 
						|
                        actual: prop,
 | 
						|
                        needed: propertiesToCheck[prop]
 | 
						|
                    });
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            for (i=0, len=needsStandard.length; i < len; i++) {
 | 
						|
                needed = needsStandard[i].needed;
 | 
						|
                actual = needsStandard[i].actual;
 | 
						|
 | 
						|
                if (!properties[needed]) {
 | 
						|
                    reporter.report("Missing standard property '" + needed + "' to go along with '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
 | 
						|
                } else {
 | 
						|
                    // make sure standard property is last
 | 
						|
                    if (properties[needed][0].pos < properties[actual][0].pos) {
 | 
						|
                        reporter.report("Standard property '" + needed + "' should come after vendor-prefixed property '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        parser.addListener("startrule", startRule);
 | 
						|
        parser.addListener("startfontface", startRule);
 | 
						|
        parser.addListener("startpage", startRule);
 | 
						|
        parser.addListener("startpagemargin", startRule);
 | 
						|
        parser.addListener("startkeyframerule", startRule);
 | 
						|
        parser.addListener("startviewport", startRule);
 | 
						|
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
            var name = event.property.text.toLowerCase();
 | 
						|
 | 
						|
            if (!properties[name]) {
 | 
						|
                properties[name] = [];
 | 
						|
            }
 | 
						|
 | 
						|
            properties[name].push({
 | 
						|
                name: event.property,
 | 
						|
                value: event.value,
 | 
						|
                pos: num++
 | 
						|
            });
 | 
						|
        });
 | 
						|
 | 
						|
        parser.addListener("endrule", endRule);
 | 
						|
        parser.addListener("endfontface", endRule);
 | 
						|
        parser.addListener("endpage", endRule);
 | 
						|
        parser.addListener("endpagemargin", endRule);
 | 
						|
        parser.addListener("endkeyframerule", endRule);
 | 
						|
        parser.addListener("endviewport", endRule);
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
/*
 | 
						|
 * Rule: You don't need to specify units when a value is 0.
 | 
						|
 */
 | 
						|
 | 
						|
CSSLint.addRule({
 | 
						|
 | 
						|
    // rule information
 | 
						|
    id: "zero-units",
 | 
						|
    name: "Disallow units for 0 values",
 | 
						|
    desc: "You don't need to specify units when a value is 0.",
 | 
						|
    url: "https://github.com/CSSLint/csslint/wiki/Disallow-units-for-zero-values",
 | 
						|
    browsers: "All",
 | 
						|
 | 
						|
    // initialization
 | 
						|
    init: function(parser, reporter) {
 | 
						|
        "use strict";
 | 
						|
        var rule = this;
 | 
						|
 | 
						|
        // count how many times "float" is used
 | 
						|
        parser.addListener("property", function(event) {
 | 
						|
            var parts = event.value.parts,
 | 
						|
                i = 0,
 | 
						|
                len = parts.length;
 | 
						|
 | 
						|
            while (i < len) {
 | 
						|
                if ((parts[i].units || parts[i].type === "percentage") && parts[i].value === 0 && parts[i].type !== "time") {
 | 
						|
                    reporter.report("Values of 0 shouldn't have units specified.", parts[i].line, parts[i].col, rule);
 | 
						|
                }
 | 
						|
                i++;
 | 
						|
            }
 | 
						|
 | 
						|
        });
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
});
 | 
						|
 | 
						|
(function() {
 | 
						|
    "use strict";
 | 
						|
 | 
						|
    /**
 | 
						|
     * Replace special characters before write to output.
 | 
						|
     *
 | 
						|
     * Rules:
 | 
						|
     *  - single quotes is the escape sequence for double-quotes
 | 
						|
     *  - & is the escape sequence for &
 | 
						|
     *  - < is the escape sequence for <
 | 
						|
     *  - > is the escape sequence for >
 | 
						|
     *
 | 
						|
     * @param {String} message to escape
 | 
						|
     * @return escaped message as {String}
 | 
						|
     */
 | 
						|
    var xmlEscape = function(str) {
 | 
						|
        if (!str || str.constructor !== String) {
 | 
						|
            return "";
 | 
						|
        }
 | 
						|
 | 
						|
        return str.replace(/["&><]/g, function(match) {
 | 
						|
            switch (match) {
 | 
						|
                case "\"":
 | 
						|
                    return """;
 | 
						|
                case "&":
 | 
						|
                    return "&";
 | 
						|
                case "<":
 | 
						|
                    return "<";
 | 
						|
                case ">":
 | 
						|
                    return ">";
 | 
						|
            }
 | 
						|
        });
 | 
						|
    };
 | 
						|
 | 
						|
    CSSLint.addFormatter({
 | 
						|
        // format information
 | 
						|
        id: "checkstyle-xml",
 | 
						|
        name: "Checkstyle XML format",
 | 
						|
 | 
						|
        /**
 | 
						|
         * Return opening root XML tag.
 | 
						|
         * @return {String} to prepend before all results
 | 
						|
         */
 | 
						|
        startFormat: function() {
 | 
						|
            return "<?xml version=\"1.0\" encoding=\"utf-8\"?><checkstyle>";
 | 
						|
        },
 | 
						|
 | 
						|
        /**
 | 
						|
         * Return closing root XML tag.
 | 
						|
         * @return {String} to append after all results
 | 
						|
         */
 | 
						|
        endFormat: function() {
 | 
						|
            return "</checkstyle>";
 | 
						|
        },
 | 
						|
 | 
						|
        /**
 | 
						|
         * Returns message when there is a file read error.
 | 
						|
         * @param {String} filename The name of the file that caused the error.
 | 
						|
         * @param {String} message The error message
 | 
						|
         * @return {String} The error message.
 | 
						|
         */
 | 
						|
        readError: function(filename, message) {
 | 
						|
            return "<file name=\"" + xmlEscape(filename) + "\"><error line=\"0\" column=\"0\" severty=\"error\" message=\"" + xmlEscape(message) + "\"></error></file>";
 | 
						|
        },
 | 
						|
 | 
						|
        /**
 | 
						|
         * Given CSS Lint results for a file, return output for this format.
 | 
						|
         * @param results {Object} with error and warning messages
 | 
						|
         * @param filename {String} relative file path
 | 
						|
         * @param options {Object} (UNUSED for now) specifies special handling of output
 | 
						|
         * @return {String} output for results
 | 
						|
         */
 | 
						|
        formatResults: function(results, filename/*, options*/) {
 | 
						|
            var messages = results.messages,
 | 
						|
                output = [];
 | 
						|
 | 
						|
            /**
 | 
						|
             * Generate a source string for a rule.
 | 
						|
             * Checkstyle source strings usually resemble Java class names e.g
 | 
						|
             * net.csslint.SomeRuleName
 | 
						|
             * @param {Object} rule
 | 
						|
             * @return rule source as {String}
 | 
						|
             */
 | 
						|
            var generateSource = function(rule) {
 | 
						|
                if (!rule || !("name" in rule)) {
 | 
						|
                    return "";
 | 
						|
                }
 | 
						|
                return "net.csslint." + rule.name.replace(/\s/g, "");
 | 
						|
            };
 | 
						|
 | 
						|
 | 
						|
            if (messages.length > 0) {
 | 
						|
                output.push("<file name=\""+filename+"\">");
 | 
						|
                CSSLint.Util.forEach(messages, function (message) {
 | 
						|
                    // ignore rollups for now
 | 
						|
                    if (!message.rollup) {
 | 
						|
                        output.push("<error line=\"" + message.line + "\" column=\"" + message.col + "\" severity=\"" + message.type + "\"" +
 | 
						|
                          " message=\"" + xmlEscape(message.message) + "\" source=\"" + generateSource(message.rule) +"\"/>");
 | 
						|
                    }
 | 
						|
                });
 | 
						|
                output.push("</file>");
 | 
						|
            }
 | 
						|
 | 
						|
            return output.join("");
 | 
						|
        }
 | 
						|
    });
 | 
						|
 | 
						|
}());
 | 
						|
 | 
						|
CSSLint.addFormatter({
 | 
						|
    // format information
 | 
						|
    id: "compact",
 | 
						|
    name: "Compact, 'porcelain' format",
 | 
						|
 | 
						|
    /**
 | 
						|
     * Return content to be printed before all file results.
 | 
						|
     * @return {String} to prepend before all results
 | 
						|
     */
 | 
						|
    startFormat: function() {
 | 
						|
        "use strict";
 | 
						|
        return "";
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Return content to be printed after all file results.
 | 
						|
     * @return {String} to append after all results
 | 
						|
     */
 | 
						|
    endFormat: function() {
 | 
						|
        "use strict";
 | 
						|
        return "";
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Given CSS Lint results for a file, return output for this format.
 | 
						|
     * @param results {Object} with error and warning messages
 | 
						|
     * @param filename {String} relative file path
 | 
						|
     * @param options {Object} (Optional) specifies special handling of output
 | 
						|
     * @return {String} output for results
 | 
						|
     */
 | 
						|
    formatResults: function(results, filename, options) {
 | 
						|
        "use strict";
 | 
						|
        var messages = results.messages,
 | 
						|
            output = "";
 | 
						|
        options = options || {};
 | 
						|
 | 
						|
        /**
 | 
						|
         * Capitalize and return given string.
 | 
						|
         * @param str {String} to capitalize
 | 
						|
         * @return {String} capitalized
 | 
						|
         */
 | 
						|
        var capitalize = function(str) {
 | 
						|
            return str.charAt(0).toUpperCase() + str.slice(1);
 | 
						|
        };
 | 
						|
 | 
						|
        if (messages.length === 0) {
 | 
						|
            return options.quiet ? "" : filename + ": Lint Free!";
 | 
						|
        }
 | 
						|
 | 
						|
        CSSLint.Util.forEach(messages, function(message) {
 | 
						|
            if (message.rollup) {
 | 
						|
                output += filename + ": " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
 | 
						|
            } else {
 | 
						|
                output += filename + ": line " + message.line +
 | 
						|
                    ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
 | 
						|
            }
 | 
						|
        });
 | 
						|
 | 
						|
        return output;
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
CSSLint.addFormatter({
 | 
						|
    // format information
 | 
						|
    id: "csslint-xml",
 | 
						|
    name: "CSSLint XML format",
 | 
						|
 | 
						|
    /**
 | 
						|
     * Return opening root XML tag.
 | 
						|
     * @return {String} to prepend before all results
 | 
						|
     */
 | 
						|
    startFormat: function() {
 | 
						|
        "use strict";
 | 
						|
        return "<?xml version=\"1.0\" encoding=\"utf-8\"?><csslint>";
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Return closing root XML tag.
 | 
						|
     * @return {String} to append after all results
 | 
						|
     */
 | 
						|
    endFormat: function() {
 | 
						|
        "use strict";
 | 
						|
        return "</csslint>";
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Given CSS Lint results for a file, return output for this format.
 | 
						|
     * @param results {Object} with error and warning messages
 | 
						|
     * @param filename {String} relative file path
 | 
						|
     * @param options {Object} (UNUSED for now) specifies special handling of output
 | 
						|
     * @return {String} output for results
 | 
						|
     */
 | 
						|
    formatResults: function(results, filename/*, options*/) {
 | 
						|
        "use strict";
 | 
						|
        var messages = results.messages,
 | 
						|
            output = [];
 | 
						|
 | 
						|
        /**
 | 
						|
         * Replace special characters before write to output.
 | 
						|
         *
 | 
						|
         * Rules:
 | 
						|
         *  - single quotes is the escape sequence for double-quotes
 | 
						|
         *  - & is the escape sequence for &
 | 
						|
         *  - < is the escape sequence for <
 | 
						|
         *  - > is the escape sequence for >
 | 
						|
         *
 | 
						|
         * @param {String} message to escape
 | 
						|
         * @return escaped message as {String}
 | 
						|
         */
 | 
						|
        var escapeSpecialCharacters = function(str) {
 | 
						|
            if (!str || str.constructor !== String) {
 | 
						|
                return "";
 | 
						|
            }
 | 
						|
            return str.replace(/"/g, "'").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
 | 
						|
        };
 | 
						|
 | 
						|
        if (messages.length > 0) {
 | 
						|
            output.push("<file name=\""+filename+"\">");
 | 
						|
            CSSLint.Util.forEach(messages, function (message) {
 | 
						|
                if (message.rollup) {
 | 
						|
                    output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
 | 
						|
                } else {
 | 
						|
                    output.push("<issue line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
 | 
						|
                        " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
 | 
						|
                }
 | 
						|
            });
 | 
						|
            output.push("</file>");
 | 
						|
        }
 | 
						|
 | 
						|
        return output.join("");
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
/* globals JSON: true */
 | 
						|
 | 
						|
CSSLint.addFormatter({
 | 
						|
    // format information
 | 
						|
    id: "json",
 | 
						|
    name: "JSON",
 | 
						|
 | 
						|
    /**
 | 
						|
     * Return content to be printed before all file results.
 | 
						|
     * @return {String} to prepend before all results
 | 
						|
     */
 | 
						|
    startFormat: function() {
 | 
						|
        "use strict";
 | 
						|
        this.json = [];
 | 
						|
        return "";
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Return content to be printed after all file results.
 | 
						|
     * @return {String} to append after all results
 | 
						|
     */
 | 
						|
    endFormat: function() {
 | 
						|
        "use strict";
 | 
						|
        var ret = "";
 | 
						|
        if (this.json.length > 0) {
 | 
						|
            if (this.json.length === 1) {
 | 
						|
                ret = JSON.stringify(this.json[0]);
 | 
						|
            } else {
 | 
						|
                ret = JSON.stringify(this.json);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return ret;
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Given CSS Lint results for a file, return output for this format.
 | 
						|
     * @param results {Object} with error and warning messages
 | 
						|
     * @param filename {String} relative file path (Unused)
 | 
						|
     * @return {String} output for results
 | 
						|
     */
 | 
						|
    formatResults: function(results, filename, options) {
 | 
						|
        "use strict";
 | 
						|
        if (results.messages.length > 0 || !options.quiet) {
 | 
						|
            this.json.push({
 | 
						|
                filename: filename,
 | 
						|
                messages: results.messages,
 | 
						|
                stats: results.stats
 | 
						|
            });
 | 
						|
        }
 | 
						|
        return "";
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
CSSLint.addFormatter({
 | 
						|
    // format information
 | 
						|
    id: "junit-xml",
 | 
						|
    name: "JUNIT XML format",
 | 
						|
 | 
						|
    /**
 | 
						|
     * Return opening root XML tag.
 | 
						|
     * @return {String} to prepend before all results
 | 
						|
     */
 | 
						|
    startFormat: function() {
 | 
						|
        "use strict";
 | 
						|
        return "<?xml version=\"1.0\" encoding=\"utf-8\"?><testsuites>";
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Return closing root XML tag.
 | 
						|
     * @return {String} to append after all results
 | 
						|
     */
 | 
						|
    endFormat: function() {
 | 
						|
        "use strict";
 | 
						|
        return "</testsuites>";
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Given CSS Lint results for a file, return output for this format.
 | 
						|
     * @param results {Object} with error and warning messages
 | 
						|
     * @param filename {String} relative file path
 | 
						|
     * @param options {Object} (UNUSED for now) specifies special handling of output
 | 
						|
     * @return {String} output for results
 | 
						|
     */
 | 
						|
    formatResults: function(results, filename/*, options*/) {
 | 
						|
        "use strict";
 | 
						|
 | 
						|
        var messages = results.messages,
 | 
						|
            output = [],
 | 
						|
            tests = {
 | 
						|
                "error": 0,
 | 
						|
                "failure": 0
 | 
						|
            };
 | 
						|
 | 
						|
        /**
 | 
						|
         * Generate a source string for a rule.
 | 
						|
         * JUNIT source strings usually resemble Java class names e.g
 | 
						|
         * net.csslint.SomeRuleName
 | 
						|
         * @param {Object} rule
 | 
						|
         * @return rule source as {String}
 | 
						|
         */
 | 
						|
        var generateSource = function(rule) {
 | 
						|
            if (!rule || !("name" in rule)) {
 | 
						|
                return "";
 | 
						|
            }
 | 
						|
            return "net.csslint." + rule.name.replace(/\s/g, "");
 | 
						|
        };
 | 
						|
 | 
						|
        /**
 | 
						|
         * Replace special characters before write to output.
 | 
						|
         *
 | 
						|
         * Rules:
 | 
						|
         *  - single quotes is the escape sequence for double-quotes
 | 
						|
         *  - < is the escape sequence for <
 | 
						|
         *  - > is the escape sequence for >
 | 
						|
         *
 | 
						|
         * @param {String} message to escape
 | 
						|
         * @return escaped message as {String}
 | 
						|
         */
 | 
						|
        var escapeSpecialCharacters = function(str) {
 | 
						|
 | 
						|
            if (!str || str.constructor !== String) {
 | 
						|
                return "";
 | 
						|
            }
 | 
						|
 | 
						|
            return str.replace(/"/g, "'").replace(/</g, "<").replace(/>/g, ">");
 | 
						|
 | 
						|
        };
 | 
						|
 | 
						|
        if (messages.length > 0) {
 | 
						|
 | 
						|
            messages.forEach(function (message) {
 | 
						|
 | 
						|
                // since junit has no warning class
 | 
						|
                // all issues as errors
 | 
						|
                var type = message.type === "warning" ? "error" : message.type;
 | 
						|
 | 
						|
                // ignore rollups for now
 | 
						|
                if (!message.rollup) {
 | 
						|
 | 
						|
                    // build the test case separately, once joined
 | 
						|
                    // we'll add it to a custom array filtered by type
 | 
						|
                    output.push("<testcase time=\"0\" name=\"" + generateSource(message.rule) + "\">");
 | 
						|
                    output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\"><![CDATA[" + message.line + ":" + message.col + ":" + escapeSpecialCharacters(message.evidence) + "]]></" + type + ">");
 | 
						|
                    output.push("</testcase>");
 | 
						|
 | 
						|
                    tests[type] += 1;
 | 
						|
 | 
						|
                }
 | 
						|
 | 
						|
            });
 | 
						|
 | 
						|
            output.unshift("<testsuite time=\"0\" tests=\"" + messages.length + "\" skipped=\"0\" errors=\"" + tests.error + "\" failures=\"" + tests.failure + "\" package=\"net.csslint\" name=\"" + filename + "\">");
 | 
						|
            output.push("</testsuite>");
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        return output.join("");
 | 
						|
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
CSSLint.addFormatter({
 | 
						|
    // format information
 | 
						|
    id: "lint-xml",
 | 
						|
    name: "Lint XML format",
 | 
						|
 | 
						|
    /**
 | 
						|
     * Return opening root XML tag.
 | 
						|
     * @return {String} to prepend before all results
 | 
						|
     */
 | 
						|
    startFormat: function() {
 | 
						|
        "use strict";
 | 
						|
        return "<?xml version=\"1.0\" encoding=\"utf-8\"?><lint>";
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Return closing root XML tag.
 | 
						|
     * @return {String} to append after all results
 | 
						|
     */
 | 
						|
    endFormat: function() {
 | 
						|
        "use strict";
 | 
						|
        return "</lint>";
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Given CSS Lint results for a file, return output for this format.
 | 
						|
     * @param results {Object} with error and warning messages
 | 
						|
     * @param filename {String} relative file path
 | 
						|
     * @param options {Object} (UNUSED for now) specifies special handling of output
 | 
						|
     * @return {String} output for results
 | 
						|
     */
 | 
						|
    formatResults: function(results, filename/*, options*/) {
 | 
						|
        "use strict";
 | 
						|
        var messages = results.messages,
 | 
						|
            output = [];
 | 
						|
 | 
						|
        /**
 | 
						|
         * Replace special characters before write to output.
 | 
						|
         *
 | 
						|
         * Rules:
 | 
						|
         *  - single quotes is the escape sequence for double-quotes
 | 
						|
         *  - & is the escape sequence for &
 | 
						|
         *  - < is the escape sequence for <
 | 
						|
         *  - > is the escape sequence for >
 | 
						|
         *
 | 
						|
         * @param {String} message to escape
 | 
						|
         * @return escaped message as {String}
 | 
						|
         */
 | 
						|
        var escapeSpecialCharacters = function(str) {
 | 
						|
            if (!str || str.constructor !== String) {
 | 
						|
                return "";
 | 
						|
            }
 | 
						|
            return str.replace(/"/g, "'").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
 | 
						|
        };
 | 
						|
 | 
						|
        if (messages.length > 0) {
 | 
						|
 | 
						|
            output.push("<file name=\""+filename+"\">");
 | 
						|
            CSSLint.Util.forEach(messages, function (message) {
 | 
						|
                if (message.rollup) {
 | 
						|
                    output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
 | 
						|
                } else {
 | 
						|
                    var rule = "";
 | 
						|
                    if (message.rule && message.rule.id) {
 | 
						|
                        rule = "rule=\"" + escapeSpecialCharacters(message.rule.id) + "\" ";
 | 
						|
                    }
 | 
						|
                    output.push("<issue " + rule + "line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
 | 
						|
                        " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
 | 
						|
                }
 | 
						|
            });
 | 
						|
            output.push("</file>");
 | 
						|
        }
 | 
						|
 | 
						|
        return output.join("");
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
CSSLint.addFormatter({
 | 
						|
    // format information
 | 
						|
    id: "text",
 | 
						|
    name: "Plain Text",
 | 
						|
 | 
						|
    /**
 | 
						|
     * Return content to be printed before all file results.
 | 
						|
     * @return {String} to prepend before all results
 | 
						|
     */
 | 
						|
    startFormat: function() {
 | 
						|
        "use strict";
 | 
						|
        return "";
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Return content to be printed after all file results.
 | 
						|
     * @return {String} to append after all results
 | 
						|
     */
 | 
						|
    endFormat: function() {
 | 
						|
        "use strict";
 | 
						|
        return "";
 | 
						|
    },
 | 
						|
 | 
						|
    /**
 | 
						|
     * Given CSS Lint results for a file, return output for this format.
 | 
						|
     * @param results {Object} with error and warning messages
 | 
						|
     * @param filename {String} relative file path
 | 
						|
     * @param options {Object} (Optional) specifies special handling of output
 | 
						|
     * @return {String} output for results
 | 
						|
     */
 | 
						|
    formatResults: function(results, filename, options) {
 | 
						|
        "use strict";
 | 
						|
        var messages = results.messages,
 | 
						|
            output = "";
 | 
						|
        options = options || {};
 | 
						|
 | 
						|
        if (messages.length === 0) {
 | 
						|
            return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + ".";
 | 
						|
        }
 | 
						|
 | 
						|
        output = "\n\ncsslint: There ";
 | 
						|
        if (messages.length === 1) {
 | 
						|
            output += "is 1 problem";
 | 
						|
        } else {
 | 
						|
            output += "are " + messages.length + " problems";
 | 
						|
        }
 | 
						|
        output += " in " + filename + ".";
 | 
						|
 | 
						|
        var pos = filename.lastIndexOf("/"),
 | 
						|
            shortFilename = filename;
 | 
						|
 | 
						|
        if (pos === -1) {
 | 
						|
            pos = filename.lastIndexOf("\\");
 | 
						|
        }
 | 
						|
        if (pos > -1) {
 | 
						|
            shortFilename = filename.substring(pos+1);
 | 
						|
        }
 | 
						|
 | 
						|
        CSSLint.Util.forEach(messages, function (message, i) {
 | 
						|
            output = output + "\n\n" + shortFilename;
 | 
						|
            if (message.rollup) {
 | 
						|
                output += "\n" + (i+1) + ": " + message.type;
 | 
						|
                output += "\n" + message.message;
 | 
						|
            } else {
 | 
						|
                output += "\n" + (i+1) + ": " + message.type + " at line " + message.line + ", col " + message.col;
 | 
						|
                output += "\n" + message.message;
 | 
						|
                output += "\n" + message.evidence;
 | 
						|
            }
 | 
						|
        });
 | 
						|
 | 
						|
        return output;
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
return CSSLint;
 | 
						|
})(); |