Diff: STRATO-apps/wordpress_03/app/wp-includes/js/codemirror/csslint.js

Keine Baseline-Datei – Diff nur gegen leer.
Zur Liste
1 -
1 + /*!
2 + CSSLint v1.0.4
3 + Copyright (c) 2016 Nicole Sullivan and Nicholas C. Zakas. All rights reserved.
4 +
5 + Permission is hereby granted, free of charge, to any person obtaining a copy
6 + of this software and associated documentation files (the 'Software'), to deal
7 + in the Software without restriction, including without limitation the rights
8 + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 + copies of the Software, and to permit persons to whom the Software is
10 + furnished to do so, subject to the following conditions:
11 +
12 + The above copyright notice and this permission notice shall be included in
13 + all copies or substantial portions of the Software.
14 +
15 + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 + THE SOFTWARE.
22 +
23 + */
24 +
25 + var CSSLint = (function(){
26 + var module = module || {},
27 + exports = exports || {};
28 +
29 + /*!
30 + Parser-Lib
31 + Copyright (c) 2009-2016 Nicholas C. Zakas. All rights reserved.
32 +
33 + Permission is hereby granted, free of charge, to any person obtaining a copy
34 + of this software and associated documentation files (the "Software"), to deal
35 + in the Software without restriction, including without limitation the rights
36 + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
37 + copies of the Software, and to permit persons to whom the Software is
38 + furnished to do so, subject to the following conditions:
39 +
40 + The above copyright notice and this permission notice shall be included in
41 + all copies or substantial portions of the Software.
42 +
43 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
44 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
45 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
46 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
47 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
48 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
49 + THE SOFTWARE.
50 + */
51 + /* Version v1.1.0, Build time: 6-December-2016 10:31:29 */
52 + var parserlib = (function () {
53 + var require;
54 + require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
55 + "use strict";
56 +
57 + /* exported Colors */
58 +
59 + var Colors = module.exports = {
60 + __proto__ :null,
61 + aliceblue :"#f0f8ff",
62 + antiquewhite :"#faebd7",
63 + aqua :"#00ffff",
64 + aquamarine :"#7fffd4",
65 + azure :"#f0ffff",
66 + beige :"#f5f5dc",
67 + bisque :"#ffe4c4",
68 + black :"#000000",
69 + blanchedalmond :"#ffebcd",
70 + blue :"#0000ff",
71 + blueviolet :"#8a2be2",
72 + brown :"#a52a2a",
73 + burlywood :"#deb887",
74 + cadetblue :"#5f9ea0",
75 + chartreuse :"#7fff00",
76 + chocolate :"#d2691e",
77 + coral :"#ff7f50",
78 + cornflowerblue :"#6495ed",
79 + cornsilk :"#fff8dc",
80 + crimson :"#dc143c",
81 + cyan :"#00ffff",
82 + darkblue :"#00008b",
83 + darkcyan :"#008b8b",
84 + darkgoldenrod :"#b8860b",
85 + darkgray :"#a9a9a9",
86 + darkgrey :"#a9a9a9",
87 + darkgreen :"#006400",
88 + darkkhaki :"#bdb76b",
89 + darkmagenta :"#8b008b",
90 + darkolivegreen :"#556b2f",
91 + darkorange :"#ff8c00",
92 + darkorchid :"#9932cc",
93 + darkred :"#8b0000",
94 + darksalmon :"#e9967a",
95 + darkseagreen :"#8fbc8f",
96 + darkslateblue :"#483d8b",
97 + darkslategray :"#2f4f4f",
98 + darkslategrey :"#2f4f4f",
99 + darkturquoise :"#00ced1",
100 + darkviolet :"#9400d3",
101 + deeppink :"#ff1493",
102 + deepskyblue :"#00bfff",
103 + dimgray :"#696969",
104 + dimgrey :"#696969",
105 + dodgerblue :"#1e90ff",
106 + firebrick :"#b22222",
107 + floralwhite :"#fffaf0",
108 + forestgreen :"#228b22",
109 + fuchsia :"#ff00ff",
110 + gainsboro :"#dcdcdc",
111 + ghostwhite :"#f8f8ff",
112 + gold :"#ffd700",
113 + goldenrod :"#daa520",
114 + gray :"#808080",
115 + grey :"#808080",
116 + green :"#008000",
117 + greenyellow :"#adff2f",
118 + honeydew :"#f0fff0",
119 + hotpink :"#ff69b4",
120 + indianred :"#cd5c5c",
121 + indigo :"#4b0082",
122 + ivory :"#fffff0",
123 + khaki :"#f0e68c",
124 + lavender :"#e6e6fa",
125 + lavenderblush :"#fff0f5",
126 + lawngreen :"#7cfc00",
127 + lemonchiffon :"#fffacd",
128 + lightblue :"#add8e6",
129 + lightcoral :"#f08080",
130 + lightcyan :"#e0ffff",
131 + lightgoldenrodyellow :"#fafad2",
132 + lightgray :"#d3d3d3",
133 + lightgrey :"#d3d3d3",
134 + lightgreen :"#90ee90",
135 + lightpink :"#ffb6c1",
136 + lightsalmon :"#ffa07a",
137 + lightseagreen :"#20b2aa",
138 + lightskyblue :"#87cefa",
139 + lightslategray :"#778899",
140 + lightslategrey :"#778899",
141 + lightsteelblue :"#b0c4de",
142 + lightyellow :"#ffffe0",
143 + lime :"#00ff00",
144 + limegreen :"#32cd32",
145 + linen :"#faf0e6",
146 + magenta :"#ff00ff",
147 + maroon :"#800000",
148 + mediumaquamarine:"#66cdaa",
149 + mediumblue :"#0000cd",
150 + mediumorchid :"#ba55d3",
151 + mediumpurple :"#9370d8",
152 + mediumseagreen :"#3cb371",
153 + mediumslateblue :"#7b68ee",
154 + mediumspringgreen :"#00fa9a",
155 + mediumturquoise :"#48d1cc",
156 + mediumvioletred :"#c71585",
157 + midnightblue :"#191970",
158 + mintcream :"#f5fffa",
159 + mistyrose :"#ffe4e1",
160 + moccasin :"#ffe4b5",
161 + navajowhite :"#ffdead",
162 + navy :"#000080",
163 + oldlace :"#fdf5e6",
164 + olive :"#808000",
165 + olivedrab :"#6b8e23",
166 + orange :"#ffa500",
167 + orangered :"#ff4500",
168 + orchid :"#da70d6",
169 + palegoldenrod :"#eee8aa",
170 + palegreen :"#98fb98",
171 + paleturquoise :"#afeeee",
172 + palevioletred :"#d87093",
173 + papayawhip :"#ffefd5",
174 + peachpuff :"#ffdab9",
175 + peru :"#cd853f",
176 + pink :"#ffc0cb",
177 + plum :"#dda0dd",
178 + powderblue :"#b0e0e6",
179 + purple :"#800080",
180 + red :"#ff0000",
181 + rosybrown :"#bc8f8f",
182 + royalblue :"#4169e1",
183 + saddlebrown :"#8b4513",
184 + salmon :"#fa8072",
185 + sandybrown :"#f4a460",
186 + seagreen :"#2e8b57",
187 + seashell :"#fff5ee",
188 + sienna :"#a0522d",
189 + silver :"#c0c0c0",
190 + skyblue :"#87ceeb",
191 + slateblue :"#6a5acd",
192 + slategray :"#708090",
193 + slategrey :"#708090",
194 + snow :"#fffafa",
195 + springgreen :"#00ff7f",
196 + steelblue :"#4682b4",
197 + tan :"#d2b48c",
198 + teal :"#008080",
199 + thistle :"#d8bfd8",
200 + tomato :"#ff6347",
201 + turquoise :"#40e0d0",
202 + violet :"#ee82ee",
203 + wheat :"#f5deb3",
204 + white :"#ffffff",
205 + whitesmoke :"#f5f5f5",
206 + yellow :"#ffff00",
207 + yellowgreen :"#9acd32",
208 + //'currentColor' color keyword https://www.w3.org/TR/css3-color/#currentcolor
209 + currentColor :"The value of the 'color' property.",
210 + //CSS2 system colors https://www.w3.org/TR/css3-color/#css2-system
211 + activeBorder :"Active window border.",
212 + activecaption :"Active window caption.",
213 + appworkspace :"Background color of multiple document interface.",
214 + background :"Desktop background.",
215 + buttonface :"The face background color for 3-D elements that appear 3-D due to one layer of surrounding border.",
216 + buttonhighlight :"The color of the border facing the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",
217 + buttonshadow :"The color of the border away from the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",
218 + buttontext :"Text on push buttons.",
219 + captiontext :"Text in caption, size box, and scrollbar arrow box.",
220 + graytext :"Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.",
221 + greytext :"Greyed (disabled) text. This color is set to #000 if the current display driver does not support a solid grey color.",
222 + highlight :"Item(s) selected in a control.",
223 + highlighttext :"Text of item(s) selected in a control.",
224 + inactiveborder :"Inactive window border.",
225 + inactivecaption :"Inactive window caption.",
226 + inactivecaptiontext :"Color of text in an inactive caption.",
227 + infobackground :"Background color for tooltip controls.",
228 + infotext :"Text color for tooltip controls.",
229 + menu :"Menu background.",
230 + menutext :"Text in menus.",
231 + scrollbar :"Scroll bar gray area.",
232 + threeddarkshadow :"The color of the darker (generally outer) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
233 + threedface :"The face background color for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
234 + threedhighlight :"The color of the lighter (generally outer) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
235 + threedlightshadow :"The color of the darker (generally inner) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
236 + threedshadow :"The color of the lighter (generally inner) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
237 + window :"Window background.",
238 + windowframe :"Window frame.",
239 + windowtext :"Text in windows."
240 + };
241 +
242 + },{}],2:[function(require,module,exports){
243 + "use strict";
244 +
245 + module.exports = Combinator;
246 +
247 + var SyntaxUnit = require("../util/SyntaxUnit");
248 +
249 + var Parser = require("./Parser");
250 +
251 + /**
252 + * Represents a selector combinator (whitespace, +, >).
253 + * @namespace parserlib.css
254 + * @class Combinator
255 + * @extends parserlib.util.SyntaxUnit
256 + * @constructor
257 + * @param {String} text The text representation of the unit.
258 + * @param {int} line The line of text on which the unit resides.
259 + * @param {int} col The column of text on which the unit resides.
260 + */
261 + function Combinator(text, line, col) {
262 +
263 + SyntaxUnit.call(this, text, line, col, Parser.COMBINATOR_TYPE);
264 +
265 + /**
266 + * The type of modifier.
267 + * @type String
268 + * @property type
269 + */
270 + this.type = "unknown";
271 +
272 + //pretty simple
273 + if (/^\s+$/.test(text)) {
274 + this.type = "descendant";
275 + } else if (text === ">") {
276 + this.type = "child";
277 + } else if (text === "+") {
278 + this.type = "adjacent-sibling";
279 + } else if (text === "~") {
280 + this.type = "sibling";
281 + }
282 +
283 + }
284 +
285 + Combinator.prototype = new SyntaxUnit();
286 + Combinator.prototype.constructor = Combinator;
287 +
288 +
289 + },{"../util/SyntaxUnit":26,"./Parser":6}],3:[function(require,module,exports){
290 + "use strict";
291 +
292 + module.exports = Matcher;
293 +
294 + var StringReader = require("../util/StringReader");
295 + var SyntaxError = require("../util/SyntaxError");
296 +
297 + /**
298 + * This class implements a combinator library for matcher functions.
299 + * The combinators are described at:
300 + * https://developer.mozilla.org/en-US/docs/Web/CSS/Value_definition_syntax#Component_value_combinators
301 + */
302 + function Matcher(matchFunc, toString) {
303 + this.match = function(expression) {
304 + // Save/restore marks to ensure that failed matches always restore
305 + // the original location in the expression.
306 + var result;
307 + expression.mark();
308 + result = matchFunc(expression);
309 + if (result) {
310 + expression.drop();
311 + } else {
312 + expression.restore();
313 + }
314 + return result;
315 + };
316 + this.toString = typeof toString === "function" ? toString : function() {
317 + return toString;
318 + };
319 + }
320 +
321 + /** Precedence table of combinators. */
322 + Matcher.prec = {
323 + MOD: 5,
324 + SEQ: 4,
325 + ANDAND: 3,
326 + OROR: 2,
327 + ALT: 1
328 + };
329 +
330 + /** Simple recursive-descent grammar to build matchers from strings. */
331 + Matcher.parse = function(str) {
332 + var reader, eat, expr, oror, andand, seq, mod, term, result;
333 + reader = new StringReader(str);
334 + eat = function(matcher) {
335 + var result = reader.readMatch(matcher);
336 + if (result === null) {
337 + throw new SyntaxError(
338 + "Expected "+matcher, reader.getLine(), reader.getCol());
339 + }
340 + return result;
341 + };
342 + expr = function() {
343 + // expr = oror (" | " oror)*
344 + var m = [ oror() ];
345 + while (reader.readMatch(" | ") !== null) {
346 + m.push(oror());
347 + }
348 + return m.length === 1 ? m[0] : Matcher.alt.apply(Matcher, m);
349 + };
350 + oror = function() {
351 + // oror = andand ( " || " andand)*
352 + var m = [ andand() ];
353 + while (reader.readMatch(" || ") !== null) {
354 + m.push(andand());
355 + }
356 + return m.length === 1 ? m[0] : Matcher.oror.apply(Matcher, m);
357 + };
358 + andand = function() {
359 + // andand = seq ( " && " seq)*
360 + var m = [ seq() ];
361 + while (reader.readMatch(" && ") !== null) {
362 + m.push(seq());
363 + }
364 + return m.length === 1 ? m[0] : Matcher.andand.apply(Matcher, m);
365 + };
366 + seq = function() {
367 + // seq = mod ( " " mod)*
368 + var m = [ mod() ];
369 + while (reader.readMatch(/^ (?![&|\]])/) !== null) {
370 + m.push(mod());
371 + }
372 + return m.length === 1 ? m[0] : Matcher.seq.apply(Matcher, m);
373 + };
374 + mod = function() {
375 + // mod = term ( "?" | "*" | "+" | "#" | "{<num>,<num>}" )?
376 + var m = term();
377 + if (reader.readMatch("?") !== null) {
378 + return m.question();
379 + } else if (reader.readMatch("*") !== null) {
380 + return m.star();
381 + } else if (reader.readMatch("+") !== null) {
382 + return m.plus();
383 + } else if (reader.readMatch("#") !== null) {
384 + return m.hash();
385 + } else if (reader.readMatch(/^\{\s*/) !== null) {
386 + var min = eat(/^\d+/);
387 + eat(/^\s*,\s*/);
388 + var max = eat(/^\d+/);
389 + eat(/^\s*\}/);
390 + return m.braces(+min, +max);
391 + }
392 + return m;
393 + };
394 + term = function() {
395 + // term = <nt> | literal | "[ " expression " ]"
396 + if (reader.readMatch("[ ") !== null) {
397 + var m = expr();
398 + eat(" ]");
399 + return m;
400 + }
401 + return Matcher.fromType(eat(/^[^ ?*+#{]+/));
402 + };
403 + result = expr();
404 + if (!reader.eof()) {
405 + throw new SyntaxError(
406 + "Expected end of string", reader.getLine(), reader.getCol());
407 + }
408 + return result;
409 + };
410 +
411 + /**
412 + * Convert a string to a matcher (parsing simple alternations),
413 + * or do nothing if the argument is already a matcher.
414 + */
415 + Matcher.cast = function(m) {
416 + if (m instanceof Matcher) {
417 + return m;
418 + }
419 + return Matcher.parse(m);
420 + };
421 +
422 + /**
423 + * Create a matcher for a single type.
424 + */
425 + Matcher.fromType = function(type) {
426 + // Late require of ValidationTypes to break a dependency cycle.
427 + var ValidationTypes = require("./ValidationTypes");
428 + return new Matcher(function(expression) {
429 + return expression.hasNext() && ValidationTypes.isType(expression, type);
430 + }, type);
431 + };
432 +
433 + /**
434 + * Create a matcher for one or more juxtaposed words, which all must
435 + * occur, in the given order.
436 + */
437 + Matcher.seq = function() {
438 + var ms = Array.prototype.slice.call(arguments).map(Matcher.cast);
439 + if (ms.length === 1) {
440 + return ms[0];
441 + }
442 + return new Matcher(function(expression) {
443 + var i, result = true;
444 + for (i = 0; result && i < ms.length; i++) {
445 + result = ms[i].match(expression);
446 + }
447 + return result;
448 + }, function(prec) {
449 + var p = Matcher.prec.SEQ;
450 + var s = ms.map(function(m) {
451 + return m.toString(p);
452 + }).join(" ");
453 + if (prec > p) {
454 + s = "[ " + s + " ]";
455 + }
456 + return s;
457 + });
458 + };
459 +
460 + /**
461 + * Create a matcher for one or more alternatives, where exactly one
462 + * must occur.
463 + */
464 + Matcher.alt = function() {
465 + var ms = Array.prototype.slice.call(arguments).map(Matcher.cast);
466 + if (ms.length === 1) {
467 + return ms[0];
468 + }
469 + return new Matcher(function(expression) {
470 + var i, result = false;
471 + for (i = 0; !result && i < ms.length; i++) {
472 + result = ms[i].match(expression);
473 + }
474 + return result;
475 + }, function(prec) {
476 + var p = Matcher.prec.ALT;
477 + var s = ms.map(function(m) {
478 + return m.toString(p);
479 + }).join(" | ");
480 + if (prec > p) {
481 + s = "[ " + s + " ]";
482 + }
483 + return s;
484 + });
485 + };
486 +
487 + /**
488 + * Create a matcher for two or more options. This implements the
489 + * double bar (||) and double ampersand (&&) operators, as well as
490 + * variants of && where some of the alternatives are optional.
491 + * This will backtrack through even successful matches to try to
492 + * maximize the number of items matched.
493 + */
494 + Matcher.many = function(required) {
495 + var ms = Array.prototype.slice.call(arguments, 1).reduce(function(acc, v) {
496 + if (v.expand) {
497 + // Insert all of the options for the given complex rule as
498 + // individual options.
499 + var ValidationTypes = require("./ValidationTypes");
500 + acc.push.apply(acc, ValidationTypes.complex[v.expand].options);
501 + } else {
502 + acc.push(Matcher.cast(v));
503 + }
504 + return acc;
505 + }, []);
506 +
507 + if (required === true) {
508 + required = ms.map(function() {
509 + return true;
510 + });
511 + }
512 +
513 + var result = new Matcher(function(expression) {
514 + var seen = [], max = 0, pass = 0;
515 + var success = function(matchCount) {
516 + if (pass === 0) {
517 + max = Math.max(matchCount, max);
518 + return matchCount === ms.length;
519 + } else {
520 + return matchCount === max;
521 + }
522 + };
523 + var tryMatch = function(matchCount) {
524 + for (var i = 0; i < ms.length; i++) {
525 + if (seen[i]) {
526 + continue;
527 + }
528 + expression.mark();
529 + if (ms[i].match(expression)) {
530 + seen[i] = true;
531 + // Increase matchCount iff this was a required element
532 + // (or if all the elements are optional)
533 + if (tryMatch(matchCount + ((required === false || required[i]) ? 1 : 0))) {
534 + expression.drop();
535 + return true;
536 + }
537 + // Backtrack: try *not* matching using this rule, and
538 + // let's see if it leads to a better overall match.
539 + expression.restore();
540 + seen[i] = false;
541 + } else {
542 + expression.drop();
543 + }
544 + }
545 + return success(matchCount);
546 + };
547 + if (!tryMatch(0)) {
548 + // Couldn't get a complete match, retrace our steps to make the
549 + // match with the maximum # of required elements.
550 + pass++;
551 + tryMatch(0);
552 + }
553 +
554 + if (required === false) {
555 + return max > 0;
556 + }
557 + // Use finer-grained specification of which matchers are required.
558 + for (var i = 0; i < ms.length; i++) {
559 + if (required[i] && !seen[i]) {
560 + return false;
561 + }
562 + }
563 + return true;
564 + }, function(prec) {
565 + var p = required === false ? Matcher.prec.OROR : Matcher.prec.ANDAND;
566 + var s = ms.map(function(m, i) {
567 + if (required !== false && !required[i]) {
568 + return m.toString(Matcher.prec.MOD) + "?";
569 + }
570 + return m.toString(p);
571 + }).join(required === false ? " || " : " && ");
572 + if (prec > p) {
573 + s = "[ " + s + " ]";
574 + }
575 + return s;
576 + });
577 + result.options = ms;
578 + return result;
579 + };
580 +
581 + /**
582 + * Create a matcher for two or more options, where all options are
583 + * mandatory but they may appear in any order.
584 + */
585 + Matcher.andand = function() {
586 + var args = Array.prototype.slice.call(arguments);
587 + args.unshift(true);
588 + return Matcher.many.apply(Matcher, args);
589 + };
590 +
591 + /**
592 + * Create a matcher for two or more options, where options are
593 + * optional and may appear in any order, but at least one must be
594 + * present.
595 + */
596 + Matcher.oror = function() {
597 + var args = Array.prototype.slice.call(arguments);
598 + args.unshift(false);
599 + return Matcher.many.apply(Matcher, args);
600 + };
601 +
602 + /** Instance methods on Matchers. */
603 + Matcher.prototype = {
604 + constructor: Matcher,
605 + // These are expected to be overridden in every instance.
606 + match: function() { throw new Error("unimplemented"); },
607 + toString: function() { throw new Error("unimplemented"); },
608 + // This returns a standalone function to do the matching.
609 + func: function() { return this.match.bind(this); },
610 + // Basic combinators
611 + then: function(m) { return Matcher.seq(this, m); },
612 + or: function(m) { return Matcher.alt(this, m); },
613 + andand: function(m) { return Matcher.many(true, this, m); },
614 + oror: function(m) { return Matcher.many(false, this, m); },
615 + // Component value multipliers
616 + star: function() { return this.braces(0, Infinity, "*"); },
617 + plus: function() { return this.braces(1, Infinity, "+"); },
618 + question: function() { return this.braces(0, 1, "?"); },
619 + hash: function() {
620 + return this.braces(1, Infinity, "#", Matcher.cast(","));
621 + },
622 + braces: function(min, max, marker, optSep) {
623 + var m1 = this, m2 = optSep ? optSep.then(this) : this;
624 + if (!marker) {
625 + marker = "{" + min + "," + max + "}";
626 + }
627 + return new Matcher(function(expression) {
628 + var result = true, i;
629 + for (i = 0; i < max; i++) {
630 + if (i > 0 && optSep) {
631 + result = m2.match(expression);
632 + } else {
633 + result = m1.match(expression);
634 + }
635 + if (!result) {
636 + break;
637 + }
638 + }
639 + return i >= min;
640 + }, function() {
641 + return m1.toString(Matcher.prec.MOD) + marker;
642 + });
643 + }
644 + };
645 +
646 + },{"../util/StringReader":24,"../util/SyntaxError":25,"./ValidationTypes":21}],4:[function(require,module,exports){
647 + "use strict";
648 +
649 + module.exports = MediaFeature;
650 +
651 + var SyntaxUnit = require("../util/SyntaxUnit");
652 +
653 + var Parser = require("./Parser");
654 +
655 + /**
656 + * Represents a media feature, such as max-width:500.
657 + * @namespace parserlib.css
658 + * @class MediaFeature
659 + * @extends parserlib.util.SyntaxUnit
660 + * @constructor
661 + * @param {SyntaxUnit} name The name of the feature.
662 + * @param {SyntaxUnit} value The value of the feature or null if none.
663 + */
664 + function MediaFeature(name, value) {
665 +
666 + SyntaxUnit.call(this, "(" + name + (value !== null ? ":" + value : "") + ")", name.startLine, name.startCol, Parser.MEDIA_FEATURE_TYPE);
667 +
668 + /**
669 + * The name of the media feature
670 + * @type String
671 + * @property name
672 + */
673 + this.name = name;
674 +
675 + /**
676 + * The value for the feature or null if there is none.
677 + * @type SyntaxUnit
678 + * @property value
679 + */
680 + this.value = value;
681 + }
682 +
683 + MediaFeature.prototype = new SyntaxUnit();
684 + MediaFeature.prototype.constructor = MediaFeature;
685 +
686 +
687 + },{"../util/SyntaxUnit":26,"./Parser":6}],5:[function(require,module,exports){
688 + "use strict";
689 +
690 + module.exports = MediaQuery;
691 +
692 + var SyntaxUnit = require("../util/SyntaxUnit");
693 +
694 + var Parser = require("./Parser");
695 +
696 + /**
697 + * Represents an individual media query.
698 + * @namespace parserlib.css
699 + * @class MediaQuery
700 + * @extends parserlib.util.SyntaxUnit
701 + * @constructor
702 + * @param {String} modifier The modifier "not" or "only" (or null).
703 + * @param {String} mediaType The type of media (i.e., "print").
704 + * @param {Array} parts Array of selectors parts making up this selector.
705 + * @param {int} line The line of text on which the unit resides.
706 + * @param {int} col The column of text on which the unit resides.
707 + */
708 + function MediaQuery(modifier, mediaType, features, line, col) {
709 +
710 + SyntaxUnit.call(this, (modifier ? modifier + " ": "") + (mediaType ? mediaType : "") + (mediaType && features.length > 0 ? " and " : "") + features.join(" and "), line, col, Parser.MEDIA_QUERY_TYPE);
711 +
712 + /**
713 + * The media modifier ("not" or "only")
714 + * @type String
715 + * @property modifier
716 + */
717 + this.modifier = modifier;
718 +
719 + /**
720 + * The mediaType (i.e., "print")
721 + * @type String
722 + * @property mediaType
723 + */
724 + this.mediaType = mediaType;
725 +
726 + /**
727 + * The parts that make up the selector.
728 + * @type Array
729 + * @property features
730 + */
731 + this.features = features;
732 +
733 + }
734 +
735 + MediaQuery.prototype = new SyntaxUnit();
736 + MediaQuery.prototype.constructor = MediaQuery;
737 +
738 +
739 + },{"../util/SyntaxUnit":26,"./Parser":6}],6:[function(require,module,exports){
740 + "use strict";
741 +
742 + module.exports = Parser;
743 +
744 + var EventTarget = require("../util/EventTarget");
745 + var SyntaxError = require("../util/SyntaxError");
746 + var SyntaxUnit = require("../util/SyntaxUnit");
747 +
748 + var Combinator = require("./Combinator");
749 + var MediaFeature = require("./MediaFeature");
750 + var MediaQuery = require("./MediaQuery");
751 + var PropertyName = require("./PropertyName");
752 + var PropertyValue = require("./PropertyValue");
753 + var PropertyValuePart = require("./PropertyValuePart");
754 + var Selector = require("./Selector");
755 + var SelectorPart = require("./SelectorPart");
756 + var SelectorSubPart = require("./SelectorSubPart");
757 + var TokenStream = require("./TokenStream");
758 + var Tokens = require("./Tokens");
759 + var Validation = require("./Validation");
760 +
761 + /**
762 + * A CSS3 parser.
763 + * @namespace parserlib.css
764 + * @class Parser
765 + * @constructor
766 + * @param {Object} options (Optional) Various options for the parser:
767 + * starHack (true|false) to allow IE6 star hack as valid,
768 + * underscoreHack (true|false) to interpret leading underscores
769 + * as IE6-7 targeting for known properties, ieFilters (true|false)
770 + * to indicate that IE < 8 filters should be accepted and not throw
771 + * syntax errors.
772 + */
773 + function Parser(options) {
774 +
775 + //inherit event functionality
776 + EventTarget.call(this);
777 +
778 +
779 + this.options = options || {};
780 +
781 + this._tokenStream = null;
782 + }
783 +
784 + //Static constants
785 + Parser.DEFAULT_TYPE = 0;
786 + Parser.COMBINATOR_TYPE = 1;
787 + Parser.MEDIA_FEATURE_TYPE = 2;
788 + Parser.MEDIA_QUERY_TYPE = 3;
789 + Parser.PROPERTY_NAME_TYPE = 4;
790 + Parser.PROPERTY_VALUE_TYPE = 5;
791 + Parser.PROPERTY_VALUE_PART_TYPE = 6;
792 + Parser.SELECTOR_TYPE = 7;
793 + Parser.SELECTOR_PART_TYPE = 8;
794 + Parser.SELECTOR_SUB_PART_TYPE = 9;
795 +
796 + Parser.prototype = function() {
797 +
798 + var proto = new EventTarget(), //new prototype
799 + prop,
800 + additions = {
801 + __proto__: null,
802 +
803 + //restore constructor
804 + constructor: Parser,
805 +
806 + //instance constants - yuck
807 + DEFAULT_TYPE : 0,
808 + COMBINATOR_TYPE : 1,
809 + MEDIA_FEATURE_TYPE : 2,
810 + MEDIA_QUERY_TYPE : 3,
811 + PROPERTY_NAME_TYPE : 4,
812 + PROPERTY_VALUE_TYPE : 5,
813 + PROPERTY_VALUE_PART_TYPE : 6,
814 + SELECTOR_TYPE : 7,
815 + SELECTOR_PART_TYPE : 8,
816 + SELECTOR_SUB_PART_TYPE : 9,
817 +
818 + //-----------------------------------------------------------------
819 + // Grammar
820 + //-----------------------------------------------------------------
821 +
822 + _stylesheet: function() {
823 +
824 + /*
825 + * stylesheet
826 + * : [ CHARSET_SYM S* STRING S* ';' ]?
827 + * [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
828 + * [ namespace [S|CDO|CDC]* ]*
829 + * [ [ ruleset | media | page | font_face | keyframes_rule | supports_rule ] [S|CDO|CDC]* ]*
830 + * ;
831 + */
832 +
833 + var tokenStream = this._tokenStream,
834 + count,
835 + token,
836 + tt;
837 +
838 + this.fire("startstylesheet");
839 +
840 + //try to read character set
841 + this._charset();
842 +
843 + this._skipCruft();
844 +
845 + //try to read imports - may be more than one
846 + while (tokenStream.peek() === Tokens.IMPORT_SYM) {
847 + this._import();
848 + this._skipCruft();
849 + }
850 +
851 + //try to read namespaces - may be more than one
852 + while (tokenStream.peek() === Tokens.NAMESPACE_SYM) {
853 + this._namespace();
854 + this._skipCruft();
855 + }
856 +
857 + //get the next token
858 + tt = tokenStream.peek();
859 +
860 + //try to read the rest
861 + while (tt > Tokens.EOF) {
862 +
863 + try {
864 +
865 + switch (tt) {
866 + case Tokens.MEDIA_SYM:
867 + this._media();
868 + this._skipCruft();
869 + break;
870 + case Tokens.PAGE_SYM:
871 + this._page();
872 + this._skipCruft();
873 + break;
874 + case Tokens.FONT_FACE_SYM:
875 + this._font_face();
876 + this._skipCruft();
877 + break;
878 + case Tokens.KEYFRAMES_SYM:
879 + this._keyframes();
880 + this._skipCruft();
881 + break;
882 + case Tokens.VIEWPORT_SYM:
883 + this._viewport();
884 + this._skipCruft();
885 + break;
886 + case Tokens.DOCUMENT_SYM:
887 + this._document();
888 + this._skipCruft();
889 + break;
890 + case Tokens.SUPPORTS_SYM:
891 + this._supports();
892 + this._skipCruft();
893 + break;
894 + case Tokens.UNKNOWN_SYM: //unknown @ rule
895 + tokenStream.get();
896 + if (!this.options.strict) {
897 +
898 + //fire error event
899 + this.fire({
900 + type: "error",
901 + error: null,
902 + message: "Unknown @ rule: " + tokenStream.LT(0).value + ".",
903 + line: tokenStream.LT(0).startLine,
904 + col: tokenStream.LT(0).startCol
905 + });
906 +
907 + //skip braces
908 + count=0;
909 + while (tokenStream.advance([Tokens.LBRACE, Tokens.RBRACE]) === Tokens.LBRACE) {
910 + count++; //keep track of nesting depth
911 + }
912 +
913 + while (count) {
914 + tokenStream.advance([Tokens.RBRACE]);
915 + count--;
916 + }
917 +
918 + } else {
919 + //not a syntax error, rethrow it
920 + throw new SyntaxError("Unknown @ rule.", tokenStream.LT(0).startLine, tokenStream.LT(0).startCol);
921 + }
922 + break;
923 + case Tokens.S:
924 + this._readWhitespace();
925 + break;
926 + default:
927 + if (!this._ruleset()) {
928 +
929 + //error handling for known issues
930 + switch (tt) {
931 + case Tokens.CHARSET_SYM:
932 + token = tokenStream.LT(1);
933 + this._charset(false);
934 + throw new SyntaxError("@charset not allowed here.", token.startLine, token.startCol);
935 + case Tokens.IMPORT_SYM:
936 + token = tokenStream.LT(1);
937 + this._import(false);
938 + throw new SyntaxError("@import not allowed here.", token.startLine, token.startCol);
939 + case Tokens.NAMESPACE_SYM:
940 + token = tokenStream.LT(1);
941 + this._namespace(false);
942 + throw new SyntaxError("@namespace not allowed here.", token.startLine, token.startCol);
943 + default:
944 + tokenStream.get(); //get the last token
945 + this._unexpectedToken(tokenStream.token());
946 + }
947 +
948 + }
949 + }
950 + } catch (ex) {
951 + if (ex instanceof SyntaxError && !this.options.strict) {
952 + this.fire({
953 + type: "error",
954 + error: ex,
955 + message: ex.message,
956 + line: ex.line,
957 + col: ex.col
958 + });
959 + } else {
960 + throw ex;
961 + }
962 + }
963 +
964 + tt = tokenStream.peek();
965 + }
966 +
967 + if (tt !== Tokens.EOF) {
968 + this._unexpectedToken(tokenStream.token());
969 + }
970 +
971 + this.fire("endstylesheet");
972 + },
973 +
974 + _charset: function(emit) {
975 + var tokenStream = this._tokenStream,
976 + charset,
977 + token,
978 + line,
979 + col;
980 +
981 + if (tokenStream.match(Tokens.CHARSET_SYM)) {
982 + line = tokenStream.token().startLine;
983 + col = tokenStream.token().startCol;
984 +
985 + this._readWhitespace();
986 + tokenStream.mustMatch(Tokens.STRING);
987 +
988 + token = tokenStream.token();
989 + charset = token.value;
990 +
991 + this._readWhitespace();
992 + tokenStream.mustMatch(Tokens.SEMICOLON);
993 +
994 + if (emit !== false) {
995 + this.fire({
996 + type: "charset",
997 + charset:charset,
998 + line: line,
999 + col: col
1000 + });
1001 + }
1002 + }
1003 + },
1004 +
1005 + _import: function(emit) {
1006 + /*
1007 + * import
1008 + * : IMPORT_SYM S*
1009 + * [STRING|URI] S* media_query_list? ';' S*
1010 + */
1011 +
1012 + var tokenStream = this._tokenStream,
1013 + uri,
1014 + importToken,
1015 + mediaList = [];
1016 +
1017 + //read import symbol
1018 + tokenStream.mustMatch(Tokens.IMPORT_SYM);
1019 + importToken = tokenStream.token();
1020 + this._readWhitespace();
1021 +
1022 + tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
1023 +
1024 + //grab the URI value
1025 + uri = tokenStream.token().value.replace(/^(?:url\()?["']?([^"']+?)["']?\)?$/, "$1");
1026 +
1027 + this._readWhitespace();
1028 +
1029 + mediaList = this._media_query_list();
1030 +
1031 + //must end with a semicolon
1032 + tokenStream.mustMatch(Tokens.SEMICOLON);
1033 + this._readWhitespace();
1034 +
1035 + if (emit !== false) {
1036 + this.fire({
1037 + type: "import",
1038 + uri: uri,
1039 + media: mediaList,
1040 + line: importToken.startLine,
1041 + col: importToken.startCol
1042 + });
1043 + }
1044 +
1045 + },
1046 +
1047 + _namespace: function(emit) {
1048 + /*
1049 + * namespace
1050 + * : NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
1051 + */
1052 +
1053 + var tokenStream = this._tokenStream,
1054 + line,
1055 + col,
1056 + prefix,
1057 + uri;
1058 +
1059 + //read import symbol
1060 + tokenStream.mustMatch(Tokens.NAMESPACE_SYM);
1061 + line = tokenStream.token().startLine;
1062 + col = tokenStream.token().startCol;
1063 + this._readWhitespace();
1064 +
1065 + //it's a namespace prefix - no _namespace_prefix() method because it's just an IDENT
1066 + if (tokenStream.match(Tokens.IDENT)) {
1067 + prefix = tokenStream.token().value;
1068 + this._readWhitespace();
1069 + }
1070 +
1071 + tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
1072 + /*if (!tokenStream.match(Tokens.STRING)){
1073 + tokenStream.mustMatch(Tokens.URI);
1074 + }*/
1075 +
1076 + //grab the URI value
1077 + uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1");
1078 +
1079 + this._readWhitespace();
1080 +
1081 + //must end with a semicolon
1082 + tokenStream.mustMatch(Tokens.SEMICOLON);
1083 + this._readWhitespace();
1084 +
1085 + if (emit !== false) {
1086 + this.fire({
1087 + type: "namespace",
1088 + prefix: prefix,
1089 + uri: uri,
1090 + line: line,
1091 + col: col
1092 + });
1093 + }
1094 +
1095 + },
1096 +
1097 + _supports: function(emit) {
1098 + /*
1099 + * supports_rule
1100 + * : SUPPORTS_SYM S* supports_condition S* group_rule_body
1101 + * ;
1102 + */
1103 + var tokenStream = this._tokenStream,
1104 + line,
1105 + col;
1106 +
1107 + if (tokenStream.match(Tokens.SUPPORTS_SYM)) {
1108 + line = tokenStream.token().startLine;
1109 + col = tokenStream.token().startCol;
1110 +
1111 + this._readWhitespace();
1112 + this._supports_condition();
1113 + this._readWhitespace();
1114 +
1115 + tokenStream.mustMatch(Tokens.LBRACE);
1116 + this._readWhitespace();
1117 +
1118 + if (emit !== false) {
1119 + this.fire({
1120 + type: "startsupports",
1121 + line: line,
1122 + col: col
1123 + });
1124 + }
1125 +
1126 + while (true) {
1127 + if (!this._ruleset()) {
1128 + break;
1129 + }
1130 + }
1131 +
1132 + tokenStream.mustMatch(Tokens.RBRACE);
1133 + this._readWhitespace();
1134 +
1135 + this.fire({
1136 + type: "endsupports",
1137 + line: line,
1138 + col: col
1139 + });
1140 + }
1141 + },
1142 +
1143 + _supports_condition: function() {
1144 + /*
1145 + * supports_condition
1146 + * : supports_negation | supports_conjunction | supports_disjunction |
1147 + * supports_condition_in_parens
1148 + * ;
1149 + */
1150 + var tokenStream = this._tokenStream,
1151 + ident;
1152 +
1153 + if (tokenStream.match(Tokens.IDENT)) {
1154 + ident = tokenStream.token().value.toLowerCase();
1155 +
1156 + if (ident === "not") {
1157 + tokenStream.mustMatch(Tokens.S);
1158 + this._supports_condition_in_parens();
1159 + } else {
1160 + tokenStream.unget();
1161 + }
1162 + } else {
1163 + this._supports_condition_in_parens();
1164 + this._readWhitespace();
1165 +
1166 + while (tokenStream.peek() === Tokens.IDENT) {
1167 + ident = tokenStream.LT(1).value.toLowerCase();
1168 + if (ident === "and" || ident === "or") {
1169 + tokenStream.mustMatch(Tokens.IDENT);
1170 + this._readWhitespace();
1171 + this._supports_condition_in_parens();
1172 + this._readWhitespace();
1173 + }
1174 + }
1175 + }
1176 + },
1177 +
1178 + _supports_condition_in_parens: function() {
1179 + /*
1180 + * supports_condition_in_parens
1181 + * : ( '(' S* supports_condition S* ')' ) | supports_declaration_condition |
1182 + * general_enclosed
1183 + * ;
1184 + */
1185 + var tokenStream = this._tokenStream,
1186 + ident;
1187 +
1188 + if (tokenStream.match(Tokens.LPAREN)) {
1189 + this._readWhitespace();
1190 + if (tokenStream.match(Tokens.IDENT)) {
1191 + // look ahead for not keyword, if not given, continue with declaration condition.
1192 + ident = tokenStream.token().value.toLowerCase();
1193 + if (ident === "not") {
1194 + this._readWhitespace();
1195 + this._supports_condition();
1196 + this._readWhitespace();
1197 + tokenStream.mustMatch(Tokens.RPAREN);
1198 + } else {
1199 + tokenStream.unget();
1200 + this._supports_declaration_condition(false);
1201 + }
1202 + } else {
1203 + this._supports_condition();
1204 + this._readWhitespace();
1205 + tokenStream.mustMatch(Tokens.RPAREN);
1206 + }
1207 + } else {
1208 + this._supports_declaration_condition();
1209 + }
1210 + },
1211 +
1212 + _supports_declaration_condition: function(requireStartParen) {
1213 + /*
1214 + * supports_declaration_condition
1215 + * : '(' S* declaration ')'
1216 + * ;
1217 + */
1218 + var tokenStream = this._tokenStream;
1219 +
1220 + if (requireStartParen !== false) {
1221 + tokenStream.mustMatch(Tokens.LPAREN);
1222 + }
1223 + this._readWhitespace();
1224 + this._declaration();
1225 + tokenStream.mustMatch(Tokens.RPAREN);
1226 + },
1227 +
1228 + _media: function() {
1229 + /*
1230 + * media
1231 + * : MEDIA_SYM S* media_query_list S* '{' S* ruleset* '}' S*
1232 + * ;
1233 + */
1234 + var tokenStream = this._tokenStream,
1235 + line,
1236 + col,
1237 + mediaList;// = [];
1238 +
1239 + //look for @media
1240 + tokenStream.mustMatch(Tokens.MEDIA_SYM);
1241 + line = tokenStream.token().startLine;
1242 + col = tokenStream.token().startCol;
1243 +
1244 + this._readWhitespace();
1245 +
1246 + mediaList = this._media_query_list();
1247 +
1248 + tokenStream.mustMatch(Tokens.LBRACE);
1249 + this._readWhitespace();
1250 +
1251 + this.fire({
1252 + type: "startmedia",
1253 + media: mediaList,
1254 + line: line,
1255 + col: col
1256 + });
1257 +
1258 + while (true) {
1259 + if (tokenStream.peek() === Tokens.PAGE_SYM) {
1260 + this._page();
1261 + } else if (tokenStream.peek() === Tokens.FONT_FACE_SYM) {
1262 + this._font_face();
1263 + } else if (tokenStream.peek() === Tokens.VIEWPORT_SYM) {
1264 + this._viewport();
1265 + } else if (tokenStream.peek() === Tokens.DOCUMENT_SYM) {
1266 + this._document();
1267 + } else if (tokenStream.peek() === Tokens.SUPPORTS_SYM) {
1268 + this._supports();
1269 + } else if (tokenStream.peek() === Tokens.MEDIA_SYM) {
1270 + this._media();
1271 + } else if (!this._ruleset()) {
1272 + break;
1273 + }
1274 + }
1275 +
1276 + tokenStream.mustMatch(Tokens.RBRACE);
1277 + this._readWhitespace();
1278 +
1279 + this.fire({
1280 + type: "endmedia",
1281 + media: mediaList,
1282 + line: line,
1283 + col: col
1284 + });
1285 + },
1286 +
1287 +
1288 + //CSS3 Media Queries
1289 + _media_query_list: function() {
1290 + /*
1291 + * media_query_list
1292 + * : S* [media_query [ ',' S* media_query ]* ]?
1293 + * ;
1294 + */
1295 + var tokenStream = this._tokenStream,
1296 + mediaList = [];
1297 +
1298 +
1299 + this._readWhitespace();
1300 +
1301 + if (tokenStream.peek() === Tokens.IDENT || tokenStream.peek() === Tokens.LPAREN) {
1302 + mediaList.push(this._media_query());
1303 + }
1304 +
1305 + while (tokenStream.match(Tokens.COMMA)) {
1306 + this._readWhitespace();
1307 + mediaList.push(this._media_query());
1308 + }
1309 +
1310 + return mediaList;
1311 + },
1312 +
1313 + /*
1314 + * Note: "expression" in the grammar maps to the _media_expression
1315 + * method.
1316 +
1317 + */
1318 + _media_query: function() {
1319 + /*
1320 + * media_query
1321 + * : [ONLY | NOT]? S* media_type S* [ AND S* expression ]*
1322 + * | expression [ AND S* expression ]*
1323 + * ;
1324 + */
1325 + var tokenStream = this._tokenStream,
1326 + type = null,
1327 + ident = null,
1328 + token = null,
1329 + expressions = [];
1330 +
1331 + if (tokenStream.match(Tokens.IDENT)) {
1332 + ident = tokenStream.token().value.toLowerCase();
1333 +
1334 + //since there's no custom tokens for these, need to manually check
1335 + if (ident !== "only" && ident !== "not") {
1336 + tokenStream.unget();
1337 + ident = null;
1338 + } else {
1339 + token = tokenStream.token();
1340 + }
1341 + }
1342 +
1343 + this._readWhitespace();
1344 +
1345 + if (tokenStream.peek() === Tokens.IDENT) {
1346 + type = this._media_type();
1347 + if (token === null) {
1348 + token = tokenStream.token();
1349 + }
1350 + } else if (tokenStream.peek() === Tokens.LPAREN) {
1351 + if (token === null) {
1352 + token = tokenStream.LT(1);
1353 + }
1354 + expressions.push(this._media_expression());
1355 + }
1356 +
1357 + if (type === null && expressions.length === 0) {
1358 + return null;
1359 + } else {
1360 + this._readWhitespace();
1361 + while (tokenStream.match(Tokens.IDENT)) {
1362 + if (tokenStream.token().value.toLowerCase() !== "and") {
1363 + this._unexpectedToken(tokenStream.token());
1364 + }
1365 +
1366 + this._readWhitespace();
1367 + expressions.push(this._media_expression());
1368 + }
1369 + }
1370 +
1371 + return new MediaQuery(ident, type, expressions, token.startLine, token.startCol);
1372 + },
1373 +
1374 + //CSS3 Media Queries
1375 + _media_type: function() {
1376 + /*
1377 + * media_type
1378 + * : IDENT
1379 + * ;
1380 + */
1381 + return this._media_feature();
1382 + },
1383 +
1384 + /**
1385 + * Note: in CSS3 Media Queries, this is called "expression".
1386 + * Renamed here to avoid conflict with CSS3 Selectors
1387 + * definition of "expression". Also note that "expr" in the
1388 + * grammar now maps to "expression" from CSS3 selectors.
1389 + * @method _media_expression
1390 + * @private
1391 + */
1392 + _media_expression: function() {
1393 + /*
1394 + * expression
1395 + * : '(' S* media_feature S* [ ':' S* expr ]? ')' S*
1396 + * ;
1397 + */
1398 + var tokenStream = this._tokenStream,
1399 + feature = null,
1400 + token,
1401 + expression = null;
1402 +
1403 + tokenStream.mustMatch(Tokens.LPAREN);
1404 +
1405 + feature = this._media_feature();
1406 + this._readWhitespace();
1407 +
1408 + if (tokenStream.match(Tokens.COLON)) {
1409 + this._readWhitespace();
1410 + token = tokenStream.LT(1);
1411 + expression = this._expression();
1412 + }
1413 +
1414 + tokenStream.mustMatch(Tokens.RPAREN);
1415 + this._readWhitespace();
1416 +
1417 + return new MediaFeature(feature, expression ? new SyntaxUnit(expression, token.startLine, token.startCol) : null);
1418 + },
1419 +
1420 + //CSS3 Media Queries
1421 + _media_feature: function() {
1422 + /*
1423 + * media_feature
1424 + * : IDENT
1425 + * ;
1426 + */
1427 + var tokenStream = this._tokenStream;
1428 +
1429 + this._readWhitespace();
1430 +
1431 + tokenStream.mustMatch(Tokens.IDENT);
1432 +
1433 + return SyntaxUnit.fromToken(tokenStream.token());
1434 + },
1435 +
1436 + //CSS3 Paged Media
1437 + _page: function() {
1438 + /*
1439 + * page:
1440 + * PAGE_SYM S* IDENT? pseudo_page? S*
1441 + * '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
1442 + * ;
1443 + */
1444 + var tokenStream = this._tokenStream,
1445 + line,
1446 + col,
1447 + identifier = null,
1448 + pseudoPage = null;
1449 +
1450 + //look for @page
1451 + tokenStream.mustMatch(Tokens.PAGE_SYM);
1452 + line = tokenStream.token().startLine;
1453 + col = tokenStream.token().startCol;
1454 +
1455 + this._readWhitespace();
1456 +
1457 + if (tokenStream.match(Tokens.IDENT)) {
1458 + identifier = tokenStream.token().value;
1459 +
1460 + //The value 'auto' may not be used as a page name and MUST be treated as a syntax error.
1461 + if (identifier.toLowerCase() === "auto") {
1462 + this._unexpectedToken(tokenStream.token());
1463 + }
1464 + }
1465 +
1466 + //see if there's a colon upcoming
1467 + if (tokenStream.peek() === Tokens.COLON) {
1468 + pseudoPage = this._pseudo_page();
1469 + }
1470 +
1471 + this._readWhitespace();
1472 +
1473 + this.fire({
1474 + type: "startpage",
1475 + id: identifier,
1476 + pseudo: pseudoPage,
1477 + line: line,
1478 + col: col
1479 + });
1480 +
1481 + this._readDeclarations(true, true);
1482 +
1483 + this.fire({
1484 + type: "endpage",
1485 + id: identifier,
1486 + pseudo: pseudoPage,
1487 + line: line,
1488 + col: col
1489 + });
1490 +
1491 + },
1492 +
1493 + //CSS3 Paged Media
1494 + _margin: function() {
1495 + /*
1496 + * margin :
1497 + * margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S*
1498 + * ;
1499 + */
1500 + var tokenStream = this._tokenStream,
1501 + line,
1502 + col,
1503 + marginSym = this._margin_sym();
1504 +
1505 + if (marginSym) {
1506 + line = tokenStream.token().startLine;
1507 + col = tokenStream.token().startCol;
1508 +
1509 + this.fire({
1510 + type: "startpagemargin",
1511 + margin: marginSym,
1512 + line: line,
1513 + col: col
1514 + });
1515 +
1516 + this._readDeclarations(true);
1517 +
1518 + this.fire({
1519 + type: "endpagemargin",
1520 + margin: marginSym,
1521 + line: line,
1522 + col: col
1523 + });
1524 + return true;
1525 + } else {
1526 + return false;
1527 + }
1528 + },
1529 +
1530 + //CSS3 Paged Media
1531 + _margin_sym: function() {
1532 +
1533 + /*
1534 + * margin_sym :
1535 + * TOPLEFTCORNER_SYM |
1536 + * TOPLEFT_SYM |
1537 + * TOPCENTER_SYM |
1538 + * TOPRIGHT_SYM |
1539 + * TOPRIGHTCORNER_SYM |
1540 + * BOTTOMLEFTCORNER_SYM |
1541 + * BOTTOMLEFT_SYM |
1542 + * BOTTOMCENTER_SYM |
1543 + * BOTTOMRIGHT_SYM |
1544 + * BOTTOMRIGHTCORNER_SYM |
1545 + * LEFTTOP_SYM |
1546 + * LEFTMIDDLE_SYM |
1547 + * LEFTBOTTOM_SYM |
1548 + * RIGHTTOP_SYM |
1549 + * RIGHTMIDDLE_SYM |
1550 + * RIGHTBOTTOM_SYM
1551 + * ;
1552 + */
1553 +
1554 + var tokenStream = this._tokenStream;
1555 +
1556 + if (tokenStream.match([Tokens.TOPLEFTCORNER_SYM, Tokens.TOPLEFT_SYM,
1557 + Tokens.TOPCENTER_SYM, Tokens.TOPRIGHT_SYM, Tokens.TOPRIGHTCORNER_SYM,
1558 + Tokens.BOTTOMLEFTCORNER_SYM, Tokens.BOTTOMLEFT_SYM,
1559 + Tokens.BOTTOMCENTER_SYM, Tokens.BOTTOMRIGHT_SYM,
1560 + Tokens.BOTTOMRIGHTCORNER_SYM, Tokens.LEFTTOP_SYM,
1561 + Tokens.LEFTMIDDLE_SYM, Tokens.LEFTBOTTOM_SYM, Tokens.RIGHTTOP_SYM,
1562 + Tokens.RIGHTMIDDLE_SYM, Tokens.RIGHTBOTTOM_SYM])) {
1563 + return SyntaxUnit.fromToken(tokenStream.token());
1564 + } else {
1565 + return null;
1566 + }
1567 +
1568 + },
1569 +
1570 + _pseudo_page: function() {
1571 + /*
1572 + * pseudo_page
1573 + * : ':' IDENT
1574 + * ;
1575 + */
1576 +
1577 + var tokenStream = this._tokenStream;
1578 +
1579 + tokenStream.mustMatch(Tokens.COLON);
1580 + tokenStream.mustMatch(Tokens.IDENT);
1581 +
1582 + //TODO: CSS3 Paged Media says only "left", "center", and "right" are allowed
1583 +
1584 + return tokenStream.token().value;
1585 + },
1586 +
1587 + _font_face: function() {
1588 + /*
1589 + * font_face
1590 + * : FONT_FACE_SYM S*
1591 + * '{' S* declaration [ ';' S* declaration ]* '}' S*
1592 + * ;
1593 + */
1594 + var tokenStream = this._tokenStream,
1595 + line,
1596 + col;
1597 +
1598 + //look for @page
1599 + tokenStream.mustMatch(Tokens.FONT_FACE_SYM);
1600 + line = tokenStream.token().startLine;
1601 + col = tokenStream.token().startCol;
1602 +
1603 + this._readWhitespace();
1604 +
1605 + this.fire({
1606 + type: "startfontface",
1607 + line: line,
1608 + col: col
1609 + });
1610 +
1611 + this._readDeclarations(true);
1612 +
1613 + this.fire({
1614 + type: "endfontface",
1615 + line: line,
1616 + col: col
1617 + });
1618 + },
1619 +
1620 + _viewport: function() {
1621 + /*
1622 + * viewport
1623 + * : VIEWPORT_SYM S*
1624 + * '{' S* declaration? [ ';' S* declaration? ]* '}' S*
1625 + * ;
1626 + */
1627 + var tokenStream = this._tokenStream,
1628 + line,
1629 + col;
1630 +
1631 + tokenStream.mustMatch(Tokens.VIEWPORT_SYM);
1632 + line = tokenStream.token().startLine;
1633 + col = tokenStream.token().startCol;
1634 +
1635 + this._readWhitespace();
1636 +
1637 + this.fire({
1638 + type: "startviewport",
1639 + line: line,
1640 + col: col
1641 + });
1642 +
1643 + this._readDeclarations(true);
1644 +
1645 + this.fire({
1646 + type: "endviewport",
1647 + line: line,
1648 + col: col
1649 + });
1650 +
1651 + },
1652 +
1653 + _document: function() {
1654 + /*
1655 + * document
1656 + * : DOCUMENT_SYM S*
1657 + * _document_function [ ',' S* _document_function ]* S*
1658 + * '{' S* ruleset* '}'
1659 + * ;
1660 + */
1661 +
1662 + var tokenStream = this._tokenStream,
1663 + token,
1664 + functions = [],
1665 + prefix = "";
1666 +
1667 + tokenStream.mustMatch(Tokens.DOCUMENT_SYM);
1668 + token = tokenStream.token();
1669 + if (/^@\-([^\-]+)\-/.test(token.value)) {
1670 + prefix = RegExp.$1;
1671 + }
1672 +
1673 + this._readWhitespace();
1674 + functions.push(this._document_function());
1675 +
1676 + while (tokenStream.match(Tokens.COMMA)) {
1677 + this._readWhitespace();
1678 + functions.push(this._document_function());
1679 + }
1680 +
1681 + tokenStream.mustMatch(Tokens.LBRACE);
1682 + this._readWhitespace();
1683 +
1684 + this.fire({
1685 + type: "startdocument",
1686 + functions: functions,
1687 + prefix: prefix,
1688 + line: token.startLine,
1689 + col: token.startCol
1690 + });
1691 +
1692 + var ok = true;
1693 + while (ok) {
1694 + switch (tokenStream.peek()) {
1695 + case Tokens.PAGE_SYM:
1696 + this._page();
1697 + break;
1698 + case Tokens.FONT_FACE_SYM:
1699 + this._font_face();
1700 + break;
1701 + case Tokens.VIEWPORT_SYM:
1702 + this._viewport();
1703 + break;
1704 + case Tokens.MEDIA_SYM:
1705 + this._media();
1706 + break;
1707 + case Tokens.KEYFRAMES_SYM:
1708 + this._keyframes();
1709 + break;
1710 + case Tokens.DOCUMENT_SYM:
1711 + this._document();
1712 + break;
1713 + default:
1714 + ok = Boolean(this._ruleset());
1715 + }
1716 + }
1717 +
1718 + tokenStream.mustMatch(Tokens.RBRACE);
1719 + token = tokenStream.token();
1720 + this._readWhitespace();
1721 +
1722 + this.fire({
1723 + type: "enddocument",
1724 + functions: functions,
1725 + prefix: prefix,
1726 + line: token.startLine,
1727 + col: token.startCol
1728 + });
1729 + },
1730 +
1731 + _document_function: function() {
1732 + /*
1733 + * document_function
1734 + * : function | URI S*
1735 + * ;
1736 + */
1737 +
1738 + var tokenStream = this._tokenStream,
1739 + value;
1740 +
1741 + if (tokenStream.match(Tokens.URI)) {
1742 + value = tokenStream.token().value;
1743 + this._readWhitespace();
1744 + } else {
1745 + value = this._function();
1746 + }
1747 +
1748 + return value;
1749 + },
1750 +
1751 + _operator: function(inFunction) {
1752 +
1753 + /*
1754 + * operator (outside function)
1755 + * : '/' S* | ',' S* | /( empty )/
1756 + * operator (inside function)
1757 + * : '/' S* | '+' S* | '*' S* | '-' S* /( empty )/
1758 + * ;
1759 + */
1760 +
1761 + var tokenStream = this._tokenStream,
1762 + token = null;
1763 +
1764 + if (tokenStream.match([Tokens.SLASH, Tokens.COMMA]) ||
1765 + (inFunction && tokenStream.match([Tokens.PLUS, Tokens.STAR, Tokens.MINUS]))) {
1766 + token = tokenStream.token();
1767 + this._readWhitespace();
1768 + }
1769 + return token ? PropertyValuePart.fromToken(token) : null;
1770 +
1771 + },
1772 +
1773 + _combinator: function() {
1774 +
1775 + /*
1776 + * combinator
1777 + * : PLUS S* | GREATER S* | TILDE S* | S+
1778 + * ;
1779 + */
1780 +
1781 + var tokenStream = this._tokenStream,
1782 + value = null,
1783 + token;
1784 +
1785 + if (tokenStream.match([Tokens.PLUS, Tokens.GREATER, Tokens.TILDE])) {
1786 + token = tokenStream.token();
1787 + value = new Combinator(token.value, token.startLine, token.startCol);
1788 + this._readWhitespace();
1789 + }
1790 +
1791 + return value;
1792 + },
1793 +
1794 + _unary_operator: function() {
1795 +
1796 + /*
1797 + * unary_operator
1798 + * : '-' | '+'
1799 + * ;
1800 + */
1801 +
1802 + var tokenStream = this._tokenStream;
1803 +
1804 + if (tokenStream.match([Tokens.MINUS, Tokens.PLUS])) {
1805 + return tokenStream.token().value;
1806 + } else {
1807 + return null;
1808 + }
1809 + },
1810 +
1811 + _property: function() {
1812 +
1813 + /*
1814 + * property
1815 + * : IDENT S*
1816 + * ;
1817 + */
1818 +
1819 + var tokenStream = this._tokenStream,
1820 + value = null,
1821 + hack = null,
1822 + tokenValue,
1823 + token,
1824 + line,
1825 + col;
1826 +
1827 + //check for star hack - throws error if not allowed
1828 + if (tokenStream.peek() === Tokens.STAR && this.options.starHack) {
1829 + tokenStream.get();
1830 + token = tokenStream.token();
1831 + hack = token.value;
1832 + line = token.startLine;
1833 + col = token.startCol;
1834 + }
1835 +
1836 + if (tokenStream.match(Tokens.IDENT)) {
1837 + token = tokenStream.token();
1838 + tokenValue = token.value;
1839 +
1840 + //check for underscore hack - no error if not allowed because it's valid CSS syntax
1841 + if (tokenValue.charAt(0) === "_" && this.options.underscoreHack) {
1842 + hack = "_";
1843 + tokenValue = tokenValue.substring(1);
1844 + }
1845 +
1846 + value = new PropertyName(tokenValue, hack, (line||token.startLine), (col||token.startCol));
1847 + this._readWhitespace();
1848 + }
1849 +
1850 + return value;
1851 + },
1852 +
1853 + //Augmented with CSS3 Selectors
1854 + _ruleset: function() {
1855 + /*
1856 + * ruleset
1857 + * : selectors_group
1858 + * '{' S* declaration? [ ';' S* declaration? ]* '}' S*
1859 + * ;
1860 + */
1861 +
1862 + var tokenStream = this._tokenStream,
1863 + tt,
1864 + selectors;
1865 +
1866 +
1867 + /*
1868 + * Error Recovery: If even a single selector fails to parse,
1869 + * then the entire ruleset should be thrown away.
1870 + */
1871 + try {
1872 + selectors = this._selectors_group();
1873 + } catch (ex) {
1874 + if (ex instanceof SyntaxError && !this.options.strict) {
1875 +
1876 + //fire error event
1877 + this.fire({
1878 + type: "error",
1879 + error: ex,
1880 + message: ex.message,
1881 + line: ex.line,
1882 + col: ex.col
1883 + });
1884 +
1885 + //skip over everything until closing brace
1886 + tt = tokenStream.advance([Tokens.RBRACE]);
1887 + if (tt === Tokens.RBRACE) {
1888 + //if there's a right brace, the rule is finished so don't do anything
1889 + } else {
1890 + //otherwise, rethrow the error because it wasn't handled properly
1891 + throw ex;
1892 + }
1893 +
1894 + } else {
1895 + //not a syntax error, rethrow it
1896 + throw ex;
1897 + }
1898 +
1899 + //trigger parser to continue
1900 + return true;
1901 + }
1902 +
1903 + //if it got here, all selectors parsed
1904 + if (selectors) {
1905 +
1906 + this.fire({
1907 + type: "startrule",
1908 + selectors: selectors,
1909 + line: selectors[0].line,
1910 + col: selectors[0].col
1911 + });
1912 +
1913 + this._readDeclarations(true);
1914 +
1915 + this.fire({
1916 + type: "endrule",
1917 + selectors: selectors,
1918 + line: selectors[0].line,
1919 + col: selectors[0].col
1920 + });
1921 +
1922 + }
1923 +
1924 + return selectors;
1925 +
1926 + },
1927 +
1928 + //CSS3 Selectors
1929 + _selectors_group: function() {
1930 +
1931 + /*
1932 + * selectors_group
1933 + * : selector [ COMMA S* selector ]*
1934 + * ;
1935 + */
1936 + var tokenStream = this._tokenStream,
1937 + selectors = [],
1938 + selector;
1939 +
1940 + selector = this._selector();
1941 + if (selector !== null) {
1942 +
1943 + selectors.push(selector);
1944 + while (tokenStream.match(Tokens.COMMA)) {
1945 + this._readWhitespace();
1946 + selector = this._selector();
1947 + if (selector !== null) {
1948 + selectors.push(selector);
1949 + } else {
1950 + this._unexpectedToken(tokenStream.LT(1));
1951 + }
1952 + }
1953 + }
1954 +
1955 + return selectors.length ? selectors : null;
1956 + },
1957 +
1958 + //CSS3 Selectors
1959 + _selector: function() {
1960 + /*
1961 + * selector
1962 + * : simple_selector_sequence [ combinator simple_selector_sequence ]*
1963 + * ;
1964 + */
1965 +
1966 + var tokenStream = this._tokenStream,
1967 + selector = [],
1968 + nextSelector = null,
1969 + combinator = null,
1970 + ws = null;
1971 +
1972 + //if there's no simple selector, then there's no selector
1973 + nextSelector = this._simple_selector_sequence();
1974 + if (nextSelector === null) {
1975 + return null;
1976 + }
1977 +
1978 + selector.push(nextSelector);
1979 +
1980 + do {
1981 +
1982 + //look for a combinator
1983 + combinator = this._combinator();
1984 +
1985 + if (combinator !== null) {
1986 + selector.push(combinator);
1987 + nextSelector = this._simple_selector_sequence();
1988 +
1989 + //there must be a next selector
1990 + if (nextSelector === null) {
1991 + this._unexpectedToken(tokenStream.LT(1));
1992 + } else {
1993 +
1994 + //nextSelector is an instance of SelectorPart
1995 + selector.push(nextSelector);
1996 + }
1997 + } else {
1998 +
1999 + //if there's not whitespace, we're done
2000 + if (this._readWhitespace()) {
2001 +
2002 + //add whitespace separator
2003 + ws = new Combinator(tokenStream.token().value, tokenStream.token().startLine, tokenStream.token().startCol);
2004 +
2005 + //combinator is not required
2006 + combinator = this._combinator();
2007 +
2008 + //selector is required if there's a combinator
2009 + nextSelector = this._simple_selector_sequence();
2010 + if (nextSelector === null) {
2011 + if (combinator !== null) {
2012 + this._unexpectedToken(tokenStream.LT(1));
2013 + }
2014 + } else {
2015 +
2016 + if (combinator !== null) {
2017 + selector.push(combinator);
2018 + } else {
2019 + selector.push(ws);
2020 + }
2021 +
2022 + selector.push(nextSelector);
2023 + }
2024 + } else {
2025 + break;
2026 + }
2027 +
2028 + }
2029 + } while (true);
2030 +
2031 + return new Selector(selector, selector[0].line, selector[0].col);
2032 + },
2033 +
2034 + //CSS3 Selectors
2035 + _simple_selector_sequence: function() {
2036 + /*
2037 + * simple_selector_sequence
2038 + * : [ type_selector | universal ]
2039 + * [ HASH | class | attrib | pseudo | negation ]*
2040 + * | [ HASH | class | attrib | pseudo | negation ]+
2041 + * ;
2042 + */
2043 +
2044 + var tokenStream = this._tokenStream,
2045 +
2046 + //parts of a simple selector
2047 + elementName = null,
2048 + modifiers = [],
2049 +
2050 + //complete selector text
2051 + selectorText= "",
2052 +
2053 + //the different parts after the element name to search for
2054 + components = [
2055 + //HASH
2056 + function() {
2057 + return tokenStream.match(Tokens.HASH) ?
2058 + new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
2059 + null;
2060 + },
2061 + this._class,
2062 + this._attrib,
2063 + this._pseudo,
2064 + this._negation
2065 + ],
2066 + i = 0,
2067 + len = components.length,
2068 + component = null,
2069 + line,
2070 + col;
2071 +
2072 +
2073 + //get starting line and column for the selector
2074 + line = tokenStream.LT(1).startLine;
2075 + col = tokenStream.LT(1).startCol;
2076 +
2077 + elementName = this._type_selector();
2078 + if (!elementName) {
2079 + elementName = this._universal();
2080 + }
2081 +
2082 + if (elementName !== null) {
2083 + selectorText += elementName;
2084 + }
2085 +
2086 + while (true) {
2087 +
2088 + //whitespace means we're done
2089 + if (tokenStream.peek() === Tokens.S) {
2090 + break;
2091 + }
2092 +
2093 + //check for each component
2094 + while (i < len && component === null) {
2095 + component = components[i++].call(this);
2096 + }
2097 +
2098 + if (component === null) {
2099 +
2100 + //we don't have a selector
2101 + if (selectorText === "") {
2102 + return null;
2103 + } else {
2104 + break;
2105 + }
2106 + } else {
2107 + i = 0;
2108 + modifiers.push(component);
2109 + selectorText += component.toString();
2110 + component = null;
2111 + }
2112 + }
2113 +
2114 +
2115 + return selectorText !== "" ?
2116 + new SelectorPart(elementName, modifiers, selectorText, line, col) :
2117 + null;
2118 + },
2119 +
2120 + //CSS3 Selectors
2121 + _type_selector: function() {
2122 + /*
2123 + * type_selector
2124 + * : [ namespace_prefix ]? element_name
2125 + * ;
2126 + */
2127 +
2128 + var tokenStream = this._tokenStream,
2129 + ns = this._namespace_prefix(),
2130 + elementName = this._element_name();
2131 +
2132 + if (!elementName) {
2133 + /*
2134 + * Need to back out the namespace that was read due to both
2135 + * type_selector and universal reading namespace_prefix
2136 + * first. Kind of hacky, but only way I can figure out
2137 + * right now how to not change the grammar.
2138 + */
2139 + if (ns) {
2140 + tokenStream.unget();
2141 + if (ns.length > 1) {
2142 + tokenStream.unget();
2143 + }
2144 + }
2145 +
2146 + return null;
2147 + } else {
2148 + if (ns) {
2149 + elementName.text = ns + elementName.text;
2150 + elementName.col -= ns.length;
2151 + }
2152 + return elementName;
2153 + }
2154 + },
2155 +
2156 + //CSS3 Selectors
2157 + _class: function() {
2158 + /*
2159 + * class
2160 + * : '.' IDENT
2161 + * ;
2162 + */
2163 +
2164 + var tokenStream = this._tokenStream,
2165 + token;
2166 +
2167 + if (tokenStream.match(Tokens.DOT)) {
2168 + tokenStream.mustMatch(Tokens.IDENT);
2169 + token = tokenStream.token();
2170 + return new SelectorSubPart("." + token.value, "class", token.startLine, token.startCol - 1);
2171 + } else {
2172 + return null;
2173 + }
2174 +
2175 + },
2176 +
2177 + //CSS3 Selectors
2178 + _element_name: function() {
2179 + /*
2180 + * element_name
2181 + * : IDENT
2182 + * ;
2183 + */
2184 +
2185 + var tokenStream = this._tokenStream,
2186 + token;
2187 +
2188 + if (tokenStream.match(Tokens.IDENT)) {
2189 + token = tokenStream.token();
2190 + return new SelectorSubPart(token.value, "elementName", token.startLine, token.startCol);
2191 +
2192 + } else {
2193 + return null;
2194 + }
2195 + },
2196 +
2197 + //CSS3 Selectors
2198 + _namespace_prefix: function() {
2199 + /*
2200 + * namespace_prefix
2201 + * : [ IDENT | '*' ]? '|'
2202 + * ;
2203 + */
2204 + var tokenStream = this._tokenStream,
2205 + value = "";
2206 +
2207 + //verify that this is a namespace prefix
2208 + if (tokenStream.LA(1) === Tokens.PIPE || tokenStream.LA(2) === Tokens.PIPE) {
2209 +
2210 + if (tokenStream.match([Tokens.IDENT, Tokens.STAR])) {
2211 + value += tokenStream.token().value;
2212 + }
2213 +
2214 + tokenStream.mustMatch(Tokens.PIPE);
2215 + value += "|";
2216 +
2217 + }
2218 +
2219 + return value.length ? value : null;
2220 + },
2221 +
2222 + //CSS3 Selectors
2223 + _universal: function() {
2224 + /*
2225 + * universal
2226 + * : [ namespace_prefix ]? '*'
2227 + * ;
2228 + */
2229 + var tokenStream = this._tokenStream,
2230 + value = "",
2231 + ns;
2232 +
2233 + ns = this._namespace_prefix();
2234 + if (ns) {
2235 + value += ns;
2236 + }
2237 +
2238 + if (tokenStream.match(Tokens.STAR)) {
2239 + value += "*";
2240 + }
2241 +
2242 + return value.length ? value : null;
2243 +
2244 + },
2245 +
2246 + //CSS3 Selectors
2247 + _attrib: function() {
2248 + /*
2249 + * attrib
2250 + * : '[' S* [ namespace_prefix ]? IDENT S*
2251 + * [ [ PREFIXMATCH |
2252 + * SUFFIXMATCH |
2253 + * SUBSTRINGMATCH |
2254 + * '=' |
2255 + * INCLUDES |
2256 + * DASHMATCH ] S* [ IDENT | STRING ] S*
2257 + * ]? ']'
2258 + * ;
2259 + */
2260 +
2261 + var tokenStream = this._tokenStream,
2262 + value = null,
2263 + ns,
2264 + token;
2265 +
2266 + if (tokenStream.match(Tokens.LBRACKET)) {
2267 + token = tokenStream.token();
2268 + value = token.value;
2269 + value += this._readWhitespace();
2270 +
2271 + ns = this._namespace_prefix();
2272 +
2273 + if (ns) {
2274 + value += ns;
2275 + }
2276 +
2277 + tokenStream.mustMatch(Tokens.IDENT);
2278 + value += tokenStream.token().value;
2279 + value += this._readWhitespace();
2280 +
2281 + if (tokenStream.match([Tokens.PREFIXMATCH, Tokens.SUFFIXMATCH, Tokens.SUBSTRINGMATCH,
2282 + Tokens.EQUALS, Tokens.INCLUDES, Tokens.DASHMATCH])) {
2283 +
2284 + value += tokenStream.token().value;
2285 + value += this._readWhitespace();
2286 +
2287 + tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
2288 + value += tokenStream.token().value;
2289 + value += this._readWhitespace();
2290 + }
2291 +
2292 + tokenStream.mustMatch(Tokens.RBRACKET);
2293 +
2294 + return new SelectorSubPart(value + "]", "attribute", token.startLine, token.startCol);
2295 + } else {
2296 + return null;
2297 + }
2298 + },
2299 +
2300 + //CSS3 Selectors
2301 + _pseudo: function() {
2302 +
2303 + /*
2304 + * pseudo
2305 + * : ':' ':'? [ IDENT | functional_pseudo ]
2306 + * ;
2307 + */
2308 +
2309 + var tokenStream = this._tokenStream,
2310 + pseudo = null,
2311 + colons = ":",
2312 + line,
2313 + col;
2314 +
2315 + if (tokenStream.match(Tokens.COLON)) {
2316 +
2317 + if (tokenStream.match(Tokens.COLON)) {
2318 + colons += ":";
2319 + }
2320 +
2321 + if (tokenStream.match(Tokens.IDENT)) {
2322 + pseudo = tokenStream.token().value;
2323 + line = tokenStream.token().startLine;
2324 + col = tokenStream.token().startCol - colons.length;
2325 + } else if (tokenStream.peek() === Tokens.FUNCTION) {
2326 + line = tokenStream.LT(1).startLine;
2327 + col = tokenStream.LT(1).startCol - colons.length;
2328 + pseudo = this._functional_pseudo();
2329 + }
2330 +
2331 + if (pseudo) {
2332 + pseudo = new SelectorSubPart(colons + pseudo, "pseudo", line, col);
2333 + } else {
2334 + var startLine = tokenStream.LT(1).startLine,
2335 + startCol = tokenStream.LT(0).startCol;
2336 + throw new SyntaxError("Expected a `FUNCTION` or `IDENT` after colon at line " + startLine + ", col " + startCol + ".", startLine, startCol);
2337 + }
2338 + }
2339 +
2340 + return pseudo;
2341 + },
2342 +
2343 + //CSS3 Selectors
2344 + _functional_pseudo: function() {
2345 + /*
2346 + * functional_pseudo
2347 + * : FUNCTION S* expression ')'
2348 + * ;
2349 + */
2350 +
2351 + var tokenStream = this._tokenStream,
2352 + value = null;
2353 +
2354 + if (tokenStream.match(Tokens.FUNCTION)) {
2355 + value = tokenStream.token().value;
2356 + value += this._readWhitespace();
2357 + value += this._expression();
2358 + tokenStream.mustMatch(Tokens.RPAREN);
2359 + value += ")";
2360 + }
2361 +
2362 + return value;
2363 + },
2364 +
2365 + //CSS3 Selectors
2366 + _expression: function() {
2367 + /*
2368 + * expression
2369 + * : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
2370 + * ;
2371 + */
2372 +
2373 + var tokenStream = this._tokenStream,
2374 + value = "";
2375 +
2376 + while (tokenStream.match([Tokens.PLUS, Tokens.MINUS, Tokens.DIMENSION,
2377 + Tokens.NUMBER, Tokens.STRING, Tokens.IDENT, Tokens.LENGTH,
2378 + Tokens.FREQ, Tokens.ANGLE, Tokens.TIME,
2379 + Tokens.RESOLUTION, Tokens.SLASH])) {
2380 +
2381 + value += tokenStream.token().value;
2382 + value += this._readWhitespace();
2383 + }
2384 +
2385 + return value.length ? value : null;
2386 +
2387 + },
2388 +
2389 + //CSS3 Selectors
2390 + _negation: function() {
2391 + /*
2392 + * negation
2393 + * : NOT S* negation_arg S* ')'
2394 + * ;
2395 + */
2396 +
2397 + var tokenStream = this._tokenStream,
2398 + line,
2399 + col,
2400 + value = "",
2401 + arg,
2402 + subpart = null;
2403 +
2404 + if (tokenStream.match(Tokens.NOT)) {
2405 + value = tokenStream.token().value;
2406 + line = tokenStream.token().startLine;
2407 + col = tokenStream.token().startCol;
2408 + value += this._readWhitespace();
2409 + arg = this._negation_arg();
2410 + value += arg;
2411 + value += this._readWhitespace();
2412 + tokenStream.match(Tokens.RPAREN);
2413 + value += tokenStream.token().value;
2414 +
2415 + subpart = new SelectorSubPart(value, "not", line, col);
2416 + subpart.args.push(arg);
2417 + }
2418 +
2419 + return subpart;
2420 + },
2421 +
2422 + //CSS3 Selectors
2423 + _negation_arg: function() {
2424 + /*
2425 + * negation_arg
2426 + * : type_selector | universal | HASH | class | attrib | pseudo
2427 + * ;
2428 + */
2429 +
2430 + var tokenStream = this._tokenStream,
2431 + args = [
2432 + this._type_selector,
2433 + this._universal,
2434 + function() {
2435 + return tokenStream.match(Tokens.HASH) ?
2436 + new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
2437 + null;
2438 + },
2439 + this._class,
2440 + this._attrib,
2441 + this._pseudo
2442 + ],
2443 + arg = null,
2444 + i = 0,
2445 + len = args.length,
2446 + line,
2447 + col,
2448 + part;
2449 +
2450 + line = tokenStream.LT(1).startLine;
2451 + col = tokenStream.LT(1).startCol;
2452 +
2453 + while (i < len && arg === null) {
2454 +
2455 + arg = args[i].call(this);
2456 + i++;
2457 + }
2458 +
2459 + //must be a negation arg
2460 + if (arg === null) {
2461 + this._unexpectedToken(tokenStream.LT(1));
2462 + }
2463 +
2464 + //it's an element name
2465 + if (arg.type === "elementName") {
2466 + part = new SelectorPart(arg, [], arg.toString(), line, col);
2467 + } else {
2468 + part = new SelectorPart(null, [arg], arg.toString(), line, col);
2469 + }
2470 +
2471 + return part;
2472 + },
2473 +
2474 + _declaration: function() {
2475 +
2476 + /*
2477 + * declaration
2478 + * : property ':' S* expr prio?
2479 + * | /( empty )/
2480 + * ;
2481 + */
2482 +
2483 + var tokenStream = this._tokenStream,
2484 + property = null,
2485 + expr = null,
2486 + prio = null,
2487 + invalid = null,
2488 + propertyName= "";
2489 +
2490 + property = this._property();
2491 + if (property !== null) {
2492 +
2493 + tokenStream.mustMatch(Tokens.COLON);
2494 + this._readWhitespace();
2495 +
2496 + expr = this._expr();
2497 +
2498 + //if there's no parts for the value, it's an error
2499 + if (!expr || expr.length === 0) {
2500 + this._unexpectedToken(tokenStream.LT(1));
2501 + }
2502 +
2503 + prio = this._prio();
2504 +
2505 + /*
2506 + * If hacks should be allowed, then only check the root
2507 + * property. If hacks should not be allowed, treat
2508 + * _property or *property as invalid properties.
2509 + */
2510 + propertyName = property.toString();
2511 + if (this.options.starHack && property.hack === "*" ||
2512 + this.options.underscoreHack && property.hack === "_") {
2513 +
2514 + propertyName = property.text;
2515 + }
2516 +
2517 + try {
2518 + this._validateProperty(propertyName, expr);
2519 + } catch (ex) {
2520 + invalid = ex;
2521 + }
2522 +
2523 + this.fire({
2524 + type: "property",
2525 + property: property,
2526 + value: expr,
2527 + important: prio,
2528 + line: property.line,
2529 + col: property.col,
2530 + invalid: invalid
2531 + });
2532 +
2533 + return true;
2534 + } else {
2535 + return false;
2536 + }
2537 + },
2538 +
2539 + _prio: function() {
2540 + /*
2541 + * prio
2542 + * : IMPORTANT_SYM S*
2543 + * ;
2544 + */
2545 +
2546 + var tokenStream = this._tokenStream,
2547 + result = tokenStream.match(Tokens.IMPORTANT_SYM);
2548 +
2549 + this._readWhitespace();
2550 + return result;
2551 + },
2552 +
2553 + _expr: function(inFunction) {
2554 + /*
2555 + * expr
2556 + * : term [ operator term ]*
2557 + * ;
2558 + */
2559 +
2560 + var values = [],
2561 + //valueParts = [],
2562 + value = null,
2563 + operator = null;
2564 +
2565 + value = this._term(inFunction);
2566 + if (value !== null) {
2567 +
2568 + values.push(value);
2569 +
2570 + do {
2571 + operator = this._operator(inFunction);
2572 +
2573 + //if there's an operator, keep building up the value parts
2574 + if (operator) {
2575 + values.push(operator);
2576 + } /*else {
2577 + //if there's not an operator, you have a full value
2578 + values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
2579 + valueParts = [];
2580 + }*/
2581 +
2582 + value = this._term(inFunction);
2583 +
2584 + if (value === null) {
2585 + break;
2586 + } else {
2587 + values.push(value);
2588 + }
2589 + } while (true);
2590 + }
2591 +
2592 + //cleanup
2593 + /*if (valueParts.length) {
2594 + values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
2595 + }*/
2596 +
2597 + return values.length > 0 ? new PropertyValue(values, values[0].line, values[0].col) : null;
2598 + },
2599 +
2600 + _term: function(inFunction) {
2601 +
2602 + /*
2603 + * term
2604 + * : unary_operator?
2605 + * [ NUMBER S* | PERCENTAGE S* | LENGTH S* | ANGLE S* |
2606 + * TIME S* | FREQ S* | function | ie_function ]
2607 + * | STRING S* | IDENT S* | URI S* | UNICODERANGE S* | hexcolor
2608 + * ;
2609 + */
2610 +
2611 + var tokenStream = this._tokenStream,
2612 + unary = null,
2613 + value = null,
2614 + endChar = null,
2615 + part = null,
2616 + token,
2617 + line,
2618 + col;
2619 +
2620 + //returns the operator or null
2621 + unary = this._unary_operator();
2622 + if (unary !== null) {
2623 + line = tokenStream.token().startLine;
2624 + col = tokenStream.token().startCol;
2625 + }
2626 +
2627 + //exception for IE filters
2628 + if (tokenStream.peek() === Tokens.IE_FUNCTION && this.options.ieFilters) {
2629 +
2630 + value = this._ie_function();
2631 + if (unary === null) {
2632 + line = tokenStream.token().startLine;
2633 + col = tokenStream.token().startCol;
2634 + }
2635 +
2636 + //see if it's a simple block
2637 + } else if (inFunction && tokenStream.match([Tokens.LPAREN, Tokens.LBRACE, Tokens.LBRACKET])) {
2638 +
2639 + token = tokenStream.token();
2640 + endChar = token.endChar;
2641 + value = token.value + this._expr(inFunction).text;
2642 + if (unary === null) {
2643 + line = tokenStream.token().startLine;
2644 + col = tokenStream.token().startCol;
2645 + }
2646 + tokenStream.mustMatch(Tokens.type(endChar));
2647 + value += endChar;
2648 + this._readWhitespace();
2649 +
2650 + //see if there's a simple match
2651 + } else if (tokenStream.match([Tokens.NUMBER, Tokens.PERCENTAGE, Tokens.LENGTH,
2652 + Tokens.ANGLE, Tokens.TIME,
2653 + Tokens.FREQ, Tokens.STRING, Tokens.IDENT, Tokens.URI, Tokens.UNICODE_RANGE])) {
2654 +
2655 + value = tokenStream.token().value;
2656 + if (unary === null) {
2657 + line = tokenStream.token().startLine;
2658 + col = tokenStream.token().startCol;
2659 + // Correct potentially-inaccurate IDENT parsing in
2660 + // PropertyValuePart constructor.
2661 + part = PropertyValuePart.fromToken(tokenStream.token());
2662 + }
2663 + this._readWhitespace();
2664 + } else {
2665 +
2666 + //see if it's a color
2667 + token = this._hexcolor();
2668 + if (token === null) {
2669 +
2670 + //if there's no unary, get the start of the next token for line/col info
2671 + if (unary === null) {
2672 + line = tokenStream.LT(1).startLine;
2673 + col = tokenStream.LT(1).startCol;
2674 + }
2675 +
2676 + //has to be a function
2677 + if (value === null) {
2678 +
2679 + /*
2680 + * This checks for alpha(opacity=0) style of IE
2681 + * functions. IE_FUNCTION only presents progid: style.
2682 + */
2683 + if (tokenStream.LA(3) === Tokens.EQUALS && this.options.ieFilters) {
2684 + value = this._ie_function();
2685 + } else {
2686 + value = this._function();
2687 + }
2688 + }
2689 +
2690 + /*if (value === null) {
2691 + return null;
2692 + //throw new Error("Expected identifier at line " + tokenStream.token().startLine + ", character " + tokenStream.token().startCol + ".");
2693 + }*/
2694 +
2695 + } else {
2696 + value = token.value;
2697 + if (unary === null) {
2698 + line = token.startLine;
2699 + col = token.startCol;
2700 + }
2701 + }
2702 +
2703 + }
2704 +
2705 + return part !== null ? part : value !== null ?
2706 + new PropertyValuePart(unary !== null ? unary + value : value, line, col) :
2707 + null;
2708 +
2709 + },
2710 +
2711 + _function: function() {
2712 +
2713 + /*
2714 + * function
2715 + * : FUNCTION S* expr ')' S*
2716 + * ;
2717 + */
2718 +
2719 + var tokenStream = this._tokenStream,
2720 + functionText = null,
2721 + expr = null,
2722 + lt;
2723 +
2724 + if (tokenStream.match(Tokens.FUNCTION)) {
2725 + functionText = tokenStream.token().value;
2726 + this._readWhitespace();
2727 + expr = this._expr(true);
2728 + functionText += expr;
2729 +
2730 + //START: Horrible hack in case it's an IE filter
2731 + if (this.options.ieFilters && tokenStream.peek() === Tokens.EQUALS) {
2732 + do {
2733 +
2734 + if (this._readWhitespace()) {
2735 + functionText += tokenStream.token().value;
2736 + }
2737 +
2738 + //might be second time in the loop
2739 + if (tokenStream.LA(0) === Tokens.COMMA) {
2740 + functionText += tokenStream.token().value;
2741 + }
2742 +
2743 + tokenStream.match(Tokens.IDENT);
2744 + functionText += tokenStream.token().value;
2745 +
2746 + tokenStream.match(Tokens.EQUALS);
2747 + functionText += tokenStream.token().value;
2748 +
2749 + //functionText += this._term();
2750 + lt = tokenStream.peek();
2751 + while (lt !== Tokens.COMMA && lt !== Tokens.S && lt !== Tokens.RPAREN) {
2752 + tokenStream.get();
2753 + functionText += tokenStream.token().value;
2754 + lt = tokenStream.peek();
2755 + }
2756 + } while (tokenStream.match([Tokens.COMMA, Tokens.S]));
2757 + }
2758 +
2759 + //END: Horrible Hack
2760 +
2761 + tokenStream.match(Tokens.RPAREN);
2762 + functionText += ")";
2763 + this._readWhitespace();
2764 + }
2765 +
2766 + return functionText;
2767 + },
2768 +
2769 + _ie_function: function() {
2770 +
2771 + /* (My own extension)
2772 + * ie_function
2773 + * : IE_FUNCTION S* IDENT '=' term [S* ','? IDENT '=' term]+ ')' S*
2774 + * ;
2775 + */
2776 +
2777 + var tokenStream = this._tokenStream,
2778 + functionText = null,
2779 + lt;
2780 +
2781 + //IE function can begin like a regular function, too
2782 + if (tokenStream.match([Tokens.IE_FUNCTION, Tokens.FUNCTION])) {
2783 + functionText = tokenStream.token().value;
2784 +
2785 + do {
2786 +
2787 + if (this._readWhitespace()) {
2788 + functionText += tokenStream.token().value;
2789 + }
2790 +
2791 + //might be second time in the loop
2792 + if (tokenStream.LA(0) === Tokens.COMMA) {
2793 + functionText += tokenStream.token().value;
2794 + }
2795 +
2796 + tokenStream.match(Tokens.IDENT);
2797 + functionText += tokenStream.token().value;
2798 +
2799 + tokenStream.match(Tokens.EQUALS);
2800 + functionText += tokenStream.token().value;
2801 +
2802 + //functionText += this._term();
2803 + lt = tokenStream.peek();
2804 + while (lt !== Tokens.COMMA && lt !== Tokens.S && lt !== Tokens.RPAREN) {
2805 + tokenStream.get();
2806 + functionText += tokenStream.token().value;
2807 + lt = tokenStream.peek();
2808 + }
2809 + } while (tokenStream.match([Tokens.COMMA, Tokens.S]));
2810 +
2811 + tokenStream.match(Tokens.RPAREN);
2812 + functionText += ")";
2813 + this._readWhitespace();
2814 + }
2815 +
2816 + return functionText;
2817 + },
2818 +
2819 + _hexcolor: function() {
2820 + /*
2821 + * There is a constraint on the color that it must
2822 + * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
2823 + * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
2824 + *
2825 + * hexcolor
2826 + * : HASH S*
2827 + * ;
2828 + */
2829 +
2830 + var tokenStream = this._tokenStream,
2831 + token = null,
2832 + color;
2833 +
2834 + if (tokenStream.match(Tokens.HASH)) {
2835 +
2836 + //need to do some validation here
2837 +
2838 + token = tokenStream.token();
2839 + color = token.value;
2840 + if (!/#[a-f0-9]{3,6}/i.test(color)) {
2841 + throw new SyntaxError("Expected a hex color but found '" + color + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
2842 + }
2843 + this._readWhitespace();
2844 + }
2845 +
2846 + return token;
2847 + },
2848 +
2849 + //-----------------------------------------------------------------
2850 + // Animations methods
2851 + //-----------------------------------------------------------------
2852 +
2853 + _keyframes: function() {
2854 +
2855 + /*
2856 + * keyframes:
2857 + * : KEYFRAMES_SYM S* keyframe_name S* '{' S* keyframe_rule* '}' {
2858 + * ;
2859 + */
2860 + var tokenStream = this._tokenStream,
2861 + token,
2862 + tt,
2863 + name,
2864 + prefix = "";
2865 +
2866 + tokenStream.mustMatch(Tokens.KEYFRAMES_SYM);
2867 + token = tokenStream.token();
2868 + if (/^@\-([^\-]+)\-/.test(token.value)) {
2869 + prefix = RegExp.$1;
2870 + }
2871 +
2872 + this._readWhitespace();
2873 + name = this._keyframe_name();
2874 +
2875 + this._readWhitespace();
2876 + tokenStream.mustMatch(Tokens.LBRACE);
2877 +
2878 + this.fire({
2879 + type: "startkeyframes",
2880 + name: name,
2881 + prefix: prefix,
2882 + line: token.startLine,
2883 + col: token.startCol
2884 + });
2885 +
2886 + this._readWhitespace();
2887 + tt = tokenStream.peek();
2888 +
2889 + //check for key
2890 + while (tt === Tokens.IDENT || tt === Tokens.PERCENTAGE) {
2891 + this._keyframe_rule();
2892 + this._readWhitespace();
2893 + tt = tokenStream.peek();
2894 + }
2895 +
2896 + this.fire({
2897 + type: "endkeyframes",
2898 + name: name,
2899 + prefix: prefix,
2900 + line: token.startLine,
2901 + col: token.startCol
2902 + });
2903 +
2904 + this._readWhitespace();
2905 + tokenStream.mustMatch(Tokens.RBRACE);
2906 + this._readWhitespace();
2907 +
2908 + },
2909 +
2910 + _keyframe_name: function() {
2911 +
2912 + /*
2913 + * keyframe_name:
2914 + * : IDENT
2915 + * | STRING
2916 + * ;
2917 + */
2918 + var tokenStream = this._tokenStream;
2919 +
2920 + tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
2921 + return SyntaxUnit.fromToken(tokenStream.token());
2922 + },
2923 +
2924 + _keyframe_rule: function() {
2925 +
2926 + /*
2927 + * keyframe_rule:
2928 + * : key_list S*
2929 + * '{' S* declaration [ ';' S* declaration ]* '}' S*
2930 + * ;
2931 + */
2932 + var keyList = this._key_list();
2933 +
2934 + this.fire({
2935 + type: "startkeyframerule",
2936 + keys: keyList,
2937 + line: keyList[0].line,
2938 + col: keyList[0].col
2939 + });
2940 +
2941 + this._readDeclarations(true);
2942 +
2943 + this.fire({
2944 + type: "endkeyframerule",
2945 + keys: keyList,
2946 + line: keyList[0].line,
2947 + col: keyList[0].col
2948 + });
2949 +
2950 + },
2951 +
2952 + _key_list: function() {
2953 +
2954 + /*
2955 + * key_list:
2956 + * : key [ S* ',' S* key]*
2957 + * ;
2958 + */
2959 + var tokenStream = this._tokenStream,
2960 + keyList = [];
2961 +
2962 + //must be least one key
2963 + keyList.push(this._key());
2964 +
2965 + this._readWhitespace();
2966 +
2967 + while (tokenStream.match(Tokens.COMMA)) {
2968 + this._readWhitespace();
2969 + keyList.push(this._key());
2970 + this._readWhitespace();
2971 + }
2972 +
2973 + return keyList;
2974 + },
2975 +
2976 + _key: function() {
2977 + /*
2978 + * There is a restriction that IDENT can be only "from" or "to".
2979 + *
2980 + * key
2981 + * : PERCENTAGE
2982 + * | IDENT
2983 + * ;
2984 + */
2985 +
2986 + var tokenStream = this._tokenStream,
2987 + token;
2988 +
2989 + if (tokenStream.match(Tokens.PERCENTAGE)) {
2990 + return SyntaxUnit.fromToken(tokenStream.token());
2991 + } else if (tokenStream.match(Tokens.IDENT)) {
2992 + token = tokenStream.token();
2993 +
2994 + if (/from|to/i.test(token.value)) {
2995 + return SyntaxUnit.fromToken(token);
2996 + }
2997 +
2998 + tokenStream.unget();
2999 + }
3000 +
3001 + //if it gets here, there wasn't a valid token, so time to explode
3002 + this._unexpectedToken(tokenStream.LT(1));
3003 + },
3004 +
3005 + //-----------------------------------------------------------------
3006 + // Helper methods
3007 + //-----------------------------------------------------------------
3008 +
3009 + /**
3010 + * Not part of CSS grammar, but useful for skipping over
3011 + * combination of white space and HTML-style comments.
3012 + * @return {void}
3013 + * @method _skipCruft
3014 + * @private
3015 + */
3016 + _skipCruft: function() {
3017 + while (this._tokenStream.match([Tokens.S, Tokens.CDO, Tokens.CDC])) {
3018 + //noop
3019 + }
3020 + },
3021 +
3022 + /**
3023 + * Not part of CSS grammar, but this pattern occurs frequently
3024 + * in the official CSS grammar. Split out here to eliminate
3025 + * duplicate code.
3026 + * @param {Boolean} checkStart Indicates if the rule should check
3027 + * for the left brace at the beginning.
3028 + * @param {Boolean} readMargins Indicates if the rule should check
3029 + * for margin patterns.
3030 + * @return {void}
3031 + * @method _readDeclarations
3032 + * @private
3033 + */
3034 + _readDeclarations: function(checkStart, readMargins) {
3035 + /*
3036 + * Reads the pattern
3037 + * S* '{' S* declaration [ ';' S* declaration ]* '}' S*
3038 + * or
3039 + * S* '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
3040 + * Note that this is how it is described in CSS3 Paged Media, but is actually incorrect.
3041 + * A semicolon is only necessary following a declaration if there's another declaration
3042 + * or margin afterwards.
3043 + */
3044 + var tokenStream = this._tokenStream,
3045 + tt;
3046 +
3047 +
3048 + this._readWhitespace();
3049 +
3050 + if (checkStart) {
3051 + tokenStream.mustMatch(Tokens.LBRACE);
3052 + }
3053 +
3054 + this._readWhitespace();
3055 +
3056 + try {
3057 +
3058 + while (true) {
3059 +
3060 + if (tokenStream.match(Tokens.SEMICOLON) || (readMargins && this._margin())) {
3061 + //noop
3062 + } else if (this._declaration()) {
3063 + if (!tokenStream.match(Tokens.SEMICOLON)) {
3064 + break;
3065 + }
3066 + } else {
3067 + break;
3068 + }
3069 +
3070 + //if ((!this._margin() && !this._declaration()) || !tokenStream.match(Tokens.SEMICOLON)){
3071 + // break;
3072 + //}
3073 + this._readWhitespace();
3074 + }
3075 +
3076 + tokenStream.mustMatch(Tokens.RBRACE);
3077 + this._readWhitespace();
3078 +
3079 + } catch (ex) {
3080 + if (ex instanceof SyntaxError && !this.options.strict) {
3081 +
3082 + //fire error event
3083 + this.fire({
3084 + type: "error",
3085 + error: ex,
3086 + message: ex.message,
3087 + line: ex.line,
3088 + col: ex.col
3089 + });
3090 +
3091 + //see if there's another declaration
3092 + tt = tokenStream.advance([Tokens.SEMICOLON, Tokens.RBRACE]);
3093 + if (tt === Tokens.SEMICOLON) {
3094 + //if there's a semicolon, then there might be another declaration
3095 + this._readDeclarations(false, readMargins);
3096 + } else if (tt !== Tokens.RBRACE) {
3097 + //if there's a right brace, the rule is finished so don't do anything
3098 + //otherwise, rethrow the error because it wasn't handled properly
3099 + throw ex;
3100 + }
3101 +
3102 + } else {
3103 + //not a syntax error, rethrow it
3104 + throw ex;
3105 + }
3106 + }
3107 +
3108 + },
3109 +
3110 + /**
3111 + * In some cases, you can end up with two white space tokens in a
3112 + * row. Instead of making a change in every function that looks for
3113 + * white space, this function is used to match as much white space
3114 + * as necessary.
3115 + * @method _readWhitespace
3116 + * @return {String} The white space if found, empty string if not.
3117 + * @private
3118 + */
3119 + _readWhitespace: function() {
3120 +
3121 + var tokenStream = this._tokenStream,
3122 + ws = "";
3123 +
3124 + while (tokenStream.match(Tokens.S)) {
3125 + ws += tokenStream.token().value;
3126 + }
3127 +
3128 + return ws;
3129 + },
3130 +
3131 +
3132 + /**
3133 + * Throws an error when an unexpected token is found.
3134 + * @param {Object} token The token that was found.
3135 + * @method _unexpectedToken
3136 + * @return {void}
3137 + * @private
3138 + */
3139 + _unexpectedToken: function(token) {
3140 + throw new SyntaxError("Unexpected token '" + token.value + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
3141 + },
3142 +
3143 + /**
3144 + * Helper method used for parsing subparts of a style sheet.
3145 + * @return {void}
3146 + * @method _verifyEnd
3147 + * @private
3148 + */
3149 + _verifyEnd: function() {
3150 + if (this._tokenStream.LA(1) !== Tokens.EOF) {
3151 + this._unexpectedToken(this._tokenStream.LT(1));
3152 + }
3153 + },
3154 +
3155 + //-----------------------------------------------------------------
3156 + // Validation methods
3157 + //-----------------------------------------------------------------
3158 + _validateProperty: function(property, value) {
3159 + Validation.validate(property, value);
3160 + },
3161 +
3162 + //-----------------------------------------------------------------
3163 + // Parsing methods
3164 + //-----------------------------------------------------------------
3165 +
3166 + parse: function(input) {
3167 + this._tokenStream = new TokenStream(input, Tokens);
3168 + this._stylesheet();
3169 + },
3170 +
3171 + parseStyleSheet: function(input) {
3172 + //just passthrough
3173 + return this.parse(input);
3174 + },
3175 +
3176 + parseMediaQuery: function(input) {
3177 + this._tokenStream = new TokenStream(input, Tokens);
3178 + var result = this._media_query();
3179 +
3180 + //if there's anything more, then it's an invalid selector
3181 + this._verifyEnd();
3182 +
3183 + //otherwise return result
3184 + return result;
3185 + },
3186 +
3187 + /**
3188 + * Parses a property value (everything after the semicolon).
3189 + * @return {parserlib.css.PropertyValue} The property value.
3190 + * @throws parserlib.util.SyntaxError If an unexpected token is found.
3191 + * @method parserPropertyValue
3192 + */
3193 + parsePropertyValue: function(input) {
3194 +
3195 + this._tokenStream = new TokenStream(input, Tokens);
3196 + this._readWhitespace();
3197 +
3198 + var result = this._expr();
3199 +
3200 + //okay to have a trailing white space
3201 + this._readWhitespace();
3202 +
3203 + //if there's anything more, then it's an invalid selector
3204 + this._verifyEnd();
3205 +
3206 + //otherwise return result
3207 + return result;
3208 + },
3209 +
3210 + /**
3211 + * Parses a complete CSS rule, including selectors and
3212 + * properties.
3213 + * @param {String} input The text to parser.
3214 + * @return {Boolean} True if the parse completed successfully, false if not.
3215 + * @method parseRule
3216 + */
3217 + parseRule: function(input) {
3218 + this._tokenStream = new TokenStream(input, Tokens);
3219 +
3220 + //skip any leading white space
3221 + this._readWhitespace();
3222 +
3223 + var result = this._ruleset();
3224 +
3225 + //skip any trailing white space
3226 + this._readWhitespace();
3227 +
3228 + //if there's anything more, then it's an invalid selector
3229 + this._verifyEnd();
3230 +
3231 + //otherwise return result
3232 + return result;
3233 + },
3234 +
3235 + /**
3236 + * Parses a single CSS selector (no comma)
3237 + * @param {String} input The text to parse as a CSS selector.
3238 + * @return {Selector} An object representing the selector.
3239 + * @throws parserlib.util.SyntaxError If an unexpected token is found.
3240 + * @method parseSelector
3241 + */
3242 + parseSelector: function(input) {
3243 +
3244 + this._tokenStream = new TokenStream(input, Tokens);
3245 +
3246 + //skip any leading white space
3247 + this._readWhitespace();
3248 +
3249 + var result = this._selector();
3250 +
3251 + //skip any trailing white space
3252 + this._readWhitespace();
3253 +
3254 + //if there's anything more, then it's an invalid selector
3255 + this._verifyEnd();
3256 +
3257 + //otherwise return result
3258 + return result;
3259 + },
3260 +
3261 + /**
3262 + * Parses an HTML style attribute: a set of CSS declarations
3263 + * separated by semicolons.
3264 + * @param {String} input The text to parse as a style attribute
3265 + * @return {void}
3266 + * @method parseStyleAttribute
3267 + */
3268 + parseStyleAttribute: function(input) {
3269 + input += "}"; // for error recovery in _readDeclarations()
3270 + this._tokenStream = new TokenStream(input, Tokens);
3271 + this._readDeclarations();
3272 + }
3273 + };
3274 +
3275 + //copy over onto prototype
3276 + for (prop in additions) {
3277 + if (Object.prototype.hasOwnProperty.call(additions, prop)) {
3278 + proto[prop] = additions[prop];
3279 + }
3280 + }
3281 +
3282 + return proto;
3283 + }();
3284 +
3285 +
3286 + /*
3287 + nth
3288 + : S* [ ['-'|'+']? INTEGER? {N} [ S* ['-'|'+'] S* INTEGER ]? |
3289 + ['-'|'+']? INTEGER | {O}{D}{D} | {E}{V}{E}{N} ] S*
3290 + ;
3291 + */
3292 +
3293 + },{"../util/EventTarget":23,"../util/SyntaxError":25,"../util/SyntaxUnit":26,"./Combinator":2,"./MediaFeature":4,"./MediaQuery":5,"./PropertyName":8,"./PropertyValue":9,"./PropertyValuePart":11,"./Selector":13,"./SelectorPart":14,"./SelectorSubPart":15,"./TokenStream":17,"./Tokens":18,"./Validation":19}],7:[function(require,module,exports){
3294 + "use strict";
3295 +
3296 + /* exported Properties */
3297 +
3298 + var Properties = module.exports = {
3299 + __proto__: null,
3300 +
3301 + //A
3302 + "align-items" : "flex-start | flex-end | center | baseline | stretch",
3303 + "align-content" : "flex-start | flex-end | center | space-between | space-around | stretch",
3304 + "align-self" : "auto | flex-start | flex-end | center | baseline | stretch",
3305 + "all" : "initial | inherit | unset",
3306 + "-webkit-align-items" : "flex-start | flex-end | center | baseline | stretch",
3307 + "-webkit-align-content" : "flex-start | flex-end | center | space-between | space-around | stretch",
3308 + "-webkit-align-self" : "auto | flex-start | flex-end | center | baseline | stretch",
3309 + "alignment-adjust" : "auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | <percentage> | <length>",
3310 + "alignment-baseline" : "auto | baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
3311 + "animation" : 1,
3312 + "animation-delay" : "<time>#",
3313 + "animation-direction" : "<single-animation-direction>#",
3314 + "animation-duration" : "<time>#",
3315 + "animation-fill-mode" : "[ none | forwards | backwards | both ]#",
3316 + "animation-iteration-count" : "[ <number> | infinite ]#",
3317 + "animation-name" : "[ none | <single-animation-name> ]#",
3318 + "animation-play-state" : "[ running | paused ]#",
3319 + "animation-timing-function" : 1,
3320 +
3321 + //vendor prefixed
3322 + "-moz-animation-delay" : "<time>#",
3323 + "-moz-animation-direction" : "[ normal | alternate ]#",
3324 + "-moz-animation-duration" : "<time>#",
3325 + "-moz-animation-iteration-count" : "[ <number> | infinite ]#",
3326 + "-moz-animation-name" : "[ none | <single-animation-name> ]#",
3327 + "-moz-animation-play-state" : "[ running | paused ]#",
3328 +
3329 + "-ms-animation-delay" : "<time>#",
3330 + "-ms-animation-direction" : "[ normal | alternate ]#",
3331 + "-ms-animation-duration" : "<time>#",
3332 + "-ms-animation-iteration-count" : "[ <number> | infinite ]#",
3333 + "-ms-animation-name" : "[ none | <single-animation-name> ]#",
3334 + "-ms-animation-play-state" : "[ running | paused ]#",
3335 +
3336 + "-webkit-animation-delay" : "<time>#",
3337 + "-webkit-animation-direction" : "[ normal | alternate ]#",
3338 + "-webkit-animation-duration" : "<time>#",
3339 + "-webkit-animation-fill-mode" : "[ none | forwards | backwards | both ]#",
3340 + "-webkit-animation-iteration-count" : "[ <number> | infinite ]#",
3341 + "-webkit-animation-name" : "[ none | <single-animation-name> ]#",
3342 + "-webkit-animation-play-state" : "[ running | paused ]#",
3343 +
3344 + "-o-animation-delay" : "<time>#",
3345 + "-o-animation-direction" : "[ normal | alternate ]#",
3346 + "-o-animation-duration" : "<time>#",
3347 + "-o-animation-iteration-count" : "[ <number> | infinite ]#",
3348 + "-o-animation-name" : "[ none | <single-animation-name> ]#",
3349 + "-o-animation-play-state" : "[ running | paused ]#",
3350 +
3351 + "appearance" : "none | auto",
3352 + "-moz-appearance" : "none | button | button-arrow-down | button-arrow-next | button-arrow-previous | button-arrow-up | button-bevel | button-focus | caret | checkbox | checkbox-container | checkbox-label | checkmenuitem | dualbutton | groupbox | listbox | listitem | menuarrow | menubar | menucheckbox | menuimage | menuitem | menuitemtext | menulist | menulist-button | menulist-text | menulist-textfield | menupopup | menuradio | menuseparator | meterbar | meterchunk | progressbar | progressbar-vertical | progresschunk | progresschunk-vertical | radio | radio-container | radio-label | radiomenuitem | range | range-thumb | resizer | resizerpanel | scale-horizontal | scalethumbend | scalethumb-horizontal | scalethumbstart | scalethumbtick | scalethumb-vertical | scale-vertical | scrollbarbutton-down | scrollbarbutton-left | scrollbarbutton-right | scrollbarbutton-up | scrollbarthumb-horizontal | scrollbarthumb-vertical | scrollbartrack-horizontal | scrollbartrack-vertical | searchfield | separator | sheet | spinner | spinner-downbutton | spinner-textfield | spinner-upbutton | splitter | statusbar | statusbarpanel | tab | tabpanel | tabpanels | tab-scroll-arrow-back | tab-scroll-arrow-forward | textfield | textfield-multiline | toolbar | toolbarbutton | toolbarbutton-dropdown | toolbargripper | toolbox | tooltip | treeheader | treeheadercell | treeheadersortarrow | treeitem | treeline | treetwisty | treetwistyopen | treeview | -moz-mac-unified-toolbar | -moz-win-borderless-glass | -moz-win-browsertabbar-toolbox | -moz-win-communicationstext | -moz-win-communications-toolbox | -moz-win-exclude-glass | -moz-win-glass | -moz-win-mediatext | -moz-win-media-toolbox | -moz-window-button-box | -moz-window-button-box-maximized | -moz-window-button-close | -moz-window-button-maximize | -moz-window-button-minimize | -moz-window-button-restore | -moz-window-frame-bottom | -moz-window-frame-left | -moz-window-frame-right | -moz-window-titlebar | -moz-window-titlebar-maximized",
3353 + "-ms-appearance" : "none | icon | window | desktop | workspace | document | tooltip | dialog | button | push-button | hyperlink | radio | radio-button | checkbox | menu-item | tab | menu | menubar | pull-down-menu | pop-up-menu | list-menu | radio-group | checkbox-group | outline-tree | range | field | combo-box | signature | password | normal",
3354 + "-webkit-appearance" : "none | button | button-bevel | caps-lock-indicator | caret | checkbox | default-button | listbox | listitem | media-fullscreen-button | media-mute-button | media-play-button | media-seek-back-button | media-seek-forward-button | media-slider | media-sliderthumb | menulist | menulist-button | menulist-text | menulist-textfield | push-button | radio | searchfield | searchfield-cancel-button | searchfield-decoration | searchfield-results-button | searchfield-results-decoration | slider-horizontal | slider-vertical | sliderthumb-horizontal | sliderthumb-vertical | square-button | textarea | textfield | scrollbarbutton-down | scrollbarbutton-left | scrollbarbutton-right | scrollbarbutton-up | scrollbargripper-horizontal | scrollbargripper-vertical | scrollbarthumb-horizontal | scrollbarthumb-vertical | scrollbartrack-horizontal | scrollbartrack-vertical",
3355 + "-o-appearance" : "none | window | desktop | workspace | document | tooltip | dialog | button | push-button | hyperlink | radio | radio-button | checkbox | menu-item | tab | menu | menubar | pull-down-menu | pop-up-menu | list-menu | radio-group | checkbox-group | outline-tree | range | field | combo-box | signature | password | normal",
3356 +
3357 + "azimuth" : "<azimuth>",
3358 +
3359 + //B
3360 + "backface-visibility" : "visible | hidden",
3361 + "background" : 1,
3362 + "background-attachment" : "<attachment>#",
3363 + "background-clip" : "<box>#",
3364 + "background-color" : "<color>",
3365 + "background-image" : "<bg-image>#",
3366 + "background-origin" : "<box>#",
3367 + "background-position" : "<bg-position>",
3368 + "background-repeat" : "<repeat-style>#",
3369 + "background-size" : "<bg-size>#",
3370 + "baseline-shift" : "baseline | sub | super | <percentage> | <length>",
3371 + "behavior" : 1,
3372 + "binding" : 1,
3373 + "bleed" : "<length>",
3374 + "bookmark-label" : "<content> | <attr> | <string>",
3375 + "bookmark-level" : "none | <integer>",
3376 + "bookmark-state" : "open | closed",
3377 + "bookmark-target" : "none | <uri> | <attr>",
3378 + "border" : "<border-width> || <border-style> || <color>",
3379 + "border-bottom" : "<border-width> || <border-style> || <color>",
3380 + "border-bottom-color" : "<color>",
3381 + "border-bottom-left-radius" : "<x-one-radius>",
3382 + "border-bottom-right-radius" : "<x-one-radius>",
3383 + "border-bottom-style" : "<border-style>",
3384 + "border-bottom-width" : "<border-width>",
3385 + "border-collapse" : "collapse | separate",
3386 + "border-color" : "<color>{1,4}",
3387 + "border-image" : 1,
3388 + "border-image-outset" : "[ <length> | <number> ]{1,4}",
3389 + "border-image-repeat" : "[ stretch | repeat | round ]{1,2}",
3390 + "border-image-slice" : "<border-image-slice>",
3391 + "border-image-source" : "<image> | none",
3392 + "border-image-width" : "[ <length> | <percentage> | <number> | auto ]{1,4}",
3393 + "border-left" : "<border-width> || <border-style> || <color>",
3394 + "border-left-color" : "<color>",
3395 + "border-left-style" : "<border-style>",
3396 + "border-left-width" : "<border-width>",
3397 + "border-radius" : "<border-radius>",
3398 + "border-right" : "<border-width> || <border-style> || <color>",
3399 + "border-right-color" : "<color>",
3400 + "border-right-style" : "<border-style>",
3401 + "border-right-width" : "<border-width>",
3402 + "border-spacing" : "<length>{1,2}",
3403 + "border-style" : "<border-style>{1,4}",
3404 + "border-top" : "<border-width> || <border-style> || <color>",
3405 + "border-top-color" : "<color>",
3406 + "border-top-left-radius" : "<x-one-radius>",
3407 + "border-top-right-radius" : "<x-one-radius>",
3408 + "border-top-style" : "<border-style>",
3409 + "border-top-width" : "<border-width>",
3410 + "border-width" : "<border-width>{1,4}",
3411 + "bottom" : "<margin-width>",
3412 + "-moz-box-align" : "start | end | center | baseline | stretch",
3413 + "-moz-box-decoration-break" : "slice | clone",
3414 + "-moz-box-direction" : "normal | reverse",
3415 + "-moz-box-flex" : "<number>",
3416 + "-moz-box-flex-group" : "<integer>",
3417 + "-moz-box-lines" : "single | multiple",
3418 + "-moz-box-ordinal-group" : "<integer>",
3419 + "-moz-box-orient" : "horizontal | vertical | inline-axis | block-axis",
3420 + "-moz-box-pack" : "start | end | center | justify",
3421 + "-o-box-decoration-break" : "slice | clone",
3422 + "-webkit-box-align" : "start | end | center | baseline | stretch",
3423 + "-webkit-box-decoration-break" : "slice | clone",
3424 + "-webkit-box-direction" : "normal | reverse",
3425 + "-webkit-box-flex" : "<number>",
3426 + "-webkit-box-flex-group" : "<integer>",
3427 + "-webkit-box-lines" : "single | multiple",
3428 + "-webkit-box-ordinal-group" : "<integer>",
3429 + "-webkit-box-orient" : "horizontal | vertical | inline-axis | block-axis",
3430 + "-webkit-box-pack" : "start | end | center | justify",
3431 + "box-decoration-break" : "slice | clone",
3432 + "box-shadow" : "<box-shadow>",
3433 + "box-sizing" : "content-box | border-box",
3434 + "break-after" : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
3435 + "break-before" : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
3436 + "break-inside" : "auto | avoid | avoid-page | avoid-column",
3437 +
3438 + //C
3439 + "caption-side" : "top | bottom",
3440 + "clear" : "none | right | left | both",
3441 + "clip" : "<shape> | auto",
3442 + "-webkit-clip-path" : "<clip-source> | <clip-path> | none",
3443 + "clip-path" : "<clip-source> | <clip-path> | none",
3444 + "clip-rule" : "nonzero | evenodd",
3445 + "color" : "<color>",
3446 + "color-interpolation" : "auto | sRGB | linearRGB",
3447 + "color-interpolation-filters" : "auto | sRGB | linearRGB",
3448 + "color-profile" : 1,
3449 + "color-rendering" : "auto | optimizeSpeed | optimizeQuality",
3450 + "column-count" : "<integer> | auto", //https://www.w3.org/TR/css3-multicol/
3451 + "column-fill" : "auto | balance",
3452 + "column-gap" : "<length> | normal",
3453 + "column-rule" : "<border-width> || <border-style> || <color>",
3454 + "column-rule-color" : "<color>",
3455 + "column-rule-style" : "<border-style>",
3456 + "column-rule-width" : "<border-width>",
3457 + "column-span" : "none | all",
3458 + "column-width" : "<length> | auto",
3459 + "columns" : 1,
3460 + "content" : 1,
3461 + "counter-increment" : 1,
3462 + "counter-reset" : 1,
3463 + "crop" : "<shape> | auto",
3464 + "cue" : "cue-after | cue-before",
3465 + "cue-after" : 1,
3466 + "cue-before" : 1,
3467 + "cursor" : 1,
3468 +
3469 + //D
3470 + "direction" : "ltr | rtl",
3471 + "display" : "inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | grid | inline-grid | run-in | ruby | ruby-base | ruby-text | ruby-base-container | ruby-text-container | contents | none | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker | -webkit-box | -webkit-inline-box | -ms-flexbox | -ms-inline-flexbox | flex | -webkit-flex | inline-flex | -webkit-inline-flex",
3472 + "dominant-baseline" : "auto | use-script | no-change | reset-size | ideographic | alphabetic | hanging | mathematical | central | middle | text-after-edge | text-before-edge",
3473 + "drop-initial-after-adjust" : "central | middle | after-edge | text-after-edge | ideographic | alphabetic | mathematical | <percentage> | <length>",
3474 + "drop-initial-after-align" : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
3475 + "drop-initial-before-adjust" : "before-edge | text-before-edge | central | middle | hanging | mathematical | <percentage> | <length>",
3476 + "drop-initial-before-align" : "caps-height | baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
3477 + "drop-initial-size" : "auto | line | <length> | <percentage>",
3478 + "drop-initial-value" : "<integer>",
3479 +
3480 + //E
3481 + "elevation" : "<angle> | below | level | above | higher | lower",
3482 + "empty-cells" : "show | hide",
3483 + "enable-background" : 1,
3484 +
3485 + //F
3486 + "fill" : "<paint>",
3487 + "fill-opacity" : "<opacity-value>",
3488 + "fill-rule" : "nonzero | evenodd",
3489 + "filter" : "<filter-function-list> | none",
3490 + "fit" : "fill | hidden | meet | slice",
3491 + "fit-position" : 1,
3492 + "flex" : "<flex>",
3493 + "flex-basis" : "<width>",
3494 + "flex-direction" : "row | row-reverse | column | column-reverse",
3495 + "flex-flow" : "<flex-direction> || <flex-wrap>",
3496 + "flex-grow" : "<number>",
3497 + "flex-shrink" : "<number>",
3498 + "flex-wrap" : "nowrap | wrap | wrap-reverse",
3499 + "-webkit-flex" : "<flex>",
3500 + "-webkit-flex-basis" : "<width>",
3501 + "-webkit-flex-direction" : "row | row-reverse | column | column-reverse",
3502 + "-webkit-flex-flow" : "<flex-direction> || <flex-wrap>",
3503 + "-webkit-flex-grow" : "<number>",
3504 + "-webkit-flex-shrink" : "<number>",
3505 + "-webkit-flex-wrap" : "nowrap | wrap | wrap-reverse",
3506 + "-ms-flex" : "<flex>",
3507 + "-ms-flex-align" : "start | end | center | stretch | baseline",
3508 + "-ms-flex-direction" : "row | row-reverse | column | column-reverse",
3509 + "-ms-flex-order" : "<number>",
3510 + "-ms-flex-pack" : "start | end | center | justify",
3511 + "-ms-flex-wrap" : "nowrap | wrap | wrap-reverse",
3512 + "float" : "left | right | none",
3513 + "float-offset" : 1,
3514 + "flood-color" : 1,
3515 + "flood-opacity" : "<opacity-value>",
3516 + "font" : "<font-shorthand> | caption | icon | menu | message-box | small-caption | status-bar",
3517 + "font-family" : "<font-family>",
3518 + "font-feature-settings" : "<feature-tag-value> | normal",
3519 + "font-kerning" : "auto | normal | none",
3520 + "font-size" : "<font-size>",
3521 + "font-size-adjust" : "<number> | none",
3522 + "font-stretch" : "<font-stretch>",
3523 + "font-style" : "<font-style>",
3524 + "font-variant" : "<font-variant> | normal | none",
3525 + "font-variant-alternates" : "<font-variant-alternates> | normal",
3526 + "font-variant-caps" : "<font-variant-caps> | normal",
3527 + "font-variant-east-asian" : "<font-variant-east-asian> | normal",
3528 + "font-variant-ligatures" : "<font-variant-ligatures> | normal | none",
3529 + "font-variant-numeric" : "<font-variant-numeric> | normal",
3530 + "font-variant-position" : "normal | sub | super",
3531 + "font-weight" : "<font-weight>",
3532 +
3533 + //G
3534 + "glyph-orientation-horizontal" : "<glyph-angle>",
3535 + "glyph-orientation-vertical" : "auto | <glyph-angle>",
3536 + "grid" : 1,
3537 + "grid-area" : 1,
3538 + "grid-auto-columns" : 1,
3539 + "grid-auto-flow" : 1,
3540 + "grid-auto-position" : 1,
3541 + "grid-auto-rows" : 1,
3542 + "grid-cell-stacking" : "columns | rows | layer",
3543 + "grid-column" : 1,
3544 + "grid-columns" : 1,
3545 + "grid-column-align" : "start | end | center | stretch",
3546 + "grid-column-sizing" : 1,
3547 + "grid-column-start" : 1,
3548 + "grid-column-end" : 1,
3549 + "grid-column-span" : "<integer>",
3550 + "grid-flow" : "none | rows | columns",
3551 + "grid-layer" : "<integer>",
3552 + "grid-row" : 1,
3553 + "grid-rows" : 1,
3554 + "grid-row-align" : "start | end | center | stretch",
3555 + "grid-row-start" : 1,
3556 + "grid-row-end" : 1,
3557 + "grid-row-span" : "<integer>",
3558 + "grid-row-sizing" : 1,
3559 + "grid-template" : 1,
3560 + "grid-template-areas" : 1,
3561 + "grid-template-columns" : 1,
3562 + "grid-template-rows" : 1,
3563 +
3564 + //H
3565 + "hanging-punctuation" : 1,
3566 + "height" : "<margin-width> | <content-sizing>",
3567 + "hyphenate-after" : "<integer> | auto",
3568 + "hyphenate-before" : "<integer> | auto",
3569 + "hyphenate-character" : "<string> | auto",
3570 + "hyphenate-lines" : "no-limit | <integer>",
3571 + "hyphenate-resource" : 1,
3572 + "hyphens" : "none | manual | auto",
3573 +
3574 + //I
3575 + "icon" : 1,
3576 + "image-orientation" : "angle | auto",
3577 + "image-rendering" : "auto | optimizeSpeed | optimizeQuality",
3578 + "image-resolution" : 1,
3579 + "ime-mode" : "auto | normal | active | inactive | disabled",
3580 + "inline-box-align" : "last | <integer>",
3581 +
3582 + //J
3583 + "justify-content" : "flex-start | flex-end | center | space-between | space-around",
3584 + "-webkit-justify-content" : "flex-start | flex-end | center | space-between | space-around",
3585 +
3586 + //K
3587 + "kerning" : "auto | <length>",
3588 +
3589 + //L
3590 + "left" : "<margin-width>",
3591 + "letter-spacing" : "<length> | normal",
3592 + "line-height" : "<line-height>",
3593 + "line-break" : "auto | loose | normal | strict",
3594 + "line-stacking" : 1,
3595 + "line-stacking-ruby" : "exclude-ruby | include-ruby",
3596 + "line-stacking-shift" : "consider-shifts | disregard-shifts",
3597 + "line-stacking-strategy" : "inline-line-height | block-line-height | max-height | grid-height",
3598 + "list-style" : 1,
3599 + "list-style-image" : "<uri> | none",
3600 + "list-style-position" : "inside | outside",
3601 + "list-style-type" : "disc | circle | square | decimal | decimal-leading-zero | lower-roman | upper-roman | lower-greek | lower-latin | upper-latin | armenian | georgian | lower-alpha | upper-alpha | none",
3602 +
3603 + //M
3604 + "margin" : "<margin-width>{1,4}",
3605 + "margin-bottom" : "<margin-width>",
3606 + "margin-left" : "<margin-width>",
3607 + "margin-right" : "<margin-width>",
3608 + "margin-top" : "<margin-width>",
3609 + "mark" : 1,
3610 + "mark-after" : 1,
3611 + "mark-before" : 1,
3612 + "marker" : 1,
3613 + "marker-end" : 1,
3614 + "marker-mid" : 1,
3615 + "marker-start" : 1,
3616 + "marks" : 1,
3617 + "marquee-direction" : 1,
3618 + "marquee-play-count" : 1,
3619 + "marquee-speed" : 1,
3620 + "marquee-style" : 1,
3621 + "mask" : 1,
3622 + "max-height" : "<length> | <percentage> | <content-sizing> | none",
3623 + "max-width" : "<length> | <percentage> | <content-sizing> | none",
3624 + "min-height" : "<length> | <percentage> | <content-sizing> | contain-floats | -moz-contain-floats | -webkit-contain-floats",
3625 + "min-width" : "<length> | <percentage> | <content-sizing> | contain-floats | -moz-contain-floats | -webkit-contain-floats",
3626 + "move-to" : 1,
3627 +
3628 + //N
3629 + "nav-down" : 1,
3630 + "nav-index" : 1,
3631 + "nav-left" : 1,
3632 + "nav-right" : 1,
3633 + "nav-up" : 1,
3634 +
3635 + //O
3636 + "object-fit" : "fill | contain | cover | none | scale-down",
3637 + "object-position" : "<position>",
3638 + "opacity" : "<opacity-value>",
3639 + "order" : "<integer>",
3640 + "-webkit-order" : "<integer>",
3641 + "orphans" : "<integer>",
3642 + "outline" : 1,
3643 + "outline-color" : "<color> | invert",
3644 + "outline-offset" : 1,
3645 + "outline-style" : "<border-style>",
3646 + "outline-width" : "<border-width>",
3647 + "overflow" : "visible | hidden | scroll | auto",
3648 + "overflow-style" : 1,
3649 + "overflow-wrap" : "normal | break-word",
3650 + "overflow-x" : 1,
3651 + "overflow-y" : 1,
3652 +
3653 + //P
3654 + "padding" : "<padding-width>{1,4}",
3655 + "padding-bottom" : "<padding-width>",
3656 + "padding-left" : "<padding-width>",
3657 + "padding-right" : "<padding-width>",
3658 + "padding-top" : "<padding-width>",
3659 + "page" : 1,
3660 + "page-break-after" : "auto | always | avoid | left | right",
3661 + "page-break-before" : "auto | always | avoid | left | right",
3662 + "page-break-inside" : "auto | avoid",
3663 + "page-policy" : 1,
3664 + "pause" : 1,
3665 + "pause-after" : 1,
3666 + "pause-before" : 1,
3667 + "perspective" : 1,
3668 + "perspective-origin" : 1,
3669 + "phonemes" : 1,
3670 + "pitch" : 1,
3671 + "pitch-range" : 1,
3672 + "play-during" : 1,
3673 + "pointer-events" : "auto | none | visiblePainted | visibleFill | visibleStroke | visible | painted | fill | stroke | all",
3674 + "position" : "static | relative | absolute | fixed",
3675 + "presentation-level" : 1,
3676 + "punctuation-trim" : 1,
3677 +
3678 + //Q
3679 + "quotes" : 1,
3680 +
3681 + //R
3682 + "rendering-intent" : 1,
3683 + "resize" : 1,
3684 + "rest" : 1,
3685 + "rest-after" : 1,
3686 + "rest-before" : 1,
3687 + "richness" : 1,
3688 + "right" : "<margin-width>",
3689 + "rotation" : 1,
3690 + "rotation-point" : 1,
3691 + "ruby-align" : 1,
3692 + "ruby-overhang" : 1,
3693 + "ruby-position" : 1,
3694 + "ruby-span" : 1,
3695 +
3696 + //S
3697 + "shape-rendering" : "auto | optimizeSpeed | crispEdges | geometricPrecision",
3698 + "size" : 1,
3699 + "speak" : "normal | none | spell-out",
3700 + "speak-header" : "once | always",
3701 + "speak-numeral" : "digits | continuous",
3702 + "speak-punctuation" : "code | none",
3703 + "speech-rate" : 1,
3704 + "src" : 1,
3705 + "stop-color" : 1,
3706 + "stop-opacity" : "<opacity-value>",
3707 + "stress" : 1,
3708 + "string-set" : 1,
3709 + "stroke" : "<paint>",
3710 + "stroke-dasharray" : "none | <dasharray>",
3711 + "stroke-dashoffset" : "<percentage> | <length>",
3712 + "stroke-linecap" : "butt | round | square",
3713 + "stroke-linejoin" : "miter | round | bevel",
3714 + "stroke-miterlimit" : "<miterlimit>",
3715 + "stroke-opacity" : "<opacity-value>",
3716 + "stroke-width" : "<percentage> | <length>",
3717 +
3718 + "table-layout" : "auto | fixed",
3719 + "tab-size" : "<integer> | <length>",
3720 + "target" : 1,
3721 + "target-name" : 1,
3722 + "target-new" : 1,
3723 + "target-position" : 1,
3724 + "text-align" : "left | right | center | justify | match-parent | start | end",
3725 + "text-align-last" : 1,
3726 + "text-anchor" : "start | middle | end",
3727 + "text-decoration" : "<text-decoration-line> || <text-decoration-style> || <text-decoration-color>",
3728 + "text-decoration-color" : "<text-decoration-color>",
3729 + "text-decoration-line" : "<text-decoration-line>",
3730 + "text-decoration-style" : "<text-decoration-style>",
3731 + "text-emphasis" : 1,
3732 + "text-height" : 1,
3733 + "text-indent" : "<length> | <percentage>",
3734 + "text-justify" : "auto | none | inter-word | inter-ideograph | inter-cluster | distribute | kashida",
3735 + "text-outline" : 1,
3736 + "text-overflow" : 1,
3737 + "text-rendering" : "auto | optimizeSpeed | optimizeLegibility | geometricPrecision",
3738 + "text-shadow" : 1,
3739 + "text-transform" : "capitalize | uppercase | lowercase | none",
3740 + "text-wrap" : "normal | none | avoid",
3741 + "top" : "<margin-width>",
3742 + "-ms-touch-action" : "auto | none | pan-x | pan-y | pan-left | pan-right | pan-up | pan-down | manipulation",
3743 + "touch-action" : "auto | none | pan-x | pan-y | pan-left | pan-right | pan-up | pan-down | manipulation",
3744 + "transform" : 1,
3745 + "transform-origin" : 1,
3746 + "transform-style" : 1,
3747 + "transition" : 1,
3748 + "transition-delay" : 1,
3749 + "transition-duration" : 1,
3750 + "transition-property" : 1,
3751 + "transition-timing-function" : 1,
3752 +
3753 + //U
3754 + "unicode-bidi" : "normal | embed | isolate | bidi-override | isolate-override | plaintext",
3755 + "user-modify" : "read-only | read-write | write-only",
3756 + "user-select" : "none | text | toggle | element | elements | all",
3757 +
3758 + //V
3759 + "vertical-align" : "auto | use-script | baseline | sub | super | top | text-top | central | middle | bottom | text-bottom | <percentage> | <length>",
3760 + "visibility" : "visible | hidden | collapse",
3761 + "voice-balance" : 1,
3762 + "voice-duration" : 1,
3763 + "voice-family" : 1,
3764 + "voice-pitch" : 1,
3765 + "voice-pitch-range" : 1,
3766 + "voice-rate" : 1,
3767 + "voice-stress" : 1,
3768 + "voice-volume" : 1,
3769 + "volume" : 1,
3770 +
3771 + //W
3772 + "white-space" : "normal | pre | nowrap | pre-wrap | pre-line | -pre-wrap | -o-pre-wrap | -moz-pre-wrap | -hp-pre-wrap", // https://perishablepress.com/wrapping-content/
3773 + "white-space-collapse" : 1,
3774 + "widows" : "<integer>",
3775 + "width" : "<length> | <percentage> | <content-sizing> | auto",
3776 + "will-change" : "<will-change>",
3777 + "word-break" : "normal | keep-all | break-all",
3778 + "word-spacing" : "<length> | normal",
3779 + "word-wrap" : "normal | break-word",
3780 + "writing-mode" : "horizontal-tb | vertical-rl | vertical-lr | lr-tb | rl-tb | tb-rl | bt-rl | tb-lr | bt-lr | lr-bt | rl-bt | lr | rl | tb",
3781 +
3782 + //Z
3783 + "z-index" : "<integer> | auto",
3784 + "zoom" : "<number> | <percentage> | normal"
3785 + };
3786 +
3787 + },{}],8:[function(require,module,exports){
3788 + "use strict";
3789 +
3790 + module.exports = PropertyName;
3791 +
3792 + var SyntaxUnit = require("../util/SyntaxUnit");
3793 +
3794 + var Parser = require("./Parser");
3795 +
3796 + /**
3797 + * Represents a selector combinator (whitespace, +, >).
3798 + * @namespace parserlib.css
3799 + * @class PropertyName
3800 + * @extends parserlib.util.SyntaxUnit
3801 + * @constructor
3802 + * @param {String} text The text representation of the unit.
3803 + * @param {String} hack The type of IE hack applied ("*", "_", or null).
3804 + * @param {int} line The line of text on which the unit resides.
3805 + * @param {int} col The column of text on which the unit resides.
3806 + */
3807 + function PropertyName(text, hack, line, col) {
3808 +
3809 + SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_NAME_TYPE);
3810 +
3811 + /**
3812 + * The type of IE hack applied ("*", "_", or null).
3813 + * @type String
3814 + * @property hack
3815 + */
3816 + this.hack = hack;
3817 +
3818 + }
3819 +
3820 + PropertyName.prototype = new SyntaxUnit();
3821 + PropertyName.prototype.constructor = PropertyName;
3822 + PropertyName.prototype.toString = function() {
3823 + return (this.hack ? this.hack : "") + this.text;
3824 + };
3825 +
3826 + },{"../util/SyntaxUnit":26,"./Parser":6}],9:[function(require,module,exports){
3827 + "use strict";
3828 +
3829 + module.exports = PropertyValue;
3830 +
3831 + var SyntaxUnit = require("../util/SyntaxUnit");
3832 +
3833 + var Parser = require("./Parser");
3834 +
3835 + /**
3836 + * Represents a single part of a CSS property value, meaning that it represents
3837 + * just everything single part between ":" and ";". If there are multiple values
3838 + * separated by commas, this type represents just one of the values.
3839 + * @param {String[]} parts An array of value parts making up this value.
3840 + * @param {int} line The line of text on which the unit resides.
3841 + * @param {int} col The column of text on which the unit resides.
3842 + * @namespace parserlib.css
3843 + * @class PropertyValue
3844 + * @extends parserlib.util.SyntaxUnit
3845 + * @constructor
3846 + */
3847 + function PropertyValue(parts, line, col) {
3848 +
3849 + SyntaxUnit.call(this, parts.join(" "), line, col, Parser.PROPERTY_VALUE_TYPE);
3850 +
3851 + /**
3852 + * The parts that make up the selector.
3853 + * @type Array
3854 + * @property parts
3855 + */
3856 + this.parts = parts;
3857 +
3858 + }
3859 +
3860 + PropertyValue.prototype = new SyntaxUnit();
3861 + PropertyValue.prototype.constructor = PropertyValue;
3862 +
3863 +
3864 + },{"../util/SyntaxUnit":26,"./Parser":6}],10:[function(require,module,exports){
3865 + "use strict";
3866 +
3867 + module.exports = PropertyValueIterator;
3868 +
3869 + /**
3870 + * A utility class that allows for easy iteration over the various parts of a
3871 + * property value.
3872 + * @param {parserlib.css.PropertyValue} value The property value to iterate over.
3873 + * @namespace parserlib.css
3874 + * @class PropertyValueIterator
3875 + * @constructor
3876 + */
3877 + function PropertyValueIterator(value) {
3878 +
3879 + /**
3880 + * Iterator value
3881 + * @type int
3882 + * @property _i
3883 + * @private
3884 + */
3885 + this._i = 0;
3886 +
3887 + /**
3888 + * The parts that make up the value.
3889 + * @type Array
3890 + * @property _parts
3891 + * @private
3892 + */
3893 + this._parts = value.parts;
3894 +
3895 + /**
3896 + * Keeps track of bookmarks along the way.
3897 + * @type Array
3898 + * @property _marks
3899 + * @private
3900 + */
3901 + this._marks = [];
3902 +
3903 + /**
3904 + * Holds the original property value.
3905 + * @type parserlib.css.PropertyValue
3906 + * @property value
3907 + */
3908 + this.value = value;
3909 +
3910 + }
3911 +
3912 + /**
3913 + * Returns the total number of parts in the value.
3914 + * @return {int} The total number of parts in the value.
3915 + * @method count
3916 + */
3917 + PropertyValueIterator.prototype.count = function() {
3918 + return this._parts.length;
3919 + };
3920 +
3921 + /**
3922 + * Indicates if the iterator is positioned at the first item.
3923 + * @return {Boolean} True if positioned at first item, false if not.
3924 + * @method isFirst
3925 + */
3926 + PropertyValueIterator.prototype.isFirst = function() {
3927 + return this._i === 0;
3928 + };
3929 +
3930 + /**
3931 + * Indicates if there are more parts of the property value.
3932 + * @return {Boolean} True if there are more parts, false if not.
3933 + * @method hasNext
3934 + */
3935 + PropertyValueIterator.prototype.hasNext = function() {
3936 + return this._i < this._parts.length;
3937 + };
3938 +
3939 + /**
3940 + * Marks the current spot in the iteration so it can be restored to
3941 + * later on.
3942 + * @return {void}
3943 + * @method mark
3944 + */
3945 + PropertyValueIterator.prototype.mark = function() {
3946 + this._marks.push(this._i);
3947 + };
3948 +
3949 + /**
3950 + * Returns the next part of the property value or null if there is no next
3951 + * part. Does not move the internal counter forward.
3952 + * @return {parserlib.css.PropertyValuePart} The next part of the property value or null if there is no next
3953 + * part.
3954 + * @method peek
3955 + */
3956 + PropertyValueIterator.prototype.peek = function(count) {
3957 + return this.hasNext() ? this._parts[this._i + (count || 0)] : null;
3958 + };
3959 +
3960 + /**
3961 + * Returns the next part of the property value or null if there is no next
3962 + * part.
3963 + * @return {parserlib.css.PropertyValuePart} The next part of the property value or null if there is no next
3964 + * part.
3965 + * @method next
3966 + */
3967 + PropertyValueIterator.prototype.next = function() {
3968 + return this.hasNext() ? this._parts[this._i++] : null;
3969 + };
3970 +
3971 + /**
3972 + * Returns the previous part of the property value or null if there is no
3973 + * previous part.
3974 + * @return {parserlib.css.PropertyValuePart} The previous part of the
3975 + * property value or null if there is no previous part.
3976 + * @method previous
3977 + */
3978 + PropertyValueIterator.prototype.previous = function() {
3979 + return this._i > 0 ? this._parts[--this._i] : null;
3980 + };
3981 +
3982 + /**
3983 + * Restores the last saved bookmark.
3984 + * @return {void}
3985 + * @method restore
3986 + */
3987 + PropertyValueIterator.prototype.restore = function() {
3988 + if (this._marks.length) {
3989 + this._i = this._marks.pop();
3990 + }
3991 + };
3992 +
3993 + /**
3994 + * Drops the last saved bookmark.
3995 + * @return {void}
3996 + * @method drop
3997 + */
3998 + PropertyValueIterator.prototype.drop = function() {
3999 + this._marks.pop();
4000 + };
4001 +
4002 + },{}],11:[function(require,module,exports){
4003 + "use strict";
4004 +
4005 + module.exports = PropertyValuePart;
4006 +
4007 + var SyntaxUnit = require("../util/SyntaxUnit");
4008 +
4009 + var Colors = require("./Colors");
4010 + var Parser = require("./Parser");
4011 + var Tokens = require("./Tokens");
4012 +
4013 + /**
4014 + * Represents a single part of a CSS property value, meaning that it represents
4015 + * just one part of the data between ":" and ";".
4016 + * @param {String} text The text representation of the unit.
4017 + * @param {int} line The line of text on which the unit resides.
4018 + * @param {int} col The column of text on which the unit resides.
4019 + * @namespace parserlib.css
4020 + * @class PropertyValuePart
4021 + * @extends parserlib.util.SyntaxUnit
4022 + * @constructor
4023 + */
4024 + function PropertyValuePart(text, line, col, optionalHint) {
4025 + var hint = optionalHint || {};
4026 +
4027 + SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_VALUE_PART_TYPE);
4028 +
4029 + /**
4030 + * Indicates the type of value unit.
4031 + * @type String
4032 + * @property type
4033 + */
4034 + this.type = "unknown";
4035 +
4036 + //figure out what type of data it is
4037 +
4038 + var temp;
4039 +
4040 + //it is a measurement?
4041 + if (/^([+\-]?[\d\.]+)([a-z]+)$/i.test(text)) { //dimension
4042 + this.type = "dimension";
4043 + this.value = +RegExp.$1;
4044 + this.units = RegExp.$2;
4045 +
4046 + //try to narrow down
4047 + switch (this.units.toLowerCase()) {
4048 +
4049 + case "em":
4050 + case "rem":
4051 + case "ex":
4052 + case "px":
4053 + case "cm":
4054 + case "mm":
4055 + case "in":
4056 + case "pt":
4057 + case "pc":
4058 + case "ch":
4059 + case "vh":
4060 + case "vw":
4061 + case "vmax":
4062 + case "vmin":
4063 + this.type = "length";
4064 + break;
4065 +
4066 + case "fr":
4067 + this.type = "grid";
4068 + break;
4069 +
4070 + case "deg":
4071 + case "rad":
4072 + case "grad":
4073 + case "turn":
4074 + this.type = "angle";
4075 + break;
4076 +
4077 + case "ms":
4078 + case "s":
4079 + this.type = "time";
4080 + break;
4081 +
4082 + case "hz":
4083 + case "khz":
4084 + this.type = "frequency";
4085 + break;
4086 +
4087 + case "dpi":
4088 + case "dpcm":
4089 + this.type = "resolution";
4090 + break;
4091 +
4092 + //default
4093 +
4094 + }
4095 +
4096 + } else if (/^([+\-]?[\d\.]+)%$/i.test(text)) { //percentage
4097 + this.type = "percentage";
4098 + this.value = +RegExp.$1;
4099 + } else if (/^([+\-]?\d+)$/i.test(text)) { //integer
4100 + this.type = "integer";
4101 + this.value = +RegExp.$1;
4102 + } else if (/^([+\-]?[\d\.]+)$/i.test(text)) { //number
4103 + this.type = "number";
4104 + this.value = +RegExp.$1;
4105 +
4106 + } else if (/^#([a-f0-9]{3,6})/i.test(text)) { //hexcolor
4107 + this.type = "color";
4108 + temp = RegExp.$1;
4109 + if (temp.length === 3) {
4110 + this.red = parseInt(temp.charAt(0)+temp.charAt(0), 16);
4111 + this.green = parseInt(temp.charAt(1)+temp.charAt(1), 16);
4112 + this.blue = parseInt(temp.charAt(2)+temp.charAt(2), 16);
4113 + } else {
4114 + this.red = parseInt(temp.substring(0, 2), 16);
4115 + this.green = parseInt(temp.substring(2, 4), 16);
4116 + this.blue = parseInt(temp.substring(4, 6), 16);
4117 + }
4118 + } else if (/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i.test(text)) { //rgb() color with absolute numbers
4119 + this.type = "color";
4120 + this.red = +RegExp.$1;
4121 + this.green = +RegExp.$2;
4122 + this.blue = +RegExp.$3;
4123 + } else if (/^rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)) { //rgb() color with percentages
4124 + this.type = "color";
4125 + this.red = +RegExp.$1 * 255 / 100;
4126 + this.green = +RegExp.$2 * 255 / 100;
4127 + this.blue = +RegExp.$3 * 255 / 100;
4128 + } else if (/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //rgba() color with absolute numbers
4129 + this.type = "color";
4130 + this.red = +RegExp.$1;
4131 + this.green = +RegExp.$2;
4132 + this.blue = +RegExp.$3;
4133 + this.alpha = +RegExp.$4;
4134 + } else if (/^rgba\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //rgba() color with percentages
4135 + this.type = "color";
4136 + this.red = +RegExp.$1 * 255 / 100;
4137 + this.green = +RegExp.$2 * 255 / 100;
4138 + this.blue = +RegExp.$3 * 255 / 100;
4139 + this.alpha = +RegExp.$4;
4140 + } else if (/^hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)) { //hsl()
4141 + this.type = "color";
4142 + this.hue = +RegExp.$1;
4143 + this.saturation = +RegExp.$2 / 100;
4144 + this.lightness = +RegExp.$3 / 100;
4145 + } else if (/^hsla\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //hsla() color with percentages
4146 + this.type = "color";
4147 + this.hue = +RegExp.$1;
4148 + this.saturation = +RegExp.$2 / 100;
4149 + this.lightness = +RegExp.$3 / 100;
4150 + this.alpha = +RegExp.$4;
4151 + } else if (/^url\(("([^\\"]|\\.)*")\)/i.test(text)) { //URI
4152 + // generated by TokenStream.readURI, so always double-quoted.
4153 + this.type = "uri";
4154 + this.uri = PropertyValuePart.parseString(RegExp.$1);
4155 + } else if (/^([^\(]+)\(/i.test(text)) {
4156 + this.type = "function";
4157 + this.name = RegExp.$1;
4158 + this.value = text;
4159 + } else if (/^"([^\n\r\f\\"]|\\\r\n|\\[^\r0-9a-f]|\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)*"/i.test(text)) { //double-quoted string
4160 + this.type = "string";
4161 + this.value = PropertyValuePart.parseString(text);
4162 + } else if (/^'([^\n\r\f\\']|\\\r\n|\\[^\r0-9a-f]|\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)*'/i.test(text)) { //single-quoted string
4163 + this.type = "string";
4164 + this.value = PropertyValuePart.parseString(text);
4165 + } else if (Colors[text.toLowerCase()]) { //named color
4166 + this.type = "color";
4167 + temp = Colors[text.toLowerCase()].substring(1);
4168 + this.red = parseInt(temp.substring(0, 2), 16);
4169 + this.green = parseInt(temp.substring(2, 4), 16);
4170 + this.blue = parseInt(temp.substring(4, 6), 16);
4171 + } else if (/^[,\/]$/.test(text)) {
4172 + this.type = "operator";
4173 + this.value = text;
4174 + } else if (/^-?[a-z_\u00A0-\uFFFF][a-z0-9\-_\u00A0-\uFFFF]*$/i.test(text)) {
4175 + this.type = "identifier";
4176 + this.value = text;
4177 + }
4178 +
4179 + // There can be ambiguity with escape sequences in identifiers, as
4180 + // well as with "color" parts which are also "identifiers", so record
4181 + // an explicit hint when the token generating this PropertyValuePart
4182 + // was an identifier.
4183 + this.wasIdent = Boolean(hint.ident);
4184 +
4185 + }
4186 +
4187 + PropertyValuePart.prototype = new SyntaxUnit();
4188 + PropertyValuePart.prototype.constructor = PropertyValuePart;
4189 +
4190 + /**
4191 + * Helper method to parse a CSS string.
4192 + */
4193 + PropertyValuePart.parseString = function(str) {
4194 + str = str.slice(1, -1); // Strip surrounding single/double quotes
4195 + var replacer = function(match, esc) {
4196 + if (/^(\n|\r\n|\r|\f)$/.test(esc)) {
4197 + return "";
4198 + }
4199 + var m = /^[0-9a-f]{1,6}/i.exec(esc);
4200 + if (m) {
4201 + var codePoint = parseInt(m[0], 16);
4202 + if (String.fromCodePoint) {
4203 + return String.fromCodePoint(codePoint);
4204 + } else {
4205 + // XXX No support for surrogates on old JavaScript engines.
4206 + return String.fromCharCode(codePoint);
4207 + }
4208 + }
4209 + return esc;
4210 + };
4211 + return str.replace(/\\(\r\n|[^\r0-9a-f]|[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)/ig,
4212 + replacer);
4213 + };
4214 +
4215 + /**
4216 + * Helper method to serialize a CSS string.
4217 + */
4218 + PropertyValuePart.serializeString = function(value) {
4219 + var replacer = function(match, c) {
4220 + if (c === "\"") {
4221 + return "\\" + c;
4222 + }
4223 + var cp = String.codePointAt ? String.codePointAt(0) :
4224 + // We only escape non-surrogate chars, so using charCodeAt
4225 + // is harmless here.
4226 + String.charCodeAt(0);
4227 + return "\\" + cp.toString(16) + " ";
4228 + };
4229 + return "\"" + value.replace(/["\r\n\f]/g, replacer) + "\"";
4230 + };
4231 +
4232 + /**
4233 + * Create a new syntax unit based solely on the given token.
4234 + * Convenience method for creating a new syntax unit when
4235 + * it represents a single token instead of multiple.
4236 + * @param {Object} token The token object to represent.
4237 + * @return {parserlib.css.PropertyValuePart} The object representing the token.
4238 + * @static
4239 + * @method fromToken
4240 + */
4241 + PropertyValuePart.fromToken = function(token) {
4242 + var part = new PropertyValuePart(token.value, token.startLine, token.startCol, {
4243 + // Tokens can have escaped characters that would fool the type
4244 + // identification in the PropertyValuePart constructor, so pass
4245 + // in a hint if this was an identifier.
4246 + ident: token.type === Tokens.IDENT
4247 + });
4248 + return part;
4249 + };
4250 +
4251 + },{"../util/SyntaxUnit":26,"./Colors":1,"./Parser":6,"./Tokens":18}],12:[function(require,module,exports){
4252 + "use strict";
4253 +
4254 + var Pseudos = module.exports = {
4255 + __proto__: null,
4256 + ":first-letter": 1,
4257 + ":first-line": 1,
4258 + ":before": 1,
4259 + ":after": 1
4260 + };
4261 +
4262 + Pseudos.ELEMENT = 1;
4263 + Pseudos.CLASS = 2;
4264 +
4265 + Pseudos.isElement = function(pseudo) {
4266 + return pseudo.indexOf("::") === 0 || Pseudos[pseudo.toLowerCase()] === Pseudos.ELEMENT;
4267 + };
4268 +
4269 + },{}],13:[function(require,module,exports){
4270 + "use strict";
4271 +
4272 + module.exports = Selector;
4273 +
4274 + var SyntaxUnit = require("../util/SyntaxUnit");
4275 +
4276 + var Parser = require("./Parser");
4277 + var Specificity = require("./Specificity");
4278 +
4279 + /**
4280 + * Represents an entire single selector, including all parts but not
4281 + * including multiple selectors (those separated by commas).
4282 + * @namespace parserlib.css
4283 + * @class Selector
4284 + * @extends parserlib.util.SyntaxUnit
4285 + * @constructor
4286 + * @param {Array} parts Array of selectors parts making up this selector.
4287 + * @param {int} line The line of text on which the unit resides.
4288 + * @param {int} col The column of text on which the unit resides.
4289 + */
4290 + function Selector(parts, line, col) {
4291 +
4292 + SyntaxUnit.call(this, parts.join(" "), line, col, Parser.SELECTOR_TYPE);
4293 +
4294 + /**
4295 + * The parts that make up the selector.
4296 + * @type Array
4297 + * @property parts
4298 + */
4299 + this.parts = parts;
4300 +
4301 + /**
4302 + * The specificity of the selector.
4303 + * @type parserlib.css.Specificity
4304 + * @property specificity
4305 + */
4306 + this.specificity = Specificity.calculate(this);
4307 +
4308 + }
4309 +
4310 + Selector.prototype = new SyntaxUnit();
4311 + Selector.prototype.constructor = Selector;
4312 +
4313 +
4314 + },{"../util/SyntaxUnit":26,"./Parser":6,"./Specificity":16}],14:[function(require,module,exports){
4315 + "use strict";
4316 +
4317 + module.exports = SelectorPart;
4318 +
4319 + var SyntaxUnit = require("../util/SyntaxUnit");
4320 +
4321 + var Parser = require("./Parser");
4322 +
4323 + /**
4324 + * Represents a single part of a selector string, meaning a single set of
4325 + * element name and modifiers. This does not include combinators such as
4326 + * spaces, +, >, etc.
4327 + * @namespace parserlib.css
4328 + * @class SelectorPart
4329 + * @extends parserlib.util.SyntaxUnit
4330 + * @constructor
4331 + * @param {String} elementName The element name in the selector or null
4332 + * if there is no element name.
4333 + * @param {Array} modifiers Array of individual modifiers for the element.
4334 + * May be empty if there are none.
4335 + * @param {String} text The text representation of the unit.
4336 + * @param {int} line The line of text on which the unit resides.
4337 + * @param {int} col The column of text on which the unit resides.
4338 + */
4339 + function SelectorPart(elementName, modifiers, text, line, col) {
4340 +
4341 + SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_PART_TYPE);
4342 +
4343 + /**
4344 + * The tag name of the element to which this part
4345 + * of the selector affects.
4346 + * @type String
4347 + * @property elementName
4348 + */
4349 + this.elementName = elementName;
4350 +
4351 + /**
4352 + * The parts that come after the element name, such as class names, IDs,
4353 + * pseudo classes/elements, etc.
4354 + * @type Array
4355 + * @property modifiers
4356 + */
4357 + this.modifiers = modifiers;
4358 +
4359 + }
4360 +
4361 + SelectorPart.prototype = new SyntaxUnit();
4362 + SelectorPart.prototype.constructor = SelectorPart;
4363 +
4364 +
4365 + },{"../util/SyntaxUnit":26,"./Parser":6}],15:[function(require,module,exports){
4366 + "use strict";
4367 +
4368 + module.exports = SelectorSubPart;
4369 +
4370 + var SyntaxUnit = require("../util/SyntaxUnit");
4371 +
4372 + var Parser = require("./Parser");
4373 +
4374 + /**
4375 + * Represents a selector modifier string, meaning a class name, element name,
4376 + * element ID, pseudo rule, etc.
4377 + * @namespace parserlib.css
4378 + * @class SelectorSubPart
4379 + * @extends parserlib.util.SyntaxUnit
4380 + * @constructor
4381 + * @param {String} text The text representation of the unit.
4382 + * @param {String} type The type of selector modifier.
4383 + * @param {int} line The line of text on which the unit resides.
4384 + * @param {int} col The column of text on which the unit resides.
4385 + */
4386 + function SelectorSubPart(text, type, line, col) {
4387 +
4388 + SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_SUB_PART_TYPE);
4389 +
4390 + /**
4391 + * The type of modifier.
4392 + * @type String
4393 + * @property type
4394 + */
4395 + this.type = type;
4396 +
4397 + /**
4398 + * Some subparts have arguments, this represents them.
4399 + * @type Array
4400 + * @property args
4401 + */
4402 + this.args = [];
4403 +
4404 + }
4405 +
4406 + SelectorSubPart.prototype = new SyntaxUnit();
4407 + SelectorSubPart.prototype.constructor = SelectorSubPart;
4408 +
4409 +
4410 + },{"../util/SyntaxUnit":26,"./Parser":6}],16:[function(require,module,exports){
4411 + "use strict";
4412 +
4413 + module.exports = Specificity;
4414 +
4415 + var Pseudos = require("./Pseudos");
4416 + var SelectorPart = require("./SelectorPart");
4417 +
4418 + /**
4419 + * Represents a selector's specificity.
4420 + * @namespace parserlib.css
4421 + * @class Specificity
4422 + * @constructor
4423 + * @param {int} a Should be 1 for inline styles, zero for stylesheet styles
4424 + * @param {int} b Number of ID selectors
4425 + * @param {int} c Number of classes and pseudo classes
4426 + * @param {int} d Number of element names and pseudo elements
4427 + */
4428 + function Specificity(a, b, c, d) {
4429 + this.a = a;
4430 + this.b = b;
4431 + this.c = c;
4432 + this.d = d;
4433 + }
4434 +
4435 + Specificity.prototype = {
4436 + constructor: Specificity,
4437 +
4438 + /**
4439 + * Compare this specificity to another.
4440 + * @param {Specificity} other The other specificity to compare to.
4441 + * @return {int} -1 if the other specificity is larger, 1 if smaller, 0 if equal.
4442 + * @method compare
4443 + */
4444 + compare: function(other) {
4445 + var comps = ["a", "b", "c", "d"],
4446 + i, len;
4447 +
4448 + for (i=0, len=comps.length; i < len; i++) {
4449 + if (this[comps[i]] < other[comps[i]]) {
4450 + return -1;
4451 + } else if (this[comps[i]] > other[comps[i]]) {
4452 + return 1;
4453 + }
4454 + }
4455 +
4456 + return 0;
4457 + },
4458 +
4459 + /**
4460 + * Creates a numeric value for the specificity.
4461 + * @return {int} The numeric value for the specificity.
4462 + * @method valueOf
4463 + */
4464 + valueOf: function() {
4465 + return (this.a * 1000) + (this.b * 100) + (this.c * 10) + this.d;
4466 + },
4467 +
4468 + /**
4469 + * Returns a string representation for specificity.
4470 + * @return {String} The string representation of specificity.
4471 + * @method toString
4472 + */
4473 + toString: function() {
4474 + return this.a + "," + this.b + "," + this.c + "," + this.d;
4475 + }
4476 +
4477 + };
4478 +
4479 + /**
4480 + * Calculates the specificity of the given selector.
4481 + * @param {parserlib.css.Selector} The selector to calculate specificity for.
4482 + * @return {parserlib.css.Specificity} The specificity of the selector.
4483 + * @static
4484 + * @method calculate
4485 + */
4486 + Specificity.calculate = function(selector) {
4487 +
4488 + var i, len,
4489 + part,
4490 + b=0, c=0, d=0;
4491 +
4492 + function updateValues(part) {
4493 +
4494 + var i, j, len, num,
4495 + elementName = part.elementName ? part.elementName.text : "",
4496 + modifier;
4497 +
4498 + if (elementName && elementName.charAt(elementName.length-1) !== "*") {
4499 + d++;
4500 + }
4501 +
4502 + for (i=0, len=part.modifiers.length; i < len; i++) {
4503 + modifier = part.modifiers[i];
4504 + switch (modifier.type) {
4505 + case "class":
4506 + case "attribute":
4507 + c++;
4508 + break;
4509 +
4510 + case "id":
4511 + b++;
4512 + break;
4513 +
4514 + case "pseudo":
4515 + if (Pseudos.isElement(modifier.text)) {
4516 + d++;
4517 + } else {
4518 + c++;
4519 + }
4520 + break;
4521 +
4522 + case "not":
4523 + for (j=0, num=modifier.args.length; j < num; j++) {
4524 + updateValues(modifier.args[j]);
4525 + }
4526 + }
4527 + }
4528 + }
4529 +
4530 + for (i=0, len=selector.parts.length; i < len; i++) {
4531 + part = selector.parts[i];
4532 +
4533 + if (part instanceof SelectorPart) {
4534 + updateValues(part);
4535 + }
4536 + }
4537 +
4538 + return new Specificity(0, b, c, d);
4539 + };
4540 +
4541 + },{"./Pseudos":12,"./SelectorPart":14}],17:[function(require,module,exports){
4542 + "use strict";
4543 +
4544 + module.exports = TokenStream;
4545 +
4546 + var TokenStreamBase = require("../util/TokenStreamBase");
4547 +
4548 + var PropertyValuePart = require("./PropertyValuePart");
4549 + var Tokens = require("./Tokens");
4550 +
4551 + var h = /^[0-9a-fA-F]$/,
4552 + nonascii = /^[\u00A0-\uFFFF]$/,
4553 + nl = /\n|\r\n|\r|\f/,
4554 + whitespace = /\u0009|\u000a|\u000c|\u000d|\u0020/;
4555 +
4556 + //-----------------------------------------------------------------------------
4557 + // Helper functions
4558 + //-----------------------------------------------------------------------------
4559 +
4560 +
4561 + function isHexDigit(c) {
4562 + return c !== null && h.test(c);
4563 + }
4564 +
4565 + function isDigit(c) {
4566 + return c !== null && /\d/.test(c);
4567 + }
4568 +
4569 + function isWhitespace(c) {
4570 + return c !== null && whitespace.test(c);
4571 + }
4572 +
4573 + function isNewLine(c) {
4574 + return c !== null && nl.test(c);
4575 + }
4576 +
4577 + function isNameStart(c) {
4578 + return c !== null && /[a-z_\u00A0-\uFFFF\\]/i.test(c);
4579 + }
4580 +
4581 + function isNameChar(c) {
4582 + return c !== null && (isNameStart(c) || /[0-9\-\\]/.test(c));
4583 + }
4584 +
4585 + function isIdentStart(c) {
4586 + return c !== null && (isNameStart(c) || /\-\\/.test(c));
4587 + }
4588 +
4589 + function mix(receiver, supplier) {
4590 + for (var prop in supplier) {
4591 + if (Object.prototype.hasOwnProperty.call(supplier, prop)) {
4592 + receiver[prop] = supplier[prop];
4593 + }
4594 + }
4595 + return receiver;
4596 + }
4597 +
4598 + //-----------------------------------------------------------------------------
4599 + // CSS Token Stream
4600 + //-----------------------------------------------------------------------------
4601 +
4602 +
4603 + /**
4604 + * A token stream that produces CSS tokens.
4605 + * @param {String|Reader} input The source of text to tokenize.
4606 + * @constructor
4607 + * @class TokenStream
4608 + * @namespace parserlib.css
4609 + */
4610 + function TokenStream(input) {
4611 + TokenStreamBase.call(this, input, Tokens);
4612 + }
4613 +
4614 + TokenStream.prototype = mix(new TokenStreamBase(), {
4615 +
4616 + /**
4617 + * Overrides the TokenStreamBase method of the same name
4618 + * to produce CSS tokens.
4619 + * @return {Object} A token object representing the next token.
4620 + * @method _getToken
4621 + * @private
4622 + */
4623 + _getToken: function() {
4624 +
4625 + var c,
4626 + reader = this._reader,
4627 + token = null,
4628 + startLine = reader.getLine(),
4629 + startCol = reader.getCol();
4630 +
4631 + c = reader.read();
4632 +
4633 +
4634 + while (c) {
4635 + switch (c) {
4636 +
4637 + /*
4638 + * Potential tokens:
4639 + * - COMMENT
4640 + * - SLASH
4641 + * - CHAR
4642 + */
4643 + case "/":
4644 +
4645 + if (reader.peek() === "*") {
4646 + token = this.commentToken(c, startLine, startCol);
4647 + } else {
4648 + token = this.charToken(c, startLine, startCol);
4649 + }
4650 + break;
4651 +
4652 + /*
4653 + * Potential tokens:
4654 + * - DASHMATCH
4655 + * - INCLUDES
4656 + * - PREFIXMATCH
4657 + * - SUFFIXMATCH
4658 + * - SUBSTRINGMATCH
4659 + * - CHAR
4660 + */
4661 + case "|":
4662 + case "~":
4663 + case "^":
4664 + case "$":
4665 + case "*":
4666 + if (reader.peek() === "=") {
4667 + token = this.comparisonToken(c, startLine, startCol);
4668 + } else {
4669 + token = this.charToken(c, startLine, startCol);
4670 + }
4671 + break;
4672 +
4673 + /*
4674 + * Potential tokens:
4675 + * - STRING
4676 + * - INVALID
4677 + */
4678 + case "\"":
4679 + case "'":
4680 + token = this.stringToken(c, startLine, startCol);
4681 + break;
4682 +
4683 + /*
4684 + * Potential tokens:
4685 + * - HASH
4686 + * - CHAR
4687 + */
4688 + case "#":
4689 + if (isNameChar(reader.peek())) {
4690 + token = this.hashToken(c, startLine, startCol);
4691 + } else {
4692 + token = this.charToken(c, startLine, startCol);
4693 + }
4694 + break;
4695 +
4696 + /*
4697 + * Potential tokens:
4698 + * - DOT
4699 + * - NUMBER
4700 + * - DIMENSION
4701 + * - PERCENTAGE
4702 + */
4703 + case ".":
4704 + if (isDigit(reader.peek())) {
4705 + token = this.numberToken(c, startLine, startCol);
4706 + } else {
4707 + token = this.charToken(c, startLine, startCol);
4708 + }
4709 + break;
4710 +
4711 + /*
4712 + * Potential tokens:
4713 + * - CDC
4714 + * - MINUS
4715 + * - NUMBER
4716 + * - DIMENSION
4717 + * - PERCENTAGE
4718 + */
4719 + case "-":
4720 + if (reader.peek() === "-") { //could be closing HTML-style comment
4721 + token = this.htmlCommentEndToken(c, startLine, startCol);
4722 + } else if (isNameStart(reader.peek())) {
4723 + token = this.identOrFunctionToken(c, startLine, startCol);
4724 + } else {
4725 + token = this.charToken(c, startLine, startCol);
4726 + }
4727 + break;
4728 +
4729 + /*
4730 + * Potential tokens:
4731 + * - IMPORTANT_SYM
4732 + * - CHAR
4733 + */
4734 + case "!":
4735 + token = this.importantToken(c, startLine, startCol);
4736 + break;
4737 +
4738 + /*
4739 + * Any at-keyword or CHAR
4740 + */
4741 + case "@":
4742 + token = this.atRuleToken(c, startLine, startCol);
4743 + break;
4744 +
4745 + /*
4746 + * Potential tokens:
4747 + * - NOT
4748 + * - CHAR
4749 + */
4750 + case ":":
4751 + token = this.notToken(c, startLine, startCol);
4752 + break;
4753 +
4754 + /*
4755 + * Potential tokens:
4756 + * - CDO
4757 + * - CHAR
4758 + */
4759 + case "<":
4760 + token = this.htmlCommentStartToken(c, startLine, startCol);
4761 + break;
4762 +
4763 + /*
4764 + * Potential tokens:
4765 + * - IDENT
4766 + * - CHAR
4767 + */
4768 + case "\\":
4769 + if (/[^\r\n\f]/.test(reader.peek())) {
4770 + token = this.identOrFunctionToken(this.readEscape(c, true), startLine, startCol);
4771 + } else {
4772 + token = this.charToken(c, startLine, startCol);
4773 + }
4774 + break;
4775 +
4776 + /*
4777 + * Potential tokens:
4778 + * - UNICODE_RANGE
4779 + * - URL
4780 + * - CHAR
4781 + */
4782 + case "U":
4783 + case "u":
4784 + if (reader.peek() === "+") {
4785 + token = this.unicodeRangeToken(c, startLine, startCol);
4786 + break;
4787 + }
4788 + /* falls through */
4789 + default:
4790 +
4791 + /*
4792 + * Potential tokens:
4793 + * - NUMBER
4794 + * - DIMENSION
4795 + * - LENGTH
4796 + * - FREQ
4797 + * - TIME
4798 + * - EMS
4799 + * - EXS
4800 + * - ANGLE
4801 + */
4802 + if (isDigit(c)) {
4803 + token = this.numberToken(c, startLine, startCol);
4804 + } else
4805 +
4806 + /*
4807 + * Potential tokens:
4808 + * - S
4809 + */
4810 + if (isWhitespace(c)) {
4811 + token = this.whitespaceToken(c, startLine, startCol);
4812 + } else
4813 +
4814 + /*
4815 + * Potential tokens:
4816 + * - IDENT
4817 + */
4818 + if (isIdentStart(c)) {
4819 + token = this.identOrFunctionToken(c, startLine, startCol);
4820 + } else {
4821 + /*
4822 + * Potential tokens:
4823 + * - CHAR
4824 + * - PLUS
4825 + */
4826 + token = this.charToken(c, startLine, startCol);
4827 + }
4828 +
4829 + }
4830 +
4831 + //make sure this token is wanted
4832 + //TODO: check channel
4833 + break;
4834 + }
4835 +
4836 + if (!token && c === null) {
4837 + token = this.createToken(Tokens.EOF, null, startLine, startCol);
4838 + }
4839 +
4840 + return token;
4841 + },
4842 +
4843 + //-------------------------------------------------------------------------
4844 + // Methods to create tokens
4845 + //-------------------------------------------------------------------------
4846 +
4847 + /**
4848 + * Produces a token based on available data and the current
4849 + * reader position information. This method is called by other
4850 + * private methods to create tokens and is never called directly.
4851 + * @param {int} tt The token type.
4852 + * @param {String} value The text value of the token.
4853 + * @param {int} startLine The beginning line for the character.
4854 + * @param {int} startCol The beginning column for the character.
4855 + * @param {Object} options (Optional) Specifies a channel property
4856 + * to indicate that a different channel should be scanned
4857 + * and/or a hide property indicating that the token should
4858 + * be hidden.
4859 + * @return {Object} A token object.
4860 + * @method createToken
4861 + */
4862 + createToken: function(tt, value, startLine, startCol, options) {
4863 + var reader = this._reader;
4864 + options = options || {};
4865 +
4866 + return {
4867 + value: value,
4868 + type: tt,
4869 + channel: options.channel,
4870 + endChar: options.endChar,
4871 + hide: options.hide || false,
4872 + startLine: startLine,
4873 + startCol: startCol,
4874 + endLine: reader.getLine(),
4875 + endCol: reader.getCol()
4876 + };
4877 + },
4878 +
4879 + //-------------------------------------------------------------------------
4880 + // Methods to create specific tokens
4881 + //-------------------------------------------------------------------------
4882 +
4883 + /**
4884 + * Produces a token for any at-rule. If the at-rule is unknown, then
4885 + * the token is for a single "@" character.
4886 + * @param {String} first The first character for the token.
4887 + * @param {int} startLine The beginning line for the character.
4888 + * @param {int} startCol The beginning column for the character.
4889 + * @return {Object} A token object.
4890 + * @method atRuleToken
4891 + */
4892 + atRuleToken: function(first, startLine, startCol) {
4893 + var rule = first,
4894 + reader = this._reader,
4895 + tt = Tokens.CHAR,
4896 + ident;
4897 +
4898 + /*
4899 + * First, mark where we are. There are only four @ rules,
4900 + * so anything else is really just an invalid token.
4901 + * Basically, if this doesn't match one of the known @
4902 + * rules, just return '@' as an unknown token and allow
4903 + * parsing to continue after that point.
4904 + */
4905 + reader.mark();
4906 +
4907 + //try to find the at-keyword
4908 + ident = this.readName();
4909 + rule = first + ident;
4910 + tt = Tokens.type(rule.toLowerCase());
4911 +
4912 + //if it's not valid, use the first character only and reset the reader
4913 + if (tt === Tokens.CHAR || tt === Tokens.UNKNOWN) {
4914 + if (rule.length > 1) {
4915 + tt = Tokens.UNKNOWN_SYM;
4916 + } else {
4917 + tt = Tokens.CHAR;
4918 + rule = first;
4919 + reader.reset();
4920 + }
4921 + }
4922 +
4923 + return this.createToken(tt, rule, startLine, startCol);
4924 + },
4925 +
4926 + /**
4927 + * Produces a character token based on the given character
4928 + * and location in the stream. If there's a special (non-standard)
4929 + * token name, this is used; otherwise CHAR is used.
4930 + * @param {String} c The character for the token.
4931 + * @param {int} startLine The beginning line for the character.
4932 + * @param {int} startCol The beginning column for the character.
4933 + * @return {Object} A token object.
4934 + * @method charToken
4935 + */
4936 + charToken: function(c, startLine, startCol) {
4937 + var tt = Tokens.type(c);
4938 + var opts = {};
4939 +
4940 + if (tt === -1) {
4941 + tt = Tokens.CHAR;
4942 + } else {
4943 + opts.endChar = Tokens[tt].endChar;
4944 + }
4945 +
4946 + return this.createToken(tt, c, startLine, startCol, opts);
4947 + },
4948 +
4949 + /**
4950 + * Produces a character token based on the given character
4951 + * and location in the stream. If there's a special (non-standard)
4952 + * token name, this is used; otherwise CHAR is used.
4953 + * @param {String} first The first character for the token.
4954 + * @param {int} startLine The beginning line for the character.
4955 + * @param {int} startCol The beginning column for the character.
4956 + * @return {Object} A token object.
4957 + * @method commentToken
4958 + */
4959 + commentToken: function(first, startLine, startCol) {
4960 + var comment = this.readComment(first);
4961 +
4962 + return this.createToken(Tokens.COMMENT, comment, startLine, startCol);
4963 + },
4964 +
4965 + /**
4966 + * Produces a comparison token based on the given character
4967 + * and location in the stream. The next character must be
4968 + * read and is already known to be an equals sign.
4969 + * @param {String} c The character for the token.
4970 + * @param {int} startLine The beginning line for the character.
4971 + * @param {int} startCol The beginning column for the character.
4972 + * @return {Object} A token object.
4973 + * @method comparisonToken
4974 + */
4975 + comparisonToken: function(c, startLine, startCol) {
4976 + var reader = this._reader,
4977 + comparison = c + reader.read(),
4978 + tt = Tokens.type(comparison) || Tokens.CHAR;
4979 +
4980 + return this.createToken(tt, comparison, startLine, startCol);
4981 + },
4982 +
4983 + /**
4984 + * Produces a hash token based on the specified information. The
4985 + * first character provided is the pound sign (#) and then this
4986 + * method reads a name afterward.
4987 + * @param {String} first The first character (#) in the hash name.
4988 + * @param {int} startLine The beginning line for the character.
4989 + * @param {int} startCol The beginning column for the character.
4990 + * @return {Object} A token object.
4991 + * @method hashToken
4992 + */
4993 + hashToken: function(first, startLine, startCol) {
4994 + var name = this.readName(first);
4995 +
4996 + return this.createToken(Tokens.HASH, name, startLine, startCol);
4997 + },
4998 +
4999 + /**
5000 + * Produces a CDO or CHAR token based on the specified information. The
5001 + * first character is provided and the rest is read by the function to determine
5002 + * the correct token to create.
5003 + * @param {String} first The first character in the token.
5004 + * @param {int} startLine The beginning line for the character.
5005 + * @param {int} startCol The beginning column for the character.
5006 + * @return {Object} A token object.
5007 + * @method htmlCommentStartToken
5008 + */
5009 + htmlCommentStartToken: function(first, startLine, startCol) {
5010 + var reader = this._reader,
5011 + text = first;
5012 +
5013 + reader.mark();
5014 + text += reader.readCount(3);
5015 +
5016 + if (text === "<!--") {
5017 + return this.createToken(Tokens.CDO, text, startLine, startCol);
5018 + } else {
5019 + reader.reset();
5020 + return this.charToken(first, startLine, startCol);
5021 + }
5022 + },
5023 +
5024 + /**
5025 + * Produces a CDC or CHAR token based on the specified information. The
5026 + * first character is provided and the rest is read by the function to determine
5027 + * the correct token to create.
5028 + * @param {String} first The first character in the token.
5029 + * @param {int} startLine The beginning line for the character.
5030 + * @param {int} startCol The beginning column for the character.
5031 + * @return {Object} A token object.
5032 + * @method htmlCommentEndToken
5033 + */
5034 + htmlCommentEndToken: function(first, startLine, startCol) {
5035 + var reader = this._reader,
5036 + text = first;
5037 +
5038 + reader.mark();
5039 + text += reader.readCount(2);
5040 +
5041 + if (text === "-->") {
5042 + return this.createToken(Tokens.CDC, text, startLine, startCol);
5043 + } else {
5044 + reader.reset();
5045 + return this.charToken(first, startLine, startCol);
5046 + }
5047 + },
5048 +
5049 + /**
5050 + * Produces an IDENT or FUNCTION token based on the specified information. The
5051 + * first character is provided and the rest is read by the function to determine
5052 + * the correct token to create.
5053 + * @param {String} first The first character in the identifier.
5054 + * @param {int} startLine The beginning line for the character.
5055 + * @param {int} startCol The beginning column for the character.
5056 + * @return {Object} A token object.
5057 + * @method identOrFunctionToken
5058 + */
5059 + identOrFunctionToken: function(first, startLine, startCol) {
5060 + var reader = this._reader,
5061 + ident = this.readName(first),
5062 + tt = Tokens.IDENT,
5063 + uriFns = ["url(", "url-prefix(", "domain("],
5064 + uri;
5065 +
5066 + //if there's a left paren immediately after, it's a URI or function
5067 + if (reader.peek() === "(") {
5068 + ident += reader.read();
5069 + if (uriFns.indexOf(ident.toLowerCase()) > -1) {
5070 + reader.mark();
5071 + uri = this.readURI(ident);
5072 + if (uri === null) {
5073 + //didn't find a valid URL or there's no closing paren
5074 + reader.reset();
5075 + tt = Tokens.FUNCTION;
5076 + } else {
5077 + tt = Tokens.URI;
5078 + ident = uri;
5079 + }
5080 + } else {
5081 + tt = Tokens.FUNCTION;
5082 + }
5083 + } else if (reader.peek() === ":") { //might be an IE function
5084 +
5085 + //IE-specific functions always being with progid:
5086 + if (ident.toLowerCase() === "progid") {
5087 + ident += reader.readTo("(");
5088 + tt = Tokens.IE_FUNCTION;
5089 + }
5090 + }
5091 +
5092 + return this.createToken(tt, ident, startLine, startCol);
5093 + },
5094 +
5095 + /**
5096 + * Produces an IMPORTANT_SYM or CHAR token based on the specified information. The
5097 + * first character is provided and the rest is read by the function to determine
5098 + * the correct token to create.
5099 + * @param {String} first The first character in the token.
5100 + * @param {int} startLine The beginning line for the character.
5101 + * @param {int} startCol The beginning column for the character.
5102 + * @return {Object} A token object.
5103 + * @method importantToken
5104 + */
5105 + importantToken: function(first, startLine, startCol) {
5106 + var reader = this._reader,
5107 + important = first,
5108 + tt = Tokens.CHAR,
5109 + temp,
5110 + c;
5111 +
5112 + reader.mark();
5113 + c = reader.read();
5114 +
5115 + while (c) {
5116 +
5117 + //there can be a comment in here
5118 + if (c === "/") {
5119 +
5120 + //if the next character isn't a star, then this isn't a valid !important token
5121 + if (reader.peek() !== "*") {
5122 + break;
5123 + } else {
5124 + temp = this.readComment(c);
5125 + if (temp === "") { //broken!
5126 + break;
5127 + }
5128 + }
5129 + } else if (isWhitespace(c)) {
5130 + important += c + this.readWhitespace();
5131 + } else if (/i/i.test(c)) {
5132 + temp = reader.readCount(8);
5133 + if (/mportant/i.test(temp)) {
5134 + important += c + temp;
5135 + tt = Tokens.IMPORTANT_SYM;
5136 +
5137 + }
5138 + break; //we're done
5139 + } else {
5140 + break;
5141 + }
5142 +
5143 + c = reader.read();
5144 + }
5145 +
5146 + if (tt === Tokens.CHAR) {
5147 + reader.reset();
5148 + return this.charToken(first, startLine, startCol);
5149 + } else {
5150 + return this.createToken(tt, important, startLine, startCol);
5151 + }
5152 +
5153 +
5154 + },
5155 +
5156 + /**
5157 + * Produces a NOT or CHAR token based on the specified information. The
5158 + * first character is provided and the rest is read by the function to determine
5159 + * the correct token to create.
5160 + * @param {String} first The first character in the token.
5161 + * @param {int} startLine The beginning line for the character.
5162 + * @param {int} startCol The beginning column for the character.
5163 + * @return {Object} A token object.
5164 + * @method notToken
5165 + */
5166 + notToken: function(first, startLine, startCol) {
5167 + var reader = this._reader,
5168 + text = first;
5169 +
5170 + reader.mark();
5171 + text += reader.readCount(4);
5172 +
5173 + if (text.toLowerCase() === ":not(") {
5174 + return this.createToken(Tokens.NOT, text, startLine, startCol);
5175 + } else {
5176 + reader.reset();
5177 + return this.charToken(first, startLine, startCol);
5178 + }
5179 + },
5180 +
5181 + /**
5182 + * Produces a number token based on the given character
5183 + * and location in the stream. This may return a token of
5184 + * NUMBER, EMS, EXS, LENGTH, ANGLE, TIME, FREQ, DIMENSION,
5185 + * or PERCENTAGE.
5186 + * @param {String} first The first character for the token.
5187 + * @param {int} startLine The beginning line for the character.
5188 + * @param {int} startCol The beginning column for the character.
5189 + * @return {Object} A token object.
5190 + * @method numberToken
5191 + */
5192 + numberToken: function(first, startLine, startCol) {
5193 + var reader = this._reader,
5194 + value = this.readNumber(first),
5195 + ident,
5196 + tt = Tokens.NUMBER,
5197 + c = reader.peek();
5198 +
5199 + if (isIdentStart(c)) {
5200 + ident = this.readName(reader.read());
5201 + value += ident;
5202 +
5203 + if (/^em$|^ex$|^px$|^gd$|^rem$|^vw$|^vh$|^vmax$|^vmin$|^ch$|^cm$|^mm$|^in$|^pt$|^pc$/i.test(ident)) {
5204 + tt = Tokens.LENGTH;
5205 + } else if (/^deg|^rad$|^grad$|^turn$/i.test(ident)) {
5206 + tt = Tokens.ANGLE;
5207 + } else if (/^ms$|^s$/i.test(ident)) {
5208 + tt = Tokens.TIME;
5209 + } else if (/^hz$|^khz$/i.test(ident)) {
5210 + tt = Tokens.FREQ;
5211 + } else if (/^dpi$|^dpcm$/i.test(ident)) {
5212 + tt = Tokens.RESOLUTION;
5213 + } else {
5214 + tt = Tokens.DIMENSION;
5215 + }
5216 +
5217 + } else if (c === "%") {
5218 + value += reader.read();
5219 + tt = Tokens.PERCENTAGE;
5220 + }
5221 +
5222 + return this.createToken(tt, value, startLine, startCol);
5223 + },
5224 +
5225 + /**
5226 + * Produces a string token based on the given character
5227 + * and location in the stream. Since strings may be indicated
5228 + * by single or double quotes, a failure to match starting
5229 + * and ending quotes results in an INVALID token being generated.
5230 + * The first character in the string is passed in and then
5231 + * the rest are read up to and including the final quotation mark.
5232 + * @param {String} first The first character in the string.
5233 + * @param {int} startLine The beginning line for the character.
5234 + * @param {int} startCol The beginning column for the character.
5235 + * @return {Object} A token object.
5236 + * @method stringToken
5237 + */
5238 + stringToken: function(first, startLine, startCol) {
5239 + var delim = first,
5240 + string = first,
5241 + reader = this._reader,
5242 + tt = Tokens.STRING,
5243 + c = reader.read(),
5244 + i;
5245 +
5246 + while (c) {
5247 + string += c;
5248 +
5249 + if (c === "\\") {
5250 + c = reader.read();
5251 + if (c === null) {
5252 + break; // premature EOF after backslash
5253 + } else if (/[^\r\n\f0-9a-f]/i.test(c)) {
5254 + // single-character escape
5255 + string += c;
5256 + } else {
5257 + // read up to six hex digits
5258 + for (i=0; isHexDigit(c) && i<6; i++) {
5259 + string += c;
5260 + c = reader.read();
5261 + }
5262 + // swallow trailing newline or space
5263 + if (c === "\r" && reader.peek() === "\n") {
5264 + string += c;
5265 + c = reader.read();
5266 + }
5267 + if (isWhitespace(c)) {
5268 + string += c;
5269 + } else {
5270 + // This character is null or not part of the escape;
5271 + // jump back to the top to process it.
5272 + continue;
5273 + }
5274 + }
5275 + } else if (c === delim) {
5276 + break; // delimiter found.
5277 + } else if (isNewLine(reader.peek())) {
5278 + // newline without an escapement: it's an invalid string
5279 + tt = Tokens.INVALID;
5280 + break;
5281 + }
5282 + c = reader.read();
5283 + }
5284 +
5285 + //if c is null, that means we're out of input and the string was never closed
5286 + if (c === null) {
5287 + tt = Tokens.INVALID;
5288 + }
5289 +
5290 + return this.createToken(tt, string, startLine, startCol);
5291 + },
5292 +
5293 + unicodeRangeToken: function(first, startLine, startCol) {
5294 + var reader = this._reader,
5295 + value = first,
5296 + temp,
5297 + tt = Tokens.CHAR;
5298 +
5299 + //then it should be a unicode range
5300 + if (reader.peek() === "+") {
5301 + reader.mark();
5302 + value += reader.read();
5303 + value += this.readUnicodeRangePart(true);
5304 +
5305 + //ensure there's an actual unicode range here
5306 + if (value.length === 2) {
5307 + reader.reset();
5308 + } else {
5309 +
5310 + tt = Tokens.UNICODE_RANGE;
5311 +
5312 + //if there's a ? in the first part, there can't be a second part
5313 + if (value.indexOf("?") === -1) {
5314 +
5315 + if (reader.peek() === "-") {
5316 + reader.mark();
5317 + temp = reader.read();
5318 + temp += this.readUnicodeRangePart(false);
5319 +
5320 + //if there's not another value, back up and just take the first
5321 + if (temp.length === 1) {
5322 + reader.reset();
5323 + } else {
5324 + value += temp;
5325 + }
5326 + }
5327 +
5328 + }
5329 + }
5330 + }
5331 +
5332 + return this.createToken(tt, value, startLine, startCol);
5333 + },
5334 +
5335 + /**
5336 + * Produces a S token based on the specified information. Since whitespace
5337 + * may have multiple characters, this consumes all whitespace characters
5338 + * into a single token.
5339 + * @param {String} first The first character in the token.
5340 + * @param {int} startLine The beginning line for the character.
5341 + * @param {int} startCol The beginning column for the character.
5342 + * @return {Object} A token object.
5343 + * @method whitespaceToken
5344 + */
5345 + whitespaceToken: function(first, startLine, startCol) {
5346 + var value = first + this.readWhitespace();
5347 + return this.createToken(Tokens.S, value, startLine, startCol);
5348 + },
5349 +
5350 +
5351 + //-------------------------------------------------------------------------
5352 + // Methods to read values from the string stream
5353 + //-------------------------------------------------------------------------
5354 +
5355 + readUnicodeRangePart: function(allowQuestionMark) {
5356 + var reader = this._reader,
5357 + part = "",
5358 + c = reader.peek();
5359 +
5360 + //first read hex digits
5361 + while (isHexDigit(c) && part.length < 6) {
5362 + reader.read();
5363 + part += c;
5364 + c = reader.peek();
5365 + }
5366 +
5367 + //then read question marks if allowed
5368 + if (allowQuestionMark) {
5369 + while (c === "?" && part.length < 6) {
5370 + reader.read();
5371 + part += c;
5372 + c = reader.peek();
5373 + }
5374 + }
5375 +
5376 + //there can't be any other characters after this point
5377 +
5378 + return part;
5379 + },
5380 +
5381 + readWhitespace: function() {
5382 + var reader = this._reader,
5383 + whitespace = "",
5384 + c = reader.peek();
5385 +
5386 + while (isWhitespace(c)) {
5387 + reader.read();
5388 + whitespace += c;
5389 + c = reader.peek();
5390 + }
5391 +
5392 + return whitespace;
5393 + },
5394 + readNumber: function(first) {
5395 + var reader = this._reader,
5396 + number = first,
5397 + hasDot = (first === "."),
5398 + c = reader.peek();
5399 +
5400 +
5401 + while (c) {
5402 + if (isDigit(c)) {
5403 + number += reader.read();
5404 + } else if (c === ".") {
5405 + if (hasDot) {
5406 + break;
5407 + } else {
5408 + hasDot = true;
5409 + number += reader.read();
5410 + }
5411 + } else {
5412 + break;
5413 + }
5414 +
5415 + c = reader.peek();
5416 + }
5417 +
5418 + return number;
5419 + },
5420 +
5421 + // returns null w/o resetting reader if string is invalid.
5422 + readString: function() {
5423 + var token = this.stringToken(this._reader.read(), 0, 0);
5424 + return token.type === Tokens.INVALID ? null : token.value;
5425 + },
5426 +
5427 + // returns null w/o resetting reader if URI is invalid.
5428 + readURI: function(first) {
5429 + var reader = this._reader,
5430 + uri = first,
5431 + inner = "",
5432 + c = reader.peek();
5433 +
5434 + //skip whitespace before
5435 + while (c && isWhitespace(c)) {
5436 + reader.read();
5437 + c = reader.peek();
5438 + }
5439 +
5440 + //it's a string
5441 + if (c === "'" || c === "\"") {
5442 + inner = this.readString();
5443 + if (inner !== null) {
5444 + inner = PropertyValuePart.parseString(inner);
5445 + }
5446 + } else {
5447 + inner = this.readUnquotedURL();
5448 + }
5449 +
5450 + c = reader.peek();
5451 +
5452 + //skip whitespace after
5453 + while (c && isWhitespace(c)) {
5454 + reader.read();
5455 + c = reader.peek();
5456 + }
5457 +
5458 + //if there was no inner value or the next character isn't closing paren, it's not a URI
5459 + if (inner === null || c !== ")") {
5460 + uri = null;
5461 + } else {
5462 + // Ensure argument to URL is always double-quoted
5463 + // (This simplifies later processing in PropertyValuePart.)
5464 + uri += PropertyValuePart.serializeString(inner) + reader.read();
5465 + }
5466 +
5467 + return uri;
5468 + },
5469 + // This method never fails, although it may return an empty string.
5470 + readUnquotedURL: function(first) {
5471 + var reader = this._reader,
5472 + url = first || "",
5473 + c;
5474 +
5475 + for (c = reader.peek(); c; c = reader.peek()) {
5476 + // Note that the grammar at
5477 + // https://www.w3.org/TR/CSS2/grammar.html#scanner
5478 + // incorrectly includes the backslash character in the
5479 + // `url` production, although it is correctly omitted in
5480 + // the `baduri1` production.
5481 + if (nonascii.test(c) || /^[\-!#$%&*-\[\]-~]$/.test(c)) {
5482 + url += c;
5483 + reader.read();
5484 + } else if (c === "\\") {
5485 + if (/^[^\r\n\f]$/.test(reader.peek(2))) {
5486 + url += this.readEscape(reader.read(), true);
5487 + } else {
5488 + break; // bad escape sequence.
5489 + }
5490 + } else {
5491 + break; // bad character
5492 + }
5493 + }
5494 +
5495 + return url;
5496 + },
5497 +
5498 + readName: function(first) {
5499 + var reader = this._reader,
5500 + ident = first || "",
5501 + c;
5502 +
5503 + for (c = reader.peek(); c; c = reader.peek()) {
5504 + if (c === "\\") {
5505 + if (/^[^\r\n\f]$/.test(reader.peek(2))) {
5506 + ident += this.readEscape(reader.read(), true);
5507 + } else {
5508 + // Bad escape sequence.
5509 + break;
5510 + }
5511 + } else if (isNameChar(c)) {
5512 + ident += reader.read();
5513 + } else {
5514 + break;
5515 + }
5516 + }
5517 +
5518 + return ident;
5519 + },
5520 +
5521 + readEscape: function(first, unescape) {
5522 + var reader = this._reader,
5523 + cssEscape = first || "",
5524 + i = 0,
5525 + c = reader.peek();
5526 +
5527 + if (isHexDigit(c)) {
5528 + do {
5529 + cssEscape += reader.read();
5530 + c = reader.peek();
5531 + } while (c && isHexDigit(c) && ++i < 6);
5532 + }
5533 +
5534 + if (cssEscape.length === 1) {
5535 + if (/^[^\r\n\f0-9a-f]$/.test(c)) {
5536 + reader.read();
5537 + if (unescape) {
5538 + return c;
5539 + }
5540 + } else {
5541 + // We should never get here (readName won't call readEscape
5542 + // if the escape sequence is bad).
5543 + throw new Error("Bad escape sequence.");
5544 + }
5545 + } else if (c === "\r") {
5546 + reader.read();
5547 + if (reader.peek() === "\n") {
5548 + c += reader.read();
5549 + }
5550 + } else if (/^[ \t\n\f]$/.test(c)) {
5551 + reader.read();
5552 + } else {
5553 + c = "";
5554 + }
5555 +
5556 + if (unescape) {
5557 + var cp = parseInt(cssEscape.slice(first.length), 16);
5558 + return String.fromCodePoint ? String.fromCodePoint(cp) :
5559 + String.fromCharCode(cp);
5560 + }
5561 + return cssEscape + c;
5562 + },
5563 +
5564 + readComment: function(first) {
5565 + var reader = this._reader,
5566 + comment = first || "",
5567 + c = reader.read();
5568 +
5569 + if (c === "*") {
5570 + while (c) {
5571 + comment += c;
5572 +
5573 + //look for end of comment
5574 + if (comment.length > 2 && c === "*" && reader.peek() === "/") {
5575 + comment += reader.read();
5576 + break;
5577 + }
5578 +
5579 + c = reader.read();
5580 + }
5581 +
5582 + return comment;
5583 + } else {
5584 + return "";
5585 + }
5586 +
5587 + }
5588 + });
5589 +
5590 + },{"../util/TokenStreamBase":27,"./PropertyValuePart":11,"./Tokens":18}],18:[function(require,module,exports){
5591 + "use strict";
5592 +
5593 + var Tokens = module.exports = [
5594 +
5595 + /*
5596 + * The following token names are defined in CSS3 Grammar: https://www.w3.org/TR/css3-syntax/#lexical
5597 + */
5598 +
5599 + // HTML-style comments
5600 + { name: "CDO" },
5601 + { name: "CDC" },
5602 +
5603 + // ignorables
5604 + { name: "S", whitespace: true/*, channel: "ws"*/ },
5605 + { name: "COMMENT", comment: true, hide: true, channel: "comment" },
5606 +
5607 + // attribute equality
5608 + { name: "INCLUDES", text: "~=" },
5609 + { name: "DASHMATCH", text: "|=" },
5610 + { name: "PREFIXMATCH", text: "^=" },
5611 + { name: "SUFFIXMATCH", text: "$=" },
5612 + { name: "SUBSTRINGMATCH", text: "*=" },
5613 +
5614 + // identifier types
5615 + { name: "STRING" },
5616 + { name: "IDENT" },
5617 + { name: "HASH" },
5618 +
5619 + // at-keywords
5620 + { name: "IMPORT_SYM", text: "@import" },
5621 + { name: "PAGE_SYM", text: "@page" },
5622 + { name: "MEDIA_SYM", text: "@media" },
5623 + { name: "FONT_FACE_SYM", text: "@font-face" },
5624 + { name: "CHARSET_SYM", text: "@charset" },
5625 + { name: "NAMESPACE_SYM", text: "@namespace" },
5626 + { name: "SUPPORTS_SYM", text: "@supports" },
5627 + { name: "VIEWPORT_SYM", text: ["@viewport", "@-ms-viewport", "@-o-viewport"] },
5628 + { name: "DOCUMENT_SYM", text: ["@document", "@-moz-document"] },
5629 + { name: "UNKNOWN_SYM" },
5630 + //{ name: "ATKEYWORD"},
5631 +
5632 + // CSS3 animations
5633 + { name: "KEYFRAMES_SYM", text: [ "@keyframes", "@-webkit-keyframes", "@-moz-keyframes", "@-o-keyframes" ] },
5634 +
5635 + // important symbol
5636 + { name: "IMPORTANT_SYM" },
5637 +
5638 + // measurements
5639 + { name: "LENGTH" },
5640 + { name: "ANGLE" },
5641 + { name: "TIME" },
5642 + { name: "FREQ" },
5643 + { name: "DIMENSION" },
5644 + { name: "PERCENTAGE" },
5645 + { name: "NUMBER" },
5646 +
5647 + // functions
5648 + { name: "URI" },
5649 + { name: "FUNCTION" },
5650 +
5651 + // Unicode ranges
5652 + { name: "UNICODE_RANGE" },
5653 +
5654 + /*
5655 + * The following token names are defined in CSS3 Selectors: https://www.w3.org/TR/css3-selectors/#selector-syntax
5656 + */
5657 +
5658 + // invalid string
5659 + { name: "INVALID" },
5660 +
5661 + // combinators
5662 + { name: "PLUS", text: "+" },
5663 + { name: "GREATER", text: ">" },
5664 + { name: "COMMA", text: "," },
5665 + { name: "TILDE", text: "~" },
5666 +
5667 + // modifier
5668 + { name: "NOT" },
5669 +
5670 + /*
5671 + * Defined in CSS3 Paged Media
5672 + */
5673 + { name: "TOPLEFTCORNER_SYM", text: "@top-left-corner" },
5674 + { name: "TOPLEFT_SYM", text: "@top-left" },
5675 + { name: "TOPCENTER_SYM", text: "@top-center" },
5676 + { name: "TOPRIGHT_SYM", text: "@top-right" },
5677 + { name: "TOPRIGHTCORNER_SYM", text: "@top-right-corner" },
5678 + { name: "BOTTOMLEFTCORNER_SYM", text: "@bottom-left-corner" },
5679 + { name: "BOTTOMLEFT_SYM", text: "@bottom-left" },
5680 + { name: "BOTTOMCENTER_SYM", text: "@bottom-center" },
5681 + { name: "BOTTOMRIGHT_SYM", text: "@bottom-right" },
5682 + { name: "BOTTOMRIGHTCORNER_SYM", text: "@bottom-right-corner" },
5683 + { name: "LEFTTOP_SYM", text: "@left-top" },
5684 + { name: "LEFTMIDDLE_SYM", text: "@left-middle" },
5685 + { name: "LEFTBOTTOM_SYM", text: "@left-bottom" },
5686 + { name: "RIGHTTOP_SYM", text: "@right-top" },
5687 + { name: "RIGHTMIDDLE_SYM", text: "@right-middle" },
5688 + { name: "RIGHTBOTTOM_SYM", text: "@right-bottom" },
5689 +
5690 + /*
5691 + * The following token names are defined in CSS3 Media Queries: https://www.w3.org/TR/css3-mediaqueries/#syntax
5692 + */
5693 + /*{ name: "MEDIA_ONLY", state: "media"},
5694 + { name: "MEDIA_NOT", state: "media"},
5695 + { name: "MEDIA_AND", state: "media"},*/
5696 + { name: "RESOLUTION", state: "media" },
5697 +
5698 + /*
5699 + * The following token names are not defined in any CSS specification but are used by the lexer.
5700 + */
5701 +
5702 + // not a real token, but useful for stupid IE filters
5703 + { name: "IE_FUNCTION" },
5704 +
5705 + // part of CSS3 grammar but not the Flex code
5706 + { name: "CHAR" },
5707 +
5708 + // TODO: Needed?
5709 + // Not defined as tokens, but might as well be
5710 + {
5711 + name: "PIPE",
5712 + text: "|"
5713 + },
5714 + {
5715 + name: "SLASH",
5716 + text: "/"
5717 + },
5718 + {
5719 + name: "MINUS",
5720 + text: "-"
5721 + },
5722 + {
5723 + name: "STAR",
5724 + text: "*"
5725 + },
5726 +
5727 + {
5728 + name: "LBRACE",
5729 + endChar: "}",
5730 + text: "{"
5731 + },
5732 + {
5733 + name: "RBRACE",
5734 + text: "}"
5735 + },
5736 + {
5737 + name: "LBRACKET",
5738 + endChar: "]",
5739 + text: "["
5740 + },
5741 + {
5742 + name: "RBRACKET",
5743 + text: "]"
5744 + },
5745 + {
5746 + name: "EQUALS",
5747 + text: "="
5748 + },
5749 + {
5750 + name: "COLON",
5751 + text: ":"
5752 + },
5753 + {
5754 + name: "SEMICOLON",
5755 + text: ";"
5756 + },
5757 + {
5758 + name: "LPAREN",
5759 + endChar: ")",
5760 + text: "("
5761 + },
5762 + {
5763 + name: "RPAREN",
5764 + text: ")"
5765 + },
5766 + {
5767 + name: "DOT",
5768 + text: "."
5769 + }
5770 + ];
5771 +
5772 + (function() {
5773 + var nameMap = [],
5774 + typeMap = Object.create(null);
5775 +
5776 + Tokens.UNKNOWN = -1;
5777 + Tokens.unshift({ name:"EOF" });
5778 + for (var i=0, len = Tokens.length; i < len; i++) {
5779 + nameMap.push(Tokens[i].name);
5780 + Tokens[Tokens[i].name] = i;
5781 + if (Tokens[i].text) {
5782 + if (Tokens[i].text instanceof Array) {
5783 + for (var j=0; j < Tokens[i].text.length; j++) {
5784 + typeMap[Tokens[i].text[j]] = i;
5785 + }
5786 + } else {
5787 + typeMap[Tokens[i].text] = i;
5788 + }
5789 + }
5790 + }
5791 +
5792 + Tokens.name = function(tt) {
5793 + return nameMap[tt];
5794 + };
5795 +
5796 + Tokens.type = function(c) {
5797 + return typeMap[c] || -1;
5798 + };
5799 + })();
5800 +
5801 + },{}],19:[function(require,module,exports){
5802 + "use strict";
5803 +
5804 + /* exported Validation */
5805 +
5806 + var Matcher = require("./Matcher");
5807 + var Properties = require("./Properties");
5808 + var ValidationTypes = require("./ValidationTypes");
5809 + var ValidationError = require("./ValidationError");
5810 + var PropertyValueIterator = require("./PropertyValueIterator");
5811 +
5812 + var Validation = module.exports = {
5813 +
5814 + validate: function(property, value) {
5815 +
5816 + //normalize name
5817 + var name = property.toString().toLowerCase(),
5818 + expression = new PropertyValueIterator(value),
5819 + spec = Properties[name],
5820 + part;
5821 +
5822 + if (!spec) {
5823 + if (name.indexOf("-") !== 0) { //vendor prefixed are ok
5824 + throw new ValidationError("Unknown property '" + property + "'.", property.line, property.col);
5825 + }
5826 + } else if (typeof spec !== "number") {
5827 +
5828 + // All properties accept some CSS-wide values.
5829 + // https://drafts.csswg.org/css-values-3/#common-keywords
5830 + if (ValidationTypes.isAny(expression, "inherit | initial | unset")) {
5831 + if (expression.hasNext()) {
5832 + part = expression.next();
5833 + throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5834 + }
5835 + return;
5836 + }
5837 +
5838 + // Property-specific validation.
5839 + this.singleProperty(spec, expression);
5840 +
5841 + }
5842 +
5843 + },
5844 +
5845 + singleProperty: function(types, expression) {
5846 +
5847 + var result = false,
5848 + value = expression.value,
5849 + part;
5850 +
5851 + result = Matcher.parse(types).match(expression);
5852 +
5853 + if (!result) {
5854 + if (expression.hasNext() && !expression.isFirst()) {
5855 + part = expression.peek();
5856 + throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5857 + } else {
5858 + throw new ValidationError("Expected (" + ValidationTypes.describe(types) + ") but found '" + value + "'.", value.line, value.col);
5859 + }
5860 + } else if (expression.hasNext()) {
5861 + part = expression.next();
5862 + throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5863 + }
5864 +
5865 + }
5866 +
5867 + };
5868 +
5869 + },{"./Matcher":3,"./Properties":7,"./PropertyValueIterator":10,"./ValidationError":20,"./ValidationTypes":21}],20:[function(require,module,exports){
5870 + "use strict";
5871 +
5872 + module.exports = ValidationError;
5873 +
5874 + /**
5875 + * Type to use when a validation error occurs.
5876 + * @class ValidationError
5877 + * @namespace parserlib.util
5878 + * @constructor
5879 + * @param {String} message The error message.
5880 + * @param {int} line The line at which the error occurred.
5881 + * @param {int} col The column at which the error occurred.
5882 + */
5883 + function ValidationError(message, line, col) {
5884 +
5885 + /**
5886 + * The column at which the error occurred.
5887 + * @type int
5888 + * @property col
5889 + */
5890 + this.col = col;
5891 +
5892 + /**
5893 + * The line at which the error occurred.
5894 + * @type int
5895 + * @property line
5896 + */
5897 + this.line = line;
5898 +
5899 + /**
5900 + * The text representation of the unit.
5901 + * @type String
5902 + * @property text
5903 + */
5904 + this.message = message;
5905 +
5906 + }
5907 +
5908 + //inherit from Error
5909 + ValidationError.prototype = new Error();
5910 +
5911 + },{}],21:[function(require,module,exports){
5912 + "use strict";
5913 +
5914 + var ValidationTypes = module.exports;
5915 +
5916 + var Matcher = require("./Matcher");
5917 +
5918 + function copy(to, from) {
5919 + Object.keys(from).forEach(function(prop) {
5920 + to[prop] = from[prop];
5921 + });
5922 + }
5923 + copy(ValidationTypes, {
5924 +
5925 + isLiteral: function (part, literals) {
5926 + var text = part.text.toString().toLowerCase(),
5927 + args = literals.split(" | "),
5928 + i, len, found = false;
5929 +
5930 + for (i=0, len=args.length; i < len && !found; i++) {
5931 + if (args[i].charAt(0) === "<") {
5932 + found = this.simple[args[i]](part);
5933 + } else if (args[i].slice(-2) === "()") {
5934 + found = (part.type === "function" &&
5935 + part.name === args[i].slice(0, -2));
5936 + } else if (text === args[i].toLowerCase()) {
5937 + found = true;
5938 + }
5939 + }
5940 +
5941 + return found;
5942 + },
5943 +
5944 + isSimple: function(type) {
5945 + return Boolean(this.simple[type]);
5946 + },
5947 +
5948 + isComplex: function(type) {
5949 + return Boolean(this.complex[type]);
5950 + },
5951 +
5952 + describe: function(type) {
5953 + if (this.complex[type] instanceof Matcher) {
5954 + return this.complex[type].toString(0);
5955 + }
5956 + return type;
5957 + },
5958 +
5959 + /**
5960 + * Determines if the next part(s) of the given expression
5961 + * are any of the given types.
5962 + */
5963 + isAny: function (expression, types) {
5964 + var args = types.split(" | "),
5965 + i, len, found = false;
5966 +
5967 + for (i=0, len=args.length; i < len && !found && expression.hasNext(); i++) {
5968 + found = this.isType(expression, args[i]);
5969 + }
5970 +
5971 + return found;
5972 + },
5973 +
5974 + /**
5975 + * Determines if the next part(s) of the given expression
5976 + * are one of a group.
5977 + */
5978 + isAnyOfGroup: function(expression, types) {
5979 + var args = types.split(" || "),
5980 + i, len, found = false;
5981 +
5982 + for (i=0, len=args.length; i < len && !found; i++) {
5983 + found = this.isType(expression, args[i]);
5984 + }
5985 +
5986 + return found ? args[i-1] : false;
5987 + },
5988 +
5989 + /**
5990 + * Determines if the next part(s) of the given expression
5991 + * are of a given type.
5992 + */
5993 + isType: function (expression, type) {
5994 + var part = expression.peek(),
5995 + result = false;
5996 +
5997 + if (type.charAt(0) !== "<") {
5998 + result = this.isLiteral(part, type);
5999 + if (result) {
6000 + expression.next();
6001 + }
6002 + } else if (this.simple[type]) {
6003 + result = this.simple[type](part);
6004 + if (result) {
6005 + expression.next();
6006 + }
6007 + } else if (this.complex[type] instanceof Matcher) {
6008 + result = this.complex[type].match(expression);
6009 + } else {
6010 + result = this.complex[type](expression);
6011 + }
6012 +
6013 + return result;
6014 + },
6015 +
6016 +
6017 + simple: {
6018 + __proto__: null,
6019 +
6020 + "<absolute-size>":
6021 + "xx-small | x-small | small | medium | large | x-large | xx-large",
6022 +
6023 + "<animateable-feature>":
6024 + "scroll-position | contents | <animateable-feature-name>",
6025 +
6026 + "<animateable-feature-name>": function(part) {
6027 + return this["<ident>"](part) &&
6028 + !/^(unset|initial|inherit|will-change|auto|scroll-position|contents)$/i.test(part);
6029 + },
6030 +
6031 + "<angle>": function(part) {
6032 + return part.type === "angle";
6033 + },
6034 +
6035 + "<attachment>": "scroll | fixed | local",
6036 +
6037 + "<attr>": "attr()",
6038 +
6039 + // inset() = inset( <shape-arg>{1,4} [round <border-radius>]? )
6040 + // circle() = circle( [<shape-radius>]? [at <position>]? )
6041 + // ellipse() = ellipse( [<shape-radius>{2}]? [at <position>]? )
6042 + // polygon() = polygon( [<fill-rule>,]? [<shape-arg> <shape-arg>]# )
6043 + "<basic-shape>": "inset() | circle() | ellipse() | polygon()",
6044 +
6045 + "<bg-image>": "<image> | <gradient> | none",
6046 +
6047 + "<border-style>":
6048 + "none | hidden | dotted | dashed | solid | double | groove | " +
6049 + "ridge | inset | outset",
6050 +
6051 + "<border-width>": "<length> | thin | medium | thick",
6052 +
6053 + "<box>": "padding-box | border-box | content-box",
6054 +
6055 + "<clip-source>": "<uri>",
6056 +
6057 + "<color>": function(part) {
6058 + return part.type === "color" || String(part) === "transparent" || String(part) === "currentColor";
6059 + },
6060 +
6061 + // The SVG <color> spec doesn't include "currentColor" or "transparent" as a color.
6062 + "<color-svg>": function(part) {
6063 + return part.type === "color";
6064 + },
6065 +
6066 + "<content>": "content()",
6067 +
6068 + // https://www.w3.org/TR/css3-sizing/#width-height-keywords
6069 + "<content-sizing>":
6070 + "fill-available | -moz-available | -webkit-fill-available | " +
6071 + "max-content | -moz-max-content | -webkit-max-content | " +
6072 + "min-content | -moz-min-content | -webkit-min-content | " +
6073 + "fit-content | -moz-fit-content | -webkit-fit-content",
6074 +
6075 + "<feature-tag-value>": function(part) {
6076 + return part.type === "function" && /^[A-Z0-9]{4}$/i.test(part);
6077 + },
6078 +
6079 + // custom() isn't actually in the spec
6080 + "<filter-function>":
6081 + "blur() | brightness() | contrast() | custom() | " +
6082 + "drop-shadow() | grayscale() | hue-rotate() | invert() | " +
6083 + "opacity() | saturate() | sepia()",
6084 +
6085 + "<flex-basis>": "<width>",
6086 +
6087 + "<flex-direction>": "row | row-reverse | column | column-reverse",
6088 +
6089 + "<flex-grow>": "<number>",
6090 +
6091 + "<flex-shrink>": "<number>",
6092 +
6093 + "<flex-wrap>": "nowrap | wrap | wrap-reverse",
6094 +
6095 + "<font-size>":
6096 + "<absolute-size> | <relative-size> | <length> | <percentage>",
6097 +
6098 + "<font-stretch>":
6099 + "normal | ultra-condensed | extra-condensed | condensed | " +
6100 + "semi-condensed | semi-expanded | expanded | extra-expanded | " +
6101 + "ultra-expanded",
6102 +
6103 + "<font-style>": "normal | italic | oblique",
6104 +
6105 + "<font-variant-caps>":
6106 + "small-caps | all-small-caps | petite-caps | all-petite-caps | " +
6107 + "unicase | titling-caps",
6108 +
6109 + "<font-variant-css21>": "normal | small-caps",
6110 +
6111 + "<font-weight>":
6112 + "normal | bold | bolder | lighter | " +
6113 + "100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900",
6114 +
6115 + "<generic-family>":
6116 + "serif | sans-serif | cursive | fantasy | monospace",
6117 +
6118 + "<geometry-box>": "<shape-box> | fill-box | stroke-box | view-box",
6119 +
6120 + "<glyph-angle>": function(part) {
6121 + return part.type === "angle" && part.units === "deg";
6122 + },
6123 +
6124 + "<gradient>": function(part) {
6125 + return part.type === "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?(?:repeating\-)?(?:radial\-|linear\-)?gradient/i.test(part);
6126 + },
6127 +
6128 + "<icccolor>":
6129 + "cielab() | cielch() | cielchab() | " +
6130 + "icc-color() | icc-named-color()",
6131 +
6132 + //any identifier
6133 + "<ident>": function(part) {
6134 + return part.type === "identifier" || part.wasIdent;
6135 + },
6136 +
6137 + "<ident-not-generic-family>": function(part) {
6138 + return this["<ident>"](part) && !this["<generic-family>"](part);
6139 + },
6140 +
6141 + "<image>": "<uri>",
6142 +
6143 + "<integer>": function(part) {
6144 + return part.type === "integer";
6145 + },
6146 +
6147 + "<length>": function(part) {
6148 + if (part.type === "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?calc/i.test(part)) {
6149 + return true;
6150 + } else {
6151 + return part.type === "length" || part.type === "number" || part.type === "integer" || String(part) === "0";
6152 + }
6153 + },
6154 +
6155 + "<line>": function(part) {
6156 + return part.type === "integer";
6157 + },
6158 +
6159 + "<line-height>": "<number> | <length> | <percentage> | normal",
6160 +
6161 + "<margin-width>": "<length> | <percentage> | auto",
6162 +
6163 + "<miterlimit>": function(part) {
6164 + return this["<number>"](part) && part.value >= 1;
6165 + },
6166 +
6167 + "<nonnegative-length-or-percentage>": function(part) {
6168 + return (this["<length>"](part) || this["<percentage>"](part)) &&
6169 + (String(part) === "0" || part.type === "function" || (part.value) >= 0);
6170 + },
6171 +
6172 + "<nonnegative-number-or-percentage>": function(part) {
6173 + return (this["<number>"](part) || this["<percentage>"](part)) &&
6174 + (String(part) === "0" || part.type === "function" || (part.value) >= 0);
6175 + },
6176 +
6177 + "<number>": function(part) {
6178 + return part.type === "number" || this["<integer>"](part);
6179 + },
6180 +
6181 + "<opacity-value>": function(part) {
6182 + return this["<number>"](part) && part.value >= 0 && part.value <= 1;
6183 + },
6184 +
6185 + "<padding-width>": "<nonnegative-length-or-percentage>",
6186 +
6187 + "<percentage>": function(part) {
6188 + return part.type === "percentage" || String(part) === "0";
6189 + },
6190 +
6191 + "<relative-size>": "smaller | larger",
6192 +
6193 + "<shape>": "rect() | inset-rect()",
6194 +
6195 + "<shape-box>": "<box> | margin-box",
6196 +
6197 + "<single-animation-direction>":
6198 + "normal | reverse | alternate | alternate-reverse",
6199 +
6200 + "<single-animation-name>": function(part) {
6201 + return this["<ident>"](part) &&
6202 + /^-?[a-z_][-a-z0-9_]+$/i.test(part) &&
6203 + !/^(none|unset|initial|inherit)$/i.test(part);
6204 + },
6205 +
6206 + "<string>": function(part) {
6207 + return part.type === "string";
6208 + },
6209 +
6210 + "<time>": function(part) {
6211 + return part.type === "time";
6212 + },
6213 +
6214 + "<uri>": function(part) {
6215 + return part.type === "uri";
6216 + },
6217 +
6218 + "<width>": "<margin-width>"
6219 + },
6220 +
6221 + complex: {
6222 + __proto__: null,
6223 +
6224 + "<azimuth>":
6225 + "<angle>" +
6226 + " | " +
6227 + "[ [ left-side | far-left | left | center-left | center | " +
6228 + "center-right | right | far-right | right-side ] || behind ]" +
6229 + " | "+
6230 + "leftwards | rightwards",
6231 +
6232 + "<bg-position>": "<position>#",
6233 +
6234 + "<bg-size>":
6235 + "[ <length> | <percentage> | auto ]{1,2} | cover | contain",
6236 +
6237 + "<border-image-slice>":
6238 + // [<number> | <percentage>]{1,4} && fill?
6239 + // *but* fill can appear between any of the numbers
6240 + Matcher.many([true /* first element is required */],
6241 + Matcher.cast("<nonnegative-number-or-percentage>"),
6242 + Matcher.cast("<nonnegative-number-or-percentage>"),
6243 + Matcher.cast("<nonnegative-number-or-percentage>"),
6244 + Matcher.cast("<nonnegative-number-or-percentage>"),
6245 + "fill"),
6246 +
6247 + "<border-radius>":
6248 + "<nonnegative-length-or-percentage>{1,4} " +
6249 + "[ / <nonnegative-length-or-percentage>{1,4} ]?",
6250 +
6251 + "<box-shadow>": "none | <shadow>#",
6252 +
6253 + "<clip-path>": "<basic-shape> || <geometry-box>",
6254 +
6255 + "<dasharray>":
6256 + // "list of comma and/or white space separated <length>s and
6257 + // <percentage>s". There is a non-negative constraint.
6258 + Matcher.cast("<nonnegative-length-or-percentage>")
6259 + .braces(1, Infinity, "#", Matcher.cast(",").question()),
6260 +
6261 + "<family-name>":
6262 + // <string> | <IDENT>+
6263 + "<string> | <ident-not-generic-family> <ident>*",
6264 +
6265 + "<filter-function-list>": "[ <filter-function> | <uri> ]+",
6266 +
6267 + // https://www.w3.org/TR/2014/WD-css-flexbox-1-20140325/#flex-property
6268 + "<flex>":
6269 + "none | [ <flex-grow> <flex-shrink>? || <flex-basis> ]",
6270 +
6271 + "<font-family>": "[ <generic-family> | <family-name> ]#",
6272 +
6273 + "<font-shorthand>":
6274 + "[ <font-style> || <font-variant-css21> || " +
6275 + "<font-weight> || <font-stretch> ]? <font-size> " +
6276 + "[ / <line-height> ]? <font-family>",
6277 +
6278 + "<font-variant-alternates>":
6279 + // stylistic(<feature-value-name>)
6280 + "stylistic() || " +
6281 + "historical-forms || " +
6282 + // styleset(<feature-value-name> #)
6283 + "styleset() || " +
6284 + // character-variant(<feature-value-name> #)
6285 + "character-variant() || " +
6286 + // swash(<feature-value-name>)
6287 + "swash() || " +
6288 + // ornaments(<feature-value-name>)
6289 + "ornaments() || " +
6290 + // annotation(<feature-value-name>)
6291 + "annotation()",
6292 +
6293 + "<font-variant-ligatures>":
6294 + // <common-lig-values>
6295 + "[ common-ligatures | no-common-ligatures ] || " +
6296 + // <discretionary-lig-values>
6297 + "[ discretionary-ligatures | no-discretionary-ligatures ] || " +
6298 + // <historical-lig-values>
6299 + "[ historical-ligatures | no-historical-ligatures ] || " +
6300 + // <contextual-alt-values>
6301 + "[ contextual | no-contextual ]",
6302 +
6303 + "<font-variant-numeric>":
6304 + // <numeric-figure-values>
6305 + "[ lining-nums | oldstyle-nums ] || " +
6306 + // <numeric-spacing-values>
6307 + "[ proportional-nums | tabular-nums ] || " +
6308 + // <numeric-fraction-values>
6309 + "[ diagonal-fractions | stacked-fractions ] || " +
6310 + "ordinal || slashed-zero",
6311 +
6312 + "<font-variant-east-asian>":
6313 + // <east-asian-variant-values>
6314 + "[ jis78 | jis83 | jis90 | jis04 | simplified | traditional ] || " +
6315 + // <east-asian-width-values>
6316 + "[ full-width | proportional-width ] || " +
6317 + "ruby",
6318 +
6319 + // Note that <color> here is "as defined in the SVG spec", which
6320 + // is more restrictive that the <color> defined in the CSS spec.
6321 + // none | currentColor | <color> [<icccolor>]? |
6322 + // <funciri> [ none | currentColor | <color> [<icccolor>]? ]?
6323 + "<paint>": "<paint-basic> | <uri> <paint-basic>?",
6324 +
6325 + // Helper definition for <paint> above.
6326 + "<paint-basic>": "none | currentColor | <color-svg> <icccolor>?",
6327 +
6328 + "<position>":
6329 + // Because our `alt` combinator is ordered, we need to test these
6330 + // in order from longest possible match to shortest.
6331 + "[ center | [ left | right ] [ <percentage> | <length> ]? ] && " +
6332 + "[ center | [ top | bottom ] [ <percentage> | <length> ]? ]" +
6333 + " | " +
6334 + "[ left | center | right | <percentage> | <length> ] " +
6335 + "[ top | center | bottom | <percentage> | <length> ]" +
6336 + " | " +
6337 + "[ left | center | right | top | bottom | <percentage> | <length> ]",
6338 +
6339 + "<repeat-style>":
6340 + "repeat-x | repeat-y | [ repeat | space | round | no-repeat ]{1,2}",
6341 +
6342 + "<shadow>":
6343 + //inset? && [ <length>{2,4} && <color>? ]
6344 + Matcher.many([true /* length is required */],
6345 + Matcher.cast("<length>").braces(2, 4), "inset", "<color>"),
6346 +
6347 + "<text-decoration-color>":
6348 + "<color>",
6349 +
6350 + "<text-decoration-line>":
6351 + "none | [ underline || overline || line-through || blink ]",
6352 +
6353 + "<text-decoration-style>":
6354 + "solid | double | dotted | dashed | wavy",
6355 +
6356 + "<will-change>":
6357 + "auto | <animateable-feature>#",
6358 +
6359 + "<x-one-radius>":
6360 + //[ <length> | <percentage> ] [ <length> | <percentage> ]?
6361 + "[ <length> | <percentage> ]{1,2}"
6362 + }
6363 + });
6364 +
6365 + Object.keys(ValidationTypes.simple).forEach(function(nt) {
6366 + var rule = ValidationTypes.simple[nt];
6367 + if (typeof rule === "string") {
6368 + ValidationTypes.simple[nt] = function(part) {
6369 + return ValidationTypes.isLiteral(part, rule);
6370 + };
6371 + }
6372 + });
6373 +
6374 + Object.keys(ValidationTypes.complex).forEach(function(nt) {
6375 + var rule = ValidationTypes.complex[nt];
6376 + if (typeof rule === "string") {
6377 + ValidationTypes.complex[nt] = Matcher.parse(rule);
6378 + }
6379 + });
6380 +
6381 + // Because this is defined relative to other complex validation types,
6382 + // we need to define it *after* the rest of the types are initialized.
6383 + ValidationTypes.complex["<font-variant>"] =
6384 + Matcher.oror({ expand: "<font-variant-ligatures>" },
6385 + { expand: "<font-variant-alternates>" },
6386 + "<font-variant-caps>",
6387 + { expand: "<font-variant-numeric>" },
6388 + { expand: "<font-variant-east-asian>" });
6389 +
6390 + },{"./Matcher":3}],22:[function(require,module,exports){
6391 + "use strict";
6392 +
6393 + module.exports = {
6394 + Colors : require("./Colors"),
6395 + Combinator : require("./Combinator"),
6396 + Parser : require("./Parser"),
6397 + PropertyName : require("./PropertyName"),
6398 + PropertyValue : require("./PropertyValue"),
6399 + PropertyValuePart : require("./PropertyValuePart"),
6400 + Matcher : require("./Matcher"),
6401 + MediaFeature : require("./MediaFeature"),
6402 + MediaQuery : require("./MediaQuery"),
6403 + Selector : require("./Selector"),
6404 + SelectorPart : require("./SelectorPart"),
6405 + SelectorSubPart : require("./SelectorSubPart"),
6406 + Specificity : require("./Specificity"),
6407 + TokenStream : require("./TokenStream"),
6408 + Tokens : require("./Tokens"),
6409 + ValidationError : require("./ValidationError")
6410 + };
6411 +
6412 + },{"./Colors":1,"./Combinator":2,"./Matcher":3,"./MediaFeature":4,"./MediaQuery":5,"./Parser":6,"./PropertyName":8,"./PropertyValue":9,"./PropertyValuePart":11,"./Selector":13,"./SelectorPart":14,"./SelectorSubPart":15,"./Specificity":16,"./TokenStream":17,"./Tokens":18,"./ValidationError":20}],23:[function(require,module,exports){
6413 + "use strict";
6414 +
6415 + module.exports = EventTarget;
6416 +
6417 + /**
6418 + * A generic base to inherit from for any object
6419 + * that needs event handling.
6420 + * @class EventTarget
6421 + * @constructor
6422 + */
6423 + function EventTarget() {
6424 +
6425 + /**
6426 + * The array of listeners for various events.
6427 + * @type Object
6428 + * @property _listeners
6429 + * @private
6430 + */
6431 + this._listeners = Object.create(null);
6432 + }
6433 +
6434 + EventTarget.prototype = {
6435 +
6436 + //restore constructor
6437 + constructor: EventTarget,
6438 +
6439 + /**
6440 + * Adds a listener for a given event type.
6441 + * @param {String} type The type of event to add a listener for.
6442 + * @param {Function} listener The function to call when the event occurs.
6443 + * @return {void}
6444 + * @method addListener
6445 + */
6446 + addListener: function(type, listener) {
6447 + if (!this._listeners[type]) {
6448 + this._listeners[type] = [];
6449 + }
6450 +
6451 + this._listeners[type].push(listener);
6452 + },
6453 +
6454 + /**
6455 + * Fires an event based on the passed-in object.
6456 + * @param {Object|String} event An object with at least a 'type' attribute
6457 + * or a string indicating the event name.
6458 + * @return {void}
6459 + * @method fire
6460 + */
6461 + fire: function(event) {
6462 + if (typeof event === "string") {
6463 + event = { type: event };
6464 + }
6465 + if (typeof event.target !== "undefined") {
6466 + event.target = this;
6467 + }
6468 +
6469 + if (typeof event.type === "undefined") {
6470 + throw new Error("Event object missing 'type' property.");
6471 + }
6472 +
6473 + if (this._listeners[event.type]) {
6474 +
6475 + //create a copy of the array and use that so listeners can't chane
6476 + var listeners = this._listeners[event.type].concat();
6477 + for (var i=0, len=listeners.length; i < len; i++) {
6478 + listeners[i].call(this, event);
6479 + }
6480 + }
6481 + },
6482 +
6483 + /**
6484 + * Removes a listener for a given event type.
6485 + * @param {String} type The type of event to remove a listener from.
6486 + * @param {Function} listener The function to remove from the event.
6487 + * @return {void}
6488 + * @method removeListener
6489 + */
6490 + removeListener: function(type, listener) {
6491 + if (this._listeners[type]) {
6492 + var listeners = this._listeners[type];
6493 + for (var i=0, len=listeners.length; i < len; i++) {
6494 + if (listeners[i] === listener) {
6495 + listeners.splice(i, 1);
6496 + break;
6497 + }
6498 + }
6499 +
6500 +
6501 + }
6502 + }
6503 + };
6504 +
6505 + },{}],24:[function(require,module,exports){
6506 + "use strict";
6507 +
6508 + module.exports = StringReader;
6509 +
6510 + /**
6511 + * Convenient way to read through strings.
6512 + * @namespace parserlib.util
6513 + * @class StringReader
6514 + * @constructor
6515 + * @param {String} text The text to read.
6516 + */
6517 + function StringReader(text) {
6518 +
6519 + /**
6520 + * The input text with line endings normalized.
6521 + * @property _input
6522 + * @type String
6523 + * @private
6524 + */
6525 + this._input = text.replace(/(\r\n?|\n)/g, "\n");
6526 +
6527 +
6528 + /**
6529 + * The row for the character to be read next.
6530 + * @property _line
6531 + * @type int
6532 + * @private
6533 + */
6534 + this._line = 1;
6535 +
6536 +
6537 + /**
6538 + * The column for the character to be read next.
6539 + * @property _col
6540 + * @type int
6541 + * @private
6542 + */
6543 + this._col = 1;
6544 +
6545 + /**
6546 + * The index of the character in the input to be read next.
6547 + * @property _cursor
6548 + * @type int
6549 + * @private
6550 + */
6551 + this._cursor = 0;
6552 + }
6553 +
6554 + StringReader.prototype = {
6555 +
6556 + // restore constructor
6557 + constructor: StringReader,
6558 +
6559 + //-------------------------------------------------------------------------
6560 + // Position info
6561 + //-------------------------------------------------------------------------
6562 +
6563 + /**
6564 + * Returns the column of the character to be read next.
6565 + * @return {int} The column of the character to be read next.
6566 + * @method getCol
6567 + */
6568 + getCol: function() {
6569 + return this._col;
6570 + },
6571 +
6572 + /**
6573 + * Returns the row of the character to be read next.
6574 + * @return {int} The row of the character to be read next.
6575 + * @method getLine
6576 + */
6577 + getLine: function() {
6578 + return this._line;
6579 + },
6580 +
6581 + /**
6582 + * Determines if you're at the end of the input.
6583 + * @return {Boolean} True if there's no more input, false otherwise.
6584 + * @method eof
6585 + */
6586 + eof: function() {
6587 + return this._cursor === this._input.length;
6588 + },
6589 +
6590 + //-------------------------------------------------------------------------
6591 + // Basic reading
6592 + //-------------------------------------------------------------------------
6593 +
6594 + /**
6595 + * Reads the next character without advancing the cursor.
6596 + * @param {int} count How many characters to look ahead (default is 1).
6597 + * @return {String} The next character or null if there is no next character.
6598 + * @method peek
6599 + */
6600 + peek: function(count) {
6601 + var c = null;
6602 + count = typeof count === "undefined" ? 1 : count;
6603 +
6604 + // if we're not at the end of the input...
6605 + if (this._cursor < this._input.length) {
6606 +
6607 + // get character and increment cursor and column
6608 + c = this._input.charAt(this._cursor + count - 1);
6609 + }
6610 +
6611 + return c;
6612 + },
6613 +
6614 + /**
6615 + * Reads the next character from the input and adjusts the row and column
6616 + * accordingly.
6617 + * @return {String} The next character or null if there is no next character.
6618 + * @method read
6619 + */
6620 + read: function() {
6621 + var c = null;
6622 +
6623 + // if we're not at the end of the input...
6624 + if (this._cursor < this._input.length) {
6625 +
6626 + // if the last character was a newline, increment row count
6627 + // and reset column count
6628 + if (this._input.charAt(this._cursor) === "\n") {
6629 + this._line++;
6630 + this._col=1;
6631 + } else {
6632 + this._col++;
6633 + }
6634 +
6635 + // get character and increment cursor and column
6636 + c = this._input.charAt(this._cursor++);
6637 + }
6638 +
6639 + return c;
6640 + },
6641 +
6642 + //-------------------------------------------------------------------------
6643 + // Misc
6644 + //-------------------------------------------------------------------------
6645 +
6646 + /**
6647 + * Saves the current location so it can be returned to later.
6648 + * @method mark
6649 + * @return {void}
6650 + */
6651 + mark: function() {
6652 + this._bookmark = {
6653 + cursor: this._cursor,
6654 + line: this._line,
6655 + col: this._col
6656 + };
6657 + },
6658 +
6659 + reset: function() {
6660 + if (this._bookmark) {
6661 + this._cursor = this._bookmark.cursor;
6662 + this._line = this._bookmark.line;
6663 + this._col = this._bookmark.col;
6664 + delete this._bookmark;
6665 + }
6666 + },
6667 +
6668 + //-------------------------------------------------------------------------
6669 + // Advanced reading
6670 + //-------------------------------------------------------------------------
6671 +
6672 + /**
6673 + * Reads up to and including the given string. Throws an error if that
6674 + * string is not found.
6675 + * @param {String} pattern The string to read.
6676 + * @return {String} The string when it is found.
6677 + * @throws Error when the string pattern is not found.
6678 + * @method readTo
6679 + */
6680 + readTo: function(pattern) {
6681 +
6682 + var buffer = "",
6683 + c;
6684 +
6685 + /*
6686 + * First, buffer must be the same length as the pattern.
6687 + * Then, buffer must end with the pattern or else reach the
6688 + * end of the input.
6689 + */
6690 + while (buffer.length < pattern.length || buffer.lastIndexOf(pattern) !== buffer.length - pattern.length) {
6691 + c = this.read();
6692 + if (c) {
6693 + buffer += c;
6694 + } else {
6695 + throw new Error("Expected \"" + pattern + "\" at line " + this._line + ", col " + this._col + ".");
6696 + }
6697 + }
6698 +
6699 + return buffer;
6700 +
6701 + },
6702 +
6703 + /**
6704 + * Reads characters while each character causes the given
6705 + * filter function to return true. The function is passed
6706 + * in each character and either returns true to continue
6707 + * reading or false to stop.
6708 + * @param {Function} filter The function to read on each character.
6709 + * @return {String} The string made up of all characters that passed the
6710 + * filter check.
6711 + * @method readWhile
6712 + */
6713 + readWhile: function(filter) {
6714 +
6715 + var buffer = "",
6716 + c = this.peek();
6717 +
6718 + while (c !== null && filter(c)) {
6719 + buffer += this.read();
6720 + c = this.peek();
6721 + }
6722 +
6723 + return buffer;
6724 +
6725 + },
6726 +
6727 + /**
6728 + * Reads characters that match either text or a regular expression and
6729 + * returns those characters. If a match is found, the row and column
6730 + * are adjusted; if no match is found, the reader's state is unchanged.
6731 + * reading or false to stop.
6732 + * @param {String|RegExp} matcher If a string, then the literal string
6733 + * value is searched for. If a regular expression, then any string
6734 + * matching the pattern is search for.
6735 + * @return {String} The string made up of all characters that matched or
6736 + * null if there was no match.
6737 + * @method readMatch
6738 + */
6739 + readMatch: function(matcher) {
6740 +
6741 + var source = this._input.substring(this._cursor),
6742 + value = null;
6743 +
6744 + // if it's a string, just do a straight match
6745 + if (typeof matcher === "string") {
6746 + if (source.slice(0, matcher.length) === matcher) {
6747 + value = this.readCount(matcher.length);
6748 + }
6749 + } else if (matcher instanceof RegExp) {
6750 + if (matcher.test(source)) {
6751 + value = this.readCount(RegExp.lastMatch.length);
6752 + }
6753 + }
6754 +
6755 + return value;
6756 + },
6757 +
6758 +
6759 + /**
6760 + * Reads a given number of characters. If the end of the input is reached,
6761 + * it reads only the remaining characters and does not throw an error.
6762 + * @param {int} count The number of characters to read.
6763 + * @return {String} The string made up the read characters.
6764 + * @method readCount
6765 + */
6766 + readCount: function(count) {
6767 + var buffer = "";
6768 +
6769 + while (count--) {
6770 + buffer += this.read();
6771 + }
6772 +
6773 + return buffer;
6774 + }
6775 +
6776 + };
6777 +
6778 + },{}],25:[function(require,module,exports){
6779 + "use strict";
6780 +
6781 + module.exports = SyntaxError;
6782 +
6783 + /**
6784 + * Type to use when a syntax error occurs.
6785 + * @class SyntaxError
6786 + * @namespace parserlib.util
6787 + * @constructor
6788 + * @param {String} message The error message.
6789 + * @param {int} line The line at which the error occurred.
6790 + * @param {int} col The column at which the error occurred.
6791 + */
6792 + function SyntaxError(message, line, col) {
6793 + Error.call(this);
6794 + this.name = this.constructor.name;
6795 +
6796 + /**
6797 + * The column at which the error occurred.
6798 + * @type int
6799 + * @property col
6800 + */
6801 + this.col = col;
6802 +
6803 + /**
6804 + * The line at which the error occurred.
6805 + * @type int
6806 + * @property line
6807 + */
6808 + this.line = line;
6809 +
6810 + /**
6811 + * The text representation of the unit.
6812 + * @type String
6813 + * @property text
6814 + */
6815 + this.message = message;
6816 +
6817 + }
6818 +
6819 + //inherit from Error
6820 + SyntaxError.prototype = Object.create(Error.prototype); // jshint ignore:line
6821 + SyntaxError.prototype.constructor = SyntaxError; // jshint ignore:line
6822 +
6823 + },{}],26:[function(require,module,exports){
6824 + "use strict";
6825 +
6826 + module.exports = SyntaxUnit;
6827 +
6828 + /**
6829 + * Base type to represent a single syntactic unit.
6830 + * @class SyntaxUnit
6831 + * @namespace parserlib.util
6832 + * @constructor
6833 + * @param {String} text The text of the unit.
6834 + * @param {int} line The line of text on which the unit resides.
6835 + * @param {int} col The column of text on which the unit resides.
6836 + */
6837 + function SyntaxUnit(text, line, col, type) {
6838 +
6839 +
6840 + /**
6841 + * The column of text on which the unit resides.
6842 + * @type int
6843 + * @property col
6844 + */
6845 + this.col = col;
6846 +
6847 + /**
6848 + * The line of text on which the unit resides.
6849 + * @type int
6850 + * @property line
6851 + */
6852 + this.line = line;
6853 +
6854 + /**
6855 + * The text representation of the unit.
6856 + * @type String
6857 + * @property text
6858 + */
6859 + this.text = text;
6860 +
6861 + /**
6862 + * The type of syntax unit.
6863 + * @type int
6864 + * @property type
6865 + */
6866 + this.type = type;
6867 + }
6868 +
6869 + /**
6870 + * Create a new syntax unit based solely on the given token.
6871 + * Convenience method for creating a new syntax unit when
6872 + * it represents a single token instead of multiple.
6873 + * @param {Object} token The token object to represent.
6874 + * @return {parserlib.util.SyntaxUnit} The object representing the token.
6875 + * @static
6876 + * @method fromToken
6877 + */
6878 + SyntaxUnit.fromToken = function(token) {
6879 + return new SyntaxUnit(token.value, token.startLine, token.startCol);
6880 + };
6881 +
6882 + SyntaxUnit.prototype = {
6883 +
6884 + //restore constructor
6885 + constructor: SyntaxUnit,
6886 +
6887 + /**
6888 + * Returns the text representation of the unit.
6889 + * @return {String} The text representation of the unit.
6890 + * @method valueOf
6891 + */
6892 + valueOf: function() {
6893 + return this.toString();
6894 + },
6895 +
6896 + /**
6897 + * Returns the text representation of the unit.
6898 + * @return {String} The text representation of the unit.
6899 + * @method toString
6900 + */
6901 + toString: function() {
6902 + return this.text;
6903 + }
6904 +
6905 + };
6906 +
6907 + },{}],27:[function(require,module,exports){
6908 + "use strict";
6909 +
6910 + module.exports = TokenStreamBase;
6911 +
6912 + var StringReader = require("./StringReader");
6913 + var SyntaxError = require("./SyntaxError");
6914 +
6915 + /**
6916 + * Generic TokenStream providing base functionality.
6917 + * @class TokenStreamBase
6918 + * @namespace parserlib.util
6919 + * @constructor
6920 + * @param {String|StringReader} input The text to tokenize or a reader from
6921 + * which to read the input.
6922 + */
6923 + function TokenStreamBase(input, tokenData) {
6924 +
6925 + /**
6926 + * The string reader for easy access to the text.
6927 + * @type StringReader
6928 + * @property _reader
6929 + * @private
6930 + */
6931 + this._reader = new StringReader(input ? input.toString() : "");
6932 +
6933 + /**
6934 + * Token object for the last consumed token.
6935 + * @type Token
6936 + * @property _token
6937 + * @private
6938 + */
6939 + this._token = null;
6940 +
6941 + /**
6942 + * The array of token information.
6943 + * @type Array
6944 + * @property _tokenData
6945 + * @private
6946 + */
6947 + this._tokenData = tokenData;
6948 +
6949 + /**
6950 + * Lookahead token buffer.
6951 + * @type Array
6952 + * @property _lt
6953 + * @private
6954 + */
6955 + this._lt = [];
6956 +
6957 + /**
6958 + * Lookahead token buffer index.
6959 + * @type int
6960 + * @property _ltIndex
6961 + * @private
6962 + */
6963 + this._ltIndex = 0;
6964 +
6965 + this._ltIndexCache = [];
6966 + }
6967 +
6968 + /**
6969 + * Accepts an array of token information and outputs
6970 + * an array of token data containing key-value mappings
6971 + * and matching functions that the TokenStream needs.
6972 + * @param {Array} tokens An array of token descriptors.
6973 + * @return {Array} An array of processed token data.
6974 + * @method createTokenData
6975 + * @static
6976 + */
6977 + TokenStreamBase.createTokenData = function(tokens) {
6978 +
6979 + var nameMap = [],
6980 + typeMap = Object.create(null),
6981 + tokenData = tokens.concat([]),
6982 + i = 0,
6983 + len = tokenData.length+1;
6984 +
6985 + tokenData.UNKNOWN = -1;
6986 + tokenData.unshift({ name:"EOF" });
6987 +
6988 + for (; i < len; i++) {
6989 + nameMap.push(tokenData[i].name);
6990 + tokenData[tokenData[i].name] = i;
6991 + if (tokenData[i].text) {
6992 + typeMap[tokenData[i].text] = i;
6993 + }
6994 + }
6995 +
6996 + tokenData.name = function(tt) {
6997 + return nameMap[tt];
6998 + };
6999 +
7000 + tokenData.type = function(c) {
7001 + return typeMap[c];
7002 + };
7003 +
7004 + return tokenData;
7005 + };
7006 +
7007 + TokenStreamBase.prototype = {
7008 +
7009 + //restore constructor
7010 + constructor: TokenStreamBase,
7011 +
7012 + //-------------------------------------------------------------------------
7013 + // Matching methods
7014 + //-------------------------------------------------------------------------
7015 +
7016 + /**
7017 + * Determines if the next token matches the given token type.
7018 + * If so, that token is consumed; if not, the token is placed
7019 + * back onto the token stream. You can pass in any number of
7020 + * token types and this will return true if any of the token
7021 + * types is found.
7022 + * @param {int|int[]} tokenTypes Either a single token type or an array of
7023 + * token types that the next token might be. If an array is passed,
7024 + * it's assumed that the token can be any of these.
7025 + * @param {variant} channel (Optional) The channel to read from. If not
7026 + * provided, reads from the default (unnamed) channel.
7027 + * @return {Boolean} True if the token type matches, false if not.
7028 + * @method match
7029 + */
7030 + match: function(tokenTypes, channel) {
7031 +
7032 + //always convert to an array, makes things easier
7033 + if (!(tokenTypes instanceof Array)) {
7034 + tokenTypes = [tokenTypes];
7035 + }
7036 +
7037 + var tt = this.get(channel),
7038 + i = 0,
7039 + len = tokenTypes.length;
7040 +
7041 + while (i < len) {
7042 + if (tt === tokenTypes[i++]) {
7043 + return true;
7044 + }
7045 + }
7046 +
7047 + //no match found, put the token back
7048 + this.unget();
7049 + return false;
7050 + },
7051 +
7052 + /**
7053 + * Determines if the next token matches the given token type.
7054 + * If so, that token is consumed; if not, an error is thrown.
7055 + * @param {int|int[]} tokenTypes Either a single token type or an array of
7056 + * token types that the next token should be. If an array is passed,
7057 + * it's assumed that the token must be one of these.
7058 + * @return {void}
7059 + * @method mustMatch
7060 + */
7061 + mustMatch: function(tokenTypes) {
7062 +
7063 + var token;
7064 +
7065 + //always convert to an array, makes things easier
7066 + if (!(tokenTypes instanceof Array)) {
7067 + tokenTypes = [tokenTypes];
7068 + }
7069 +
7070 + if (!this.match.apply(this, arguments)) {
7071 + token = this.LT(1);
7072 + throw new SyntaxError("Expected " + this._tokenData[tokenTypes[0]].name +
7073 + " at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
7074 + }
7075 + },
7076 +
7077 + //-------------------------------------------------------------------------
7078 + // Consuming methods
7079 + //-------------------------------------------------------------------------
7080 +
7081 + /**
7082 + * Keeps reading from the token stream until either one of the specified
7083 + * token types is found or until the end of the input is reached.
7084 + * @param {int|int[]} tokenTypes Either a single token type or an array of
7085 + * token types that the next token should be. If an array is passed,
7086 + * it's assumed that the token must be one of these.
7087 + * @param {variant} channel (Optional) The channel to read from. If not
7088 + * provided, reads from the default (unnamed) channel.
7089 + * @return {void}
7090 + * @method advance
7091 + */
7092 + advance: function(tokenTypes, channel) {
7093 +
7094 + while (this.LA(0) !== 0 && !this.match(tokenTypes, channel)) {
7095 + this.get();
7096 + }
7097 +
7098 + return this.LA(0);
7099 + },
7100 +
7101 + /**
7102 + * Consumes the next token from the token stream.
7103 + * @return {int} The token type of the token that was just consumed.
7104 + * @method get
7105 + */
7106 + get: function(channel) {
7107 +
7108 + var tokenInfo = this._tokenData,
7109 + i =0,
7110 + token,
7111 + info;
7112 +
7113 + //check the lookahead buffer first
7114 + if (this._lt.length && this._ltIndex >= 0 && this._ltIndex < this._lt.length) {
7115 +
7116 + i++;
7117 + this._token = this._lt[this._ltIndex++];
7118 + info = tokenInfo[this._token.type];
7119 +
7120 + //obey channels logic
7121 + while ((info.channel !== undefined && channel !== info.channel) &&
7122 + this._ltIndex < this._lt.length) {
7123 + this._token = this._lt[this._ltIndex++];
7124 + info = tokenInfo[this._token.type];
7125 + i++;
7126 + }
7127 +
7128 + //here be dragons
7129 + if ((info.channel === undefined || channel === info.channel) &&
7130 + this._ltIndex <= this._lt.length) {
7131 + this._ltIndexCache.push(i);
7132 + return this._token.type;
7133 + }
7134 + }
7135 +
7136 + //call token retriever method
7137 + token = this._getToken();
7138 +
7139 + //if it should be hidden, don't save a token
7140 + if (token.type > -1 && !tokenInfo[token.type].hide) {
7141 +
7142 + //apply token channel
7143 + token.channel = tokenInfo[token.type].channel;
7144 +
7145 + //save for later
7146 + this._token = token;
7147 + this._lt.push(token);
7148 +
7149 + //save space that will be moved (must be done before array is truncated)
7150 + this._ltIndexCache.push(this._lt.length - this._ltIndex + i);
7151 +
7152 + //keep the buffer under 5 items
7153 + if (this._lt.length > 5) {
7154 + this._lt.shift();
7155 + }
7156 +
7157 + //also keep the shift buffer under 5 items
7158 + if (this._ltIndexCache.length > 5) {
7159 + this._ltIndexCache.shift();
7160 + }
7161 +
7162 + //update lookahead index
7163 + this._ltIndex = this._lt.length;
7164 + }
7165 +
7166 + /*
7167 + * Skip to the next token if:
7168 + * 1. The token type is marked as hidden.
7169 + * 2. The token type has a channel specified and it isn't the current channel.
7170 + */
7171 + info = tokenInfo[token.type];
7172 + if (info &&
7173 + (info.hide ||
7174 + (info.channel !== undefined && channel !== info.channel))) {
7175 + return this.get(channel);
7176 + } else {
7177 + //return just the type
7178 + return token.type;
7179 + }
7180 + },
7181 +
7182 + /**
7183 + * Looks ahead a certain number of tokens and returns the token type at
7184 + * that position. This will throw an error if you lookahead past the
7185 + * end of input, past the size of the lookahead buffer, or back past
7186 + * the first token in the lookahead buffer.
7187 + * @param {int} The index of the token type to retrieve. 0 for the
7188 + * current token, 1 for the next, -1 for the previous, etc.
7189 + * @return {int} The token type of the token in the given position.
7190 + * @method LA
7191 + */
7192 + LA: function(index) {
7193 + var total = index,
7194 + tt;
7195 + if (index > 0) {
7196 + //TODO: Store 5 somewhere
7197 + if (index > 5) {
7198 + throw new Error("Too much lookahead.");
7199 + }
7200 +
7201 + //get all those tokens
7202 + while (total) {
7203 + tt = this.get();
7204 + total--;
7205 + }
7206 +
7207 + //unget all those tokens
7208 + while (total < index) {
7209 + this.unget();
7210 + total++;
7211 + }
7212 + } else if (index < 0) {
7213 +
7214 + if (this._lt[this._ltIndex+index]) {
7215 + tt = this._lt[this._ltIndex+index].type;
7216 + } else {
7217 + throw new Error("Too much lookbehind.");
7218 + }
7219 +
7220 + } else {
7221 + tt = this._token.type;
7222 + }
7223 +
7224 + return tt;
7225 +
7226 + },
7227 +
7228 + /**
7229 + * Looks ahead a certain number of tokens and returns the token at
7230 + * that position. This will throw an error if you lookahead past the
7231 + * end of input, past the size of the lookahead buffer, or back past
7232 + * the first token in the lookahead buffer.
7233 + * @param {int} The index of the token type to retrieve. 0 for the
7234 + * current token, 1 for the next, -1 for the previous, etc.
7235 + * @return {Object} The token of the token in the given position.
7236 + * @method LA
7237 + */
7238 + LT: function(index) {
7239 +
7240 + //lookahead first to prime the token buffer
7241 + this.LA(index);
7242 +
7243 + //now find the token, subtract one because _ltIndex is already at the next index
7244 + return this._lt[this._ltIndex+index-1];
7245 + },
7246 +
7247 + /**
7248 + * Returns the token type for the next token in the stream without
7249 + * consuming it.
7250 + * @return {int} The token type of the next token in the stream.
7251 + * @method peek
7252 + */
7253 + peek: function() {
7254 + return this.LA(1);
7255 + },
7256 +
7257 + /**
7258 + * Returns the actual token object for the last consumed token.
7259 + * @return {Token} The token object for the last consumed token.
7260 + * @method token
7261 + */
7262 + token: function() {
7263 + return this._token;
7264 + },
7265 +
7266 + /**
7267 + * Returns the name of the token for the given token type.
7268 + * @param {int} tokenType The type of token to get the name of.
7269 + * @return {String} The name of the token or "UNKNOWN_TOKEN" for any
7270 + * invalid token type.
7271 + * @method tokenName
7272 + */
7273 + tokenName: function(tokenType) {
7274 + if (tokenType < 0 || tokenType > this._tokenData.length) {
7275 + return "UNKNOWN_TOKEN";
7276 + } else {
7277 + return this._tokenData[tokenType].name;
7278 + }
7279 + },
7280 +
7281 + /**
7282 + * Returns the token type value for the given token name.
7283 + * @param {String} tokenName The name of the token whose value should be returned.
7284 + * @return {int} The token type value for the given token name or -1
7285 + * for an unknown token.
7286 + * @method tokenName
7287 + */
7288 + tokenType: function(tokenName) {
7289 + return this._tokenData[tokenName] || -1;
7290 + },
7291 +
7292 + /**
7293 + * Returns the last consumed token to the token stream.
7294 + * @method unget
7295 + */
7296 + unget: function() {
7297 + //if (this._ltIndex > -1) {
7298 + if (this._ltIndexCache.length) {
7299 + this._ltIndex -= this._ltIndexCache.pop();//--;
7300 + this._token = this._lt[this._ltIndex - 1];
7301 + } else {
7302 + throw new Error("Too much lookahead.");
7303 + }
7304 + }
7305 +
7306 + };
7307 +
7308 +
7309 + },{"./StringReader":24,"./SyntaxError":25}],28:[function(require,module,exports){
7310 + "use strict";
7311 +
7312 + module.exports = {
7313 + StringReader : require("./StringReader"),
7314 + SyntaxError : require("./SyntaxError"),
7315 + SyntaxUnit : require("./SyntaxUnit"),
7316 + EventTarget : require("./EventTarget"),
7317 + TokenStreamBase : require("./TokenStreamBase")
7318 + };
7319 +
7320 + },{"./EventTarget":23,"./StringReader":24,"./SyntaxError":25,"./SyntaxUnit":26,"./TokenStreamBase":27}],"parserlib":[function(require,module,exports){
7321 + "use strict";
7322 +
7323 + module.exports = {
7324 + css : require("./css"),
7325 + util : require("./util")
7326 + };
7327 +
7328 + },{"./css":22,"./util":28}]},{},[]);
7329 +
7330 + return require('parserlib');
7331 + })();
7332 + var clone = (function() {
7333 + 'use strict';
7334 +
7335 + var nativeMap;
7336 + try {
7337 + nativeMap = Map;
7338 + } catch(_) {
7339 + // maybe a reference error because no `Map`. Give it a dummy value that no
7340 + // value will ever be an instanceof.
7341 + nativeMap = function() {};
7342 + }
7343 +
7344 + var nativeSet;
7345 + try {
7346 + nativeSet = Set;
7347 + } catch(_) {
7348 + nativeSet = function() {};
7349 + }
7350 +
7351 + var nativePromise;
7352 + try {
7353 + nativePromise = Promise;
7354 + } catch(_) {
7355 + nativePromise = function() {};
7356 + }
7357 +
7358 + /**
7359 + * Clones (copies) an Object using deep copying.
7360 + *
7361 + * This function supports circular references by default, but if you are certain
7362 + * there are no circular references in your object, you can save some CPU time
7363 + * by calling clone(obj, false).
7364 + *
7365 + * Caution: if `circular` is false and `parent` contains circular references,
7366 + * your program may enter an infinite loop and crash.
7367 + *
7368 + * @param `parent` - the object to be cloned
7369 + * @param `circular` - set to true if the object to be cloned may contain
7370 + * circular references. (optional - true by default)
7371 + * @param `depth` - set to a number if the object is only to be cloned to
7372 + * a particular depth. (optional - defaults to Infinity)
7373 + * @param `prototype` - sets the prototype to be used when cloning an object.
7374 + * (optional - defaults to parent prototype).
7375 + * @param `includeNonEnumerable` - set to true if the non-enumerable properties
7376 + * should be cloned as well. Non-enumerable properties on the prototype
7377 + * chain will be ignored. (optional - false by default)
7378 + */
7379 + function clone(parent, circular, depth, prototype, includeNonEnumerable) {
7380 + if (typeof circular === 'object') {
7381 + depth = circular.depth;
7382 + prototype = circular.prototype;
7383 + includeNonEnumerable = circular.includeNonEnumerable;
7384 + circular = circular.circular;
7385 + }
7386 + // maintain two arrays for circular references, where corresponding parents
7387 + // and children have the same index
7388 + var allParents = [];
7389 + var allChildren = [];
7390 +
7391 + var useBuffer = typeof Buffer != 'undefined';
7392 +
7393 + if (typeof circular == 'undefined')
7394 + circular = true;
7395 +
7396 + if (typeof depth == 'undefined')
7397 + depth = Infinity;
7398 +
7399 + // recurse this function so we don't reset allParents and allChildren
7400 + function _clone(parent, depth) {
7401 + // cloning null always returns null
7402 + if (parent === null)
7403 + return null;
7404 +
7405 + if (depth === 0)
7406 + return parent;
7407 +
7408 + var child;
7409 + var proto;
7410 + if (typeof parent != 'object') {
7411 + return parent;
7412 + }
7413 +
7414 + if (parent instanceof nativeMap) {
7415 + child = new nativeMap();
7416 + } else if (parent instanceof nativeSet) {
7417 + child = new nativeSet();
7418 + } else if (parent instanceof nativePromise) {
7419 + child = new nativePromise(function (resolve, reject) {
7420 + parent.then(function(value) {
7421 + resolve(_clone(value, depth - 1));
7422 + }, function(err) {
7423 + reject(_clone(err, depth - 1));
7424 + });
7425 + });
7426 + } else if (clone.__isArray(parent)) {
7427 + child = [];
7428 + } else if (clone.__isRegExp(parent)) {
7429 + child = new RegExp(parent.source, __getRegExpFlags(parent));
7430 + if (parent.lastIndex) child.lastIndex = parent.lastIndex;
7431 + } else if (clone.__isDate(parent)) {
7432 + child = new Date(parent.getTime());
7433 + } else if (useBuffer && Buffer.isBuffer(parent)) {
7434 + child = new Buffer(parent.length);
7435 + parent.copy(child);
7436 + return child;
7437 + } else if (parent instanceof Error) {
7438 + child = Object.create(parent);
7439 + } else {
7440 + if (typeof prototype == 'undefined') {
7441 + proto = Object.getPrototypeOf(parent);
7442 + child = Object.create(proto);
7443 + }
7444 + else {
7445 + child = Object.create(prototype);
7446 + proto = prototype;
7447 + }
7448 + }
7449 +
7450 + if (circular) {
7451 + var index = allParents.indexOf(parent);
7452 +
7453 + if (index != -1) {
7454 + return allChildren[index];
7455 + }
7456 + allParents.push(parent);
7457 + allChildren.push(child);
7458 + }
7459 +
7460 + if (parent instanceof nativeMap) {
7461 + var keyIterator = parent.keys();
7462 + while(true) {
7463 + var next = keyIterator.next();
7464 + if (next.done) {
7465 + break;
7466 + }
7467 + var keyChild = _clone(next.value, depth - 1);
7468 + var valueChild = _clone(parent.get(next.value), depth - 1);
7469 + child.set(keyChild, valueChild);
7470 + }
7471 + }
7472 + if (parent instanceof nativeSet) {
7473 + var iterator = parent.keys();
7474 + while(true) {
7475 + var next = iterator.next();
7476 + if (next.done) {
7477 + break;
7478 + }
7479 + var entryChild = _clone(next.value, depth - 1);
7480 + child.add(entryChild);
7481 + }
7482 + }
7483 +
7484 + for (var i in parent) {
7485 + var attrs;
7486 + if (proto) {
7487 + attrs = Object.getOwnPropertyDescriptor(proto, i);
7488 + }
7489 +
7490 + if (attrs && attrs.set == null) {
7491 + continue;
7492 + }
7493 + child[i] = _clone(parent[i], depth - 1);
7494 + }
7495 +
7496 + if (Object.getOwnPropertySymbols) {
7497 + var symbols = Object.getOwnPropertySymbols(parent);
7498 + for (var i = 0; i < symbols.length; i++) {
7499 + // Don't need to worry about cloning a symbol because it is a primitive,
7500 + // like a number or string.
7501 + var symbol = symbols[i];
7502 + var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
7503 + if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
7504 + continue;
7505 + }
7506 + child[symbol] = _clone(parent[symbol], depth - 1);
7507 + if (!descriptor.enumerable) {
7508 + Object.defineProperty(child, symbol, {
7509 + enumerable: false
7510 + });
7511 + }
7512 + }
7513 + }
7514 +
7515 + if (includeNonEnumerable) {
7516 + var allPropertyNames = Object.getOwnPropertyNames(parent);
7517 + for (var i = 0; i < allPropertyNames.length; i++) {
7518 + var propertyName = allPropertyNames[i];
7519 + var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
7520 + if (descriptor && descriptor.enumerable) {
7521 + continue;
7522 + }
7523 + child[propertyName] = _clone(parent[propertyName], depth - 1);
7524 + Object.defineProperty(child, propertyName, {
7525 + enumerable: false
7526 + });
7527 + }
7528 + }
7529 +
7530 + return child;
7531 + }
7532 +
7533 + return _clone(parent, depth);
7534 + }
7535 +
7536 + /**
7537 + * Simple flat clone using prototype, accepts only objects, usefull for property
7538 + * override on FLAT configuration object (no nested props).
7539 + *
7540 + * USE WITH CAUTION! This may not behave as you wish if you do not know how this
7541 + * works.
7542 + */
7543 + clone.clonePrototype = function clonePrototype(parent) {
7544 + if (parent === null)
7545 + return null;
7546 +
7547 + var c = function () {};
7548 + c.prototype = parent;
7549 + return new c();
7550 + };
7551 +
7552 + // private utility functions
7553 +
7554 + function __objToStr(o) {
7555 + return Object.prototype.toString.call(o);
7556 + }
7557 + clone.__objToStr = __objToStr;
7558 +
7559 + function __isDate(o) {
7560 + return typeof o === 'object' && __objToStr(o) === '[object Date]';
7561 + }
7562 + clone.__isDate = __isDate;
7563 +
7564 + function __isArray(o) {
7565 + return typeof o === 'object' && __objToStr(o) === '[object Array]';
7566 + }
7567 + clone.__isArray = __isArray;
7568 +
7569 + function __isRegExp(o) {
7570 + return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
7571 + }
7572 + clone.__isRegExp = __isRegExp;
7573 +
7574 + function __getRegExpFlags(re) {
7575 + var flags = '';
7576 + if (re.global) flags += 'g';
7577 + if (re.ignoreCase) flags += 'i';
7578 + if (re.multiline) flags += 'm';
7579 + return flags;
7580 + }
7581 + clone.__getRegExpFlags = __getRegExpFlags;
7582 +
7583 + return clone;
7584 + })();
7585 +
7586 + if (typeof module === 'object' && module.exports) {
7587 + module.exports = clone;
7588 + }
7589 +
7590 + /**
7591 + * Main CSSLint object.
7592 + * @class CSSLint
7593 + * @static
7594 + * @extends parserlib.util.EventTarget
7595 + */
7596 +
7597 + /* global parserlib, clone, Reporter */
7598 + /* exported CSSLint */
7599 +
7600 + var CSSLint = (function() {
7601 + "use strict";
7602 +
7603 + var rules = [],
7604 + formatters = [],
7605 + embeddedRuleset = /\/\*\s*csslint([^\*]*)\*\//,
7606 + api = new parserlib.util.EventTarget();
7607 +
7608 + api.version = "1.0.4";
7609 +
7610 + //-------------------------------------------------------------------------
7611 + // Rule Management
7612 + //-------------------------------------------------------------------------
7613 +
7614 + /**
7615 + * Adds a new rule to the engine.
7616 + * @param {Object} rule The rule to add.
7617 + * @method addRule
7618 + */
7619 + api.addRule = function(rule) {
7620 + rules.push(rule);
7621 + rules[rule.id] = rule;
7622 + };
7623 +
7624 + /**
7625 + * Clears all rule from the engine.
7626 + * @method clearRules
7627 + */
7628 + api.clearRules = function() {
7629 + rules = [];
7630 + };
7631 +
7632 + /**
7633 + * Returns the rule objects.
7634 + * @return An array of rule objects.
7635 + * @method getRules
7636 + */
7637 + api.getRules = function() {
7638 + return [].concat(rules).sort(function(a, b) {
7639 + return a.id > b.id ? 1 : 0;
7640 + });
7641 + };
7642 +
7643 + /**
7644 + * Returns a ruleset configuration object with all current rules.
7645 + * @return A ruleset object.
7646 + * @method getRuleset
7647 + */
7648 + api.getRuleset = function() {
7649 + var ruleset = {},
7650 + i = 0,
7651 + len = rules.length;
7652 +
7653 + while (i < len) {
7654 + ruleset[rules[i++].id] = 1; // by default, everything is a warning
7655 + }
7656 +
7657 + return ruleset;
7658 + };
7659 +
7660 + /**
7661 + * Returns a ruleset object based on embedded rules.
7662 + * @param {String} text A string of css containing embedded rules.
7663 + * @param {Object} ruleset A ruleset object to modify.
7664 + * @return {Object} A ruleset object.
7665 + * @method getEmbeddedRuleset
7666 + */
7667 + function applyEmbeddedRuleset(text, ruleset) {
7668 + var valueMap,
7669 + embedded = text && text.match(embeddedRuleset),
7670 + rules = embedded && embedded[1];
7671 +
7672 + if (rules) {
7673 + valueMap = {
7674 + "true": 2, // true is error
7675 + "": 1, // blank is warning
7676 + "false": 0, // false is ignore
7677 +
7678 + "2": 2, // explicit error
7679 + "1": 1, // explicit warning
7680 + "0": 0 // explicit ignore
7681 + };
7682 +
7683 + rules.toLowerCase().split(",").forEach(function(rule) {
7684 + var pair = rule.split(":"),
7685 + property = pair[0] || "",
7686 + value = pair[1] || "";
7687 +
7688 + ruleset[property.trim()] = valueMap[value.trim()];
7689 + });
7690 + }
7691 +
7692 + return ruleset;
7693 + }
7694 +
7695 + //-------------------------------------------------------------------------
7696 + // Formatters
7697 + //-------------------------------------------------------------------------
7698 +
7699 + /**
7700 + * Adds a new formatter to the engine.
7701 + * @param {Object} formatter The formatter to add.
7702 + * @method addFormatter
7703 + */
7704 + api.addFormatter = function(formatter) {
7705 + // formatters.push(formatter);
7706 + formatters[formatter.id] = formatter;
7707 + };
7708 +
7709 + /**
7710 + * Retrieves a formatter for use.
7711 + * @param {String} formatId The name of the format to retrieve.
7712 + * @return {Object} The formatter or undefined.
7713 + * @method getFormatter
7714 + */
7715 + api.getFormatter = function(formatId) {
7716 + return formatters[formatId];
7717 + };
7718 +
7719 + /**
7720 + * Formats the results in a particular format for a single file.
7721 + * @param {Object} result The results returned from CSSLint.verify().
7722 + * @param {String} filename The filename for which the results apply.
7723 + * @param {String} formatId The name of the formatter to use.
7724 + * @param {Object} options (Optional) for special output handling.
7725 + * @return {String} A formatted string for the results.
7726 + * @method format
7727 + */
7728 + api.format = function(results, filename, formatId, options) {
7729 + var formatter = this.getFormatter(formatId),
7730 + result = null;
7731 +
7732 + if (formatter) {
7733 + result = formatter.startFormat();
7734 + result += formatter.formatResults(results, filename, options || {});
7735 + result += formatter.endFormat();
7736 + }
7737 +
7738 + return result;
7739 + };
7740 +
7741 + /**
7742 + * Indicates if the given format is supported.
7743 + * @param {String} formatId The ID of the format to check.
7744 + * @return {Boolean} True if the format exists, false if not.
7745 + * @method hasFormat
7746 + */
7747 + api.hasFormat = function(formatId) {
7748 + return formatters.hasOwnProperty(formatId);
7749 + };
7750 +
7751 + //-------------------------------------------------------------------------
7752 + // Verification
7753 + //-------------------------------------------------------------------------
7754 +
7755 + /**
7756 + * Starts the verification process for the given CSS text.
7757 + * @param {String} text The CSS text to verify.
7758 + * @param {Object} ruleset (Optional) List of rules to apply. If null, then
7759 + * all rules are used. If a rule has a value of 1 then it's a warning,
7760 + * a value of 2 means it's an error.
7761 + * @return {Object} Results of the verification.
7762 + * @method verify
7763 + */
7764 + api.verify = function(text, ruleset) {
7765 +
7766 + var i = 0,
7767 + reporter,
7768 + lines,
7769 + allow = {},
7770 + ignore = [],
7771 + report,
7772 + parser = new parserlib.css.Parser({
7773 + starHack: true,
7774 + ieFilters: true,
7775 + underscoreHack: true,
7776 + strict: false
7777 + });
7778 +
7779 + // normalize line endings
7780 + lines = text.replace(/\n\r?/g, "$split$").split("$split$");
7781 +
7782 + // find 'allow' comments
7783 + CSSLint.Util.forEach(lines, function (line, lineno) {
7784 + var allowLine = line && line.match(/\/\*[ \t]*csslint[ \t]+allow:[ \t]*([^\*]*)\*\//i),
7785 + allowRules = allowLine && allowLine[1],
7786 + allowRuleset = {};
7787 +
7788 + if (allowRules) {
7789 + allowRules.toLowerCase().split(",").forEach(function(allowRule) {
7790 + allowRuleset[allowRule.trim()] = true;
7791 + });
7792 + if (Object.keys(allowRuleset).length > 0) {
7793 + allow[lineno + 1] = allowRuleset;
7794 + }
7795 + }
7796 + });
7797 +
7798 + var ignoreStart = null,
7799 + ignoreEnd = null;
7800 + CSSLint.Util.forEach(lines, function (line, lineno) {
7801 + // Keep oldest, "unclosest" ignore:start
7802 + if (ignoreStart === null && line.match(/\/\*[ \t]*csslint[ \t]+ignore:start[ \t]*\*\//i)) {
7803 + ignoreStart = lineno;
7804 + }
7805 +
7806 + if (line.match(/\/\*[ \t]*csslint[ \t]+ignore:end[ \t]*\*\//i)) {
7807 + ignoreEnd = lineno;
7808 + }
7809 +
7810 + if (ignoreStart !== null && ignoreEnd !== null) {
7811 + ignore.push([ignoreStart, ignoreEnd]);
7812 + ignoreStart = ignoreEnd = null;
7813 + }
7814 + });
7815 +
7816 + // Close remaining ignore block, if any
7817 + if (ignoreStart !== null) {
7818 + ignore.push([ignoreStart, lines.length]);
7819 + }
7820 +
7821 + if (!ruleset) {
7822 + ruleset = this.getRuleset();
7823 + }
7824 +
7825 + if (embeddedRuleset.test(text)) {
7826 + // defensively copy so that caller's version does not get modified
7827 + ruleset = clone(ruleset);
7828 + ruleset = applyEmbeddedRuleset(text, ruleset);
7829 + }
7830 +
7831 + reporter = new Reporter(lines, ruleset, allow, ignore);
7832 +
7833 + ruleset.errors = 2; // always report parsing errors as errors
7834 + for (i in ruleset) {
7835 + if (ruleset.hasOwnProperty(i) && ruleset[i]) {
7836 + if (rules[i]) {
7837 + rules[i].init(parser, reporter);
7838 + }
7839 + }
7840 + }
7841 +
7842 +
7843 + // capture most horrible error type
7844 + try {
7845 + parser.parse(text);
7846 + } catch (ex) {
7847 + reporter.error("Fatal error, cannot continue: " + ex.message, ex.line, ex.col, {});
7848 + }
7849 +
7850 + report = {
7851 + messages : reporter.messages,
7852 + stats : reporter.stats,
7853 + ruleset : reporter.ruleset,
7854 + allow : reporter.allow,
7855 + ignore : reporter.ignore
7856 + };
7857 +
7858 + // sort by line numbers, rollups at the bottom
7859 + report.messages.sort(function (a, b) {
7860 + if (a.rollup && !b.rollup) {
7861 + return 1;
7862 + } else if (!a.rollup && b.rollup) {
7863 + return -1;
7864 + } else {
7865 + return a.line - b.line;
7866 + }
7867 + });
7868 +
7869 + return report;
7870 + };
7871 +
7872 + //-------------------------------------------------------------------------
7873 + // Publish the API
7874 + //-------------------------------------------------------------------------
7875 +
7876 + return api;
7877 +
7878 + })();
7879 +
7880 + /**
7881 + * An instance of Report is used to report results of the
7882 + * verification back to the main API.
7883 + * @class Reporter
7884 + * @constructor
7885 + * @param {String[]} lines The text lines of the source.
7886 + * @param {Object} ruleset The set of rules to work with, including if
7887 + * they are errors or warnings.
7888 + * @param {Object} explicitly allowed lines
7889 + * @param {[][]} ingore list of line ranges to be ignored
7890 + */
7891 + function Reporter(lines, ruleset, allow, ignore) {
7892 + "use strict";
7893 +
7894 + /**
7895 + * List of messages being reported.
7896 + * @property messages
7897 + * @type String[]
7898 + */
7899 + this.messages = [];
7900 +
7901 + /**
7902 + * List of statistics being reported.
7903 + * @property stats
7904 + * @type String[]
7905 + */
7906 + this.stats = [];
7907 +
7908 + /**
7909 + * Lines of code being reported on. Used to provide contextual information
7910 + * for messages.
7911 + * @property lines
7912 + * @type String[]
7913 + */
7914 + this.lines = lines;
7915 +
7916 + /**
7917 + * Information about the rules. Used to determine whether an issue is an
7918 + * error or warning.
7919 + * @property ruleset
7920 + * @type Object
7921 + */
7922 + this.ruleset = ruleset;
7923 +
7924 + /**
7925 + * Lines with specific rule messages to leave out of the report.
7926 + * @property allow
7927 + * @type Object
7928 + */
7929 + this.allow = allow;
7930 + if (!this.allow) {
7931 + this.allow = {};
7932 + }
7933 +
7934 + /**
7935 + * Linesets not to include in the report.
7936 + * @property ignore
7937 + * @type [][]
7938 + */
7939 + this.ignore = ignore;
7940 + if (!this.ignore) {
7941 + this.ignore = [];
7942 + }
7943 + }
7944 +
7945 + Reporter.prototype = {
7946 +
7947 + // restore constructor
7948 + constructor: Reporter,
7949 +
7950 + /**
7951 + * Report an error.
7952 + * @param {String} message The message to store.
7953 + * @param {int} line The line number.
7954 + * @param {int} col The column number.
7955 + * @param {Object} rule The rule this message relates to.
7956 + * @method error
7957 + */
7958 + error: function(message, line, col, rule) {
7959 + "use strict";
7960 + this.messages.push({
7961 + type : "error",
7962 + line : line,
7963 + col : col,
7964 + message : message,
7965 + evidence: this.lines[line-1],
7966 + rule : rule || {}
7967 + });
7968 + },
7969 +
7970 + /**
7971 + * Report an warning.
7972 + * @param {String} message The message to store.
7973 + * @param {int} line The line number.
7974 + * @param {int} col The column number.
7975 + * @param {Object} rule The rule this message relates to.
7976 + * @method warn
7977 + * @deprecated Use report instead.
7978 + */
7979 + warn: function(message, line, col, rule) {
7980 + "use strict";
7981 + this.report(message, line, col, rule);
7982 + },
7983 +
7984 + /**
7985 + * Report an issue.
7986 + * @param {String} message The message to store.
7987 + * @param {int} line The line number.
7988 + * @param {int} col The column number.
7989 + * @param {Object} rule The rule this message relates to.
7990 + * @method report
7991 + */
7992 + report: function(message, line, col, rule) {
7993 + "use strict";
7994 +
7995 + // Check if rule violation should be allowed
7996 + if (this.allow.hasOwnProperty(line) && this.allow[line].hasOwnProperty(rule.id)) {
7997 + return;
7998 + }
7999 +
8000 + var ignore = false;
8001 + CSSLint.Util.forEach(this.ignore, function (range) {
8002 + if (range[0] <= line && line <= range[1]) {
8003 + ignore = true;
8004 + }
8005 + });
8006 + if (ignore) {
8007 + return;
8008 + }
8009 +
8010 + this.messages.push({
8011 + type : this.ruleset[rule.id] === 2 ? "error" : "warning",
8012 + line : line,
8013 + col : col,
8014 + message : message,
8015 + evidence: this.lines[line-1],
8016 + rule : rule
8017 + });
8018 + },
8019 +
8020 + /**
8021 + * Report some informational text.
8022 + * @param {String} message The message to store.
8023 + * @param {int} line The line number.
8024 + * @param {int} col The column number.
8025 + * @param {Object} rule The rule this message relates to.
8026 + * @method info
8027 + */
8028 + info: function(message, line, col, rule) {
8029 + "use strict";
8030 + this.messages.push({
8031 + type : "info",
8032 + line : line,
8033 + col : col,
8034 + message : message,
8035 + evidence: this.lines[line-1],
8036 + rule : rule
8037 + });
8038 + },
8039 +
8040 + /**
8041 + * Report some rollup error information.
8042 + * @param {String} message The message to store.
8043 + * @param {Object} rule The rule this message relates to.
8044 + * @method rollupError
8045 + */
8046 + rollupError: function(message, rule) {
8047 + "use strict";
8048 + this.messages.push({
8049 + type : "error",
8050 + rollup : true,
8051 + message : message,
8052 + rule : rule
8053 + });
8054 + },
8055 +
8056 + /**
8057 + * Report some rollup warning information.
8058 + * @param {String} message The message to store.
8059 + * @param {Object} rule The rule this message relates to.
8060 + * @method rollupWarn
8061 + */
8062 + rollupWarn: function(message, rule) {
8063 + "use strict";
8064 + this.messages.push({
8065 + type : "warning",
8066 + rollup : true,
8067 + message : message,
8068 + rule : rule
8069 + });
8070 + },
8071 +
8072 + /**
8073 + * Report a statistic.
8074 + * @param {String} name The name of the stat to store.
8075 + * @param {Variant} value The value of the stat.
8076 + * @method stat
8077 + */
8078 + stat: function(name, value) {
8079 + "use strict";
8080 + this.stats[name] = value;
8081 + }
8082 + };
8083 +
8084 + // expose for testing purposes
8085 + CSSLint._Reporter = Reporter;
8086 +
8087 + /*
8088 + * Utility functions that make life easier.
8089 + */
8090 + CSSLint.Util = {
8091 + /*
8092 + * Adds all properties from supplier onto receiver,
8093 + * overwriting if the same name already exists on
8094 + * receiver.
8095 + * @param {Object} The object to receive the properties.
8096 + * @param {Object} The object to provide the properties.
8097 + * @return {Object} The receiver
8098 + */
8099 + mix: function(receiver, supplier) {
8100 + "use strict";
8101 + var prop;
8102 +
8103 + for (prop in supplier) {
8104 + if (supplier.hasOwnProperty(prop)) {
8105 + receiver[prop] = supplier[prop];
8106 + }
8107 + }
8108 +
8109 + return prop;
8110 + },
8111 +
8112 + /*
8113 + * Polyfill for array indexOf() method.
8114 + * @param {Array} values The array to search.
8115 + * @param {Variant} value The value to search for.
8116 + * @return {int} The index of the value if found, -1 if not.
8117 + */
8118 + indexOf: function(values, value) {
8119 + "use strict";
8120 + if (values.indexOf) {
8121 + return values.indexOf(value);
8122 + } else {
8123 + for (var i=0, len=values.length; i < len; i++) {
8124 + if (values[i] === value) {
8125 + return i;
8126 + }
8127 + }
8128 + return -1;
8129 + }
8130 + },
8131 +
8132 + /*
8133 + * Polyfill for array forEach() method.
8134 + * @param {Array} values The array to operate on.
8135 + * @param {Function} func The function to call on each item.
8136 + * @return {void}
8137 + */
8138 + forEach: function(values, func) {
8139 + "use strict";
8140 + if (values.forEach) {
8141 + return values.forEach(func);
8142 + } else {
8143 + for (var i=0, len=values.length; i < len; i++) {
8144 + func(values[i], i, values);
8145 + }
8146 + }
8147 + }
8148 + };
8149 +
8150 + /*
8151 + * Rule: Don't use adjoining classes (.foo.bar).
8152 + */
8153 +
8154 + CSSLint.addRule({
8155 +
8156 + // rule information
8157 + id: "adjoining-classes",
8158 + name: "Disallow adjoining classes",
8159 + desc: "Don't use adjoining classes.",
8160 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-adjoining-classes",
8161 + browsers: "IE6",
8162 +
8163 + // initialization
8164 + init: function(parser, reporter) {
8165 + "use strict";
8166 + var rule = this;
8167 + parser.addListener("startrule", function(event) {
8168 + var selectors = event.selectors,
8169 + selector,
8170 + part,
8171 + modifier,
8172 + classCount,
8173 + i, j, k;
8174 +
8175 + for (i=0; i < selectors.length; i++) {
8176 + selector = selectors[i];
8177 + for (j=0; j < selector.parts.length; j++) {
8178 + part = selector.parts[j];
8179 + if (part.type === parser.SELECTOR_PART_TYPE) {
8180 + classCount = 0;
8181 + for (k=0; k < part.modifiers.length; k++) {
8182 + modifier = part.modifiers[k];
8183 + if (modifier.type === "class") {
8184 + classCount++;
8185 + }
8186 + if (classCount > 1){
8187 + reporter.report("Adjoining classes: "+selectors[i].text, part.line, part.col, rule);
8188 + }
8189 + }
8190 + }
8191 + }
8192 + }
8193 + });
8194 + }
8195 +
8196 + });
8197 +
8198 + /*
8199 + * Rule: Don't use width or height when using padding or border.
8200 + */
8201 + CSSLint.addRule({
8202 +
8203 + // rule information
8204 + id: "box-model",
8205 + name: "Beware of broken box size",
8206 + desc: "Don't use width or height when using padding or border.",
8207 + url: "https://github.com/CSSLint/csslint/wiki/Beware-of-box-model-size",
8208 + browsers: "All",
8209 +
8210 + // initialization
8211 + init: function(parser, reporter) {
8212 + "use strict";
8213 + var rule = this,
8214 + widthProperties = {
8215 + border: 1,
8216 + "border-left": 1,
8217 + "border-right": 1,
8218 + padding: 1,
8219 + "padding-left": 1,
8220 + "padding-right": 1
8221 + },
8222 + heightProperties = {
8223 + border: 1,
8224 + "border-bottom": 1,
8225 + "border-top": 1,
8226 + padding: 1,
8227 + "padding-bottom": 1,
8228 + "padding-top": 1
8229 + },
8230 + properties,
8231 + boxSizing = false;
8232 +
8233 + function startRule() {
8234 + properties = {};
8235 + boxSizing = false;
8236 + }
8237 +
8238 + function endRule() {
8239 + var prop, value;
8240 +
8241 + if (!boxSizing) {
8242 + if (properties.height) {
8243 + for (prop in heightProperties) {
8244 + if (heightProperties.hasOwnProperty(prop) && properties[prop]) {
8245 + value = properties[prop].value;
8246 + // special case for padding
8247 + if (!(prop === "padding" && value.parts.length === 2 && value.parts[0].value === 0)) {
8248 + reporter.report("Using height with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
8249 + }
8250 + }
8251 + }
8252 + }
8253 +
8254 + if (properties.width) {
8255 + for (prop in widthProperties) {
8256 + if (widthProperties.hasOwnProperty(prop) && properties[prop]) {
8257 + value = properties[prop].value;
8258 +
8259 + if (!(prop === "padding" && value.parts.length === 2 && value.parts[1].value === 0)) {
8260 + reporter.report("Using width with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
8261 + }
8262 + }
8263 + }
8264 + }
8265 + }
8266 + }
8267 +
8268 + parser.addListener("startrule", startRule);
8269 + parser.addListener("startfontface", startRule);
8270 + parser.addListener("startpage", startRule);
8271 + parser.addListener("startpagemargin", startRule);
8272 + parser.addListener("startkeyframerule", startRule);
8273 + parser.addListener("startviewport", startRule);
8274 +
8275 + parser.addListener("property", function(event) {
8276 + var name = event.property.text.toLowerCase();
8277 +
8278 + if (heightProperties[name] || widthProperties[name]) {
8279 + if (!/^0\S*$/.test(event.value) && !(name === "border" && event.value.toString() === "none")) {
8280 + properties[name] = {
8281 + line: event.property.line,
8282 + col: event.property.col,
8283 + value: event.value
8284 + };
8285 + }
8286 + } else {
8287 + if (/^(width|height)/i.test(name) && /^(length|percentage)/.test(event.value.parts[0].type)) {
8288 + properties[name] = 1;
8289 + } else if (name === "box-sizing") {
8290 + boxSizing = true;
8291 + }
8292 + }
8293 +
8294 + });
8295 +
8296 + parser.addListener("endrule", endRule);
8297 + parser.addListener("endfontface", endRule);
8298 + parser.addListener("endpage", endRule);
8299 + parser.addListener("endpagemargin", endRule);
8300 + parser.addListener("endkeyframerule", endRule);
8301 + parser.addListener("endviewport", endRule);
8302 + }
8303 +
8304 + });
8305 +
8306 + /*
8307 + * Rule: box-sizing doesn't work in IE6 and IE7.
8308 + */
8309 +
8310 + CSSLint.addRule({
8311 +
8312 + // rule information
8313 + id: "box-sizing",
8314 + name: "Disallow use of box-sizing",
8315 + desc: "The box-sizing properties isn't supported in IE6 and IE7.",
8316 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-box-sizing",
8317 + browsers: "IE6, IE7",
8318 + tags: ["Compatibility"],
8319 +
8320 + // initialization
8321 + init: function(parser, reporter) {
8322 + "use strict";
8323 + var rule = this;
8324 +
8325 + parser.addListener("property", function(event) {
8326 + var name = event.property.text.toLowerCase();
8327 +
8328 + if (name === "box-sizing") {
8329 + reporter.report("The box-sizing property isn't supported in IE6 and IE7.", event.line, event.col, rule);
8330 + }
8331 + });
8332 + }
8333 +
8334 + });
8335 +
8336 + /*
8337 + * Rule: Use the bulletproof @font-face syntax to avoid 404's in old IE
8338 + * (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax)
8339 + */
8340 +
8341 + CSSLint.addRule({
8342 +
8343 + // rule information
8344 + id: "bulletproof-font-face",
8345 + name: "Use the bulletproof @font-face syntax",
8346 + desc: "Use the bulletproof @font-face syntax to avoid 404's in old IE (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax).",
8347 + url: "https://github.com/CSSLint/csslint/wiki/Bulletproof-font-face",
8348 + browsers: "All",
8349 +
8350 + // initialization
8351 + init: function(parser, reporter) {
8352 + "use strict";
8353 + var rule = this,
8354 + fontFaceRule = false,
8355 + firstSrc = true,
8356 + ruleFailed = false,
8357 + line, col;
8358 +
8359 + // Mark the start of a @font-face declaration so we only test properties inside it
8360 + parser.addListener("startfontface", function() {
8361 + fontFaceRule = true;
8362 + });
8363 +
8364 + parser.addListener("property", function(event) {
8365 + // If we aren't inside an @font-face declaration then just return
8366 + if (!fontFaceRule) {
8367 + return;
8368 + }
8369 +
8370 + var propertyName = event.property.toString().toLowerCase(),
8371 + value = event.value.toString();
8372 +
8373 + // Set the line and col numbers for use in the endfontface listener
8374 + line = event.line;
8375 + col = event.col;
8376 +
8377 + // This is the property that we care about, we can ignore the rest
8378 + if (propertyName === "src") {
8379 + var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i;
8380 +
8381 + // We need to handle the advanced syntax with two src properties
8382 + if (!value.match(regex) && firstSrc) {
8383 + ruleFailed = true;
8384 + firstSrc = false;
8385 + } else if (value.match(regex) && !firstSrc) {
8386 + ruleFailed = false;
8387 + }
8388 + }
8389 +
8390 +
8391 + });
8392 +
8393 + // Back to normal rules that we don't need to test
8394 + parser.addListener("endfontface", function() {
8395 + fontFaceRule = false;
8396 +
8397 + if (ruleFailed) {
8398 + reporter.report("@font-face declaration doesn't follow the fontspring bulletproof syntax.", line, col, rule);
8399 + }
8400 + });
8401 + }
8402 + });
8403 +
8404 + /*
8405 + * Rule: Include all compatible vendor prefixes to reach a wider
8406 + * range of users.
8407 + */
8408 +
8409 + CSSLint.addRule({
8410 +
8411 + // rule information
8412 + id: "compatible-vendor-prefixes",
8413 + name: "Require compatible vendor prefixes",
8414 + desc: "Include all compatible vendor prefixes to reach a wider range of users.",
8415 + url: "https://github.com/CSSLint/csslint/wiki/Require-compatible-vendor-prefixes",
8416 + browsers: "All",
8417 +
8418 + // initialization
8419 + init: function (parser, reporter) {
8420 + "use strict";
8421 + var rule = this,
8422 + compatiblePrefixes,
8423 + properties,
8424 + prop,
8425 + variations,
8426 + prefixed,
8427 + i,
8428 + len,
8429 + inKeyFrame = false,
8430 + arrayPush = Array.prototype.push,
8431 + applyTo = [];
8432 +
8433 + // See http://peter.sh/experiments/vendor-prefixed-css-property-overview/ for details
8434 + compatiblePrefixes = {
8435 + "animation" : "webkit",
8436 + "animation-delay" : "webkit",
8437 + "animation-direction" : "webkit",
8438 + "animation-duration" : "webkit",
8439 + "animation-fill-mode" : "webkit",
8440 + "animation-iteration-count" : "webkit",
8441 + "animation-name" : "webkit",
8442 + "animation-play-state" : "webkit",
8443 + "animation-timing-function" : "webkit",
8444 + "appearance" : "webkit moz",
8445 + "border-end" : "webkit moz",
8446 + "border-end-color" : "webkit moz",
8447 + "border-end-style" : "webkit moz",
8448 + "border-end-width" : "webkit moz",
8449 + "border-image" : "webkit moz o",
8450 + "border-radius" : "webkit",
8451 + "border-start" : "webkit moz",
8452 + "border-start-color" : "webkit moz",
8453 + "border-start-style" : "webkit moz",
8454 + "border-start-width" : "webkit moz",
8455 + "box-align" : "webkit moz ms",
8456 + "box-direction" : "webkit moz ms",
8457 + "box-flex" : "webkit moz ms",
8458 + "box-lines" : "webkit ms",
8459 + "box-ordinal-group" : "webkit moz ms",
8460 + "box-orient" : "webkit moz ms",
8461 + "box-pack" : "webkit moz ms",
8462 + "box-sizing" : "",
8463 + "box-shadow" : "",
8464 + "column-count" : "webkit moz ms",
8465 + "column-gap" : "webkit moz ms",
8466 + "column-rule" : "webkit moz ms",
8467 + "column-rule-color" : "webkit moz ms",
8468 + "column-rule-style" : "webkit moz ms",
8469 + "column-rule-width" : "webkit moz ms",
8470 + "column-width" : "webkit moz ms",
8471 + "hyphens" : "epub moz",
8472 + "line-break" : "webkit ms",
8473 + "margin-end" : "webkit moz",
8474 + "margin-start" : "webkit moz",
8475 + "marquee-speed" : "webkit wap",
8476 + "marquee-style" : "webkit wap",
8477 + "padding-end" : "webkit moz",
8478 + "padding-start" : "webkit moz",
8479 + "tab-size" : "moz o",
8480 + "text-size-adjust" : "webkit ms",
8481 + "transform" : "webkit ms",
8482 + "transform-origin" : "webkit ms",
8483 + "transition" : "",
8484 + "transition-delay" : "",
8485 + "transition-duration" : "",
8486 + "transition-property" : "",
8487 + "transition-timing-function" : "",
8488 + "user-modify" : "webkit moz",
8489 + "user-select" : "webkit moz ms",
8490 + "word-break" : "epub ms",
8491 + "writing-mode" : "epub ms"
8492 + };
8493 +
8494 +
8495 + for (prop in compatiblePrefixes) {
8496 + if (compatiblePrefixes.hasOwnProperty(prop)) {
8497 + variations = [];
8498 + prefixed = compatiblePrefixes[prop].split(" ");
8499 + for (i = 0, len = prefixed.length; i < len; i++) {
8500 + variations.push("-" + prefixed[i] + "-" + prop);
8501 + }
8502 + compatiblePrefixes[prop] = variations;
8503 + arrayPush.apply(applyTo, variations);
8504 + }
8505 + }
8506 +
8507 + parser.addListener("startrule", function () {
8508 + properties = [];
8509 + });
8510 +
8511 + parser.addListener("startkeyframes", function (event) {
8512 + inKeyFrame = event.prefix || true;
8513 + });
8514 +
8515 + parser.addListener("endkeyframes", function () {
8516 + inKeyFrame = false;
8517 + });
8518 +
8519 + parser.addListener("property", function (event) {
8520 + var name = event.property;
8521 + if (CSSLint.Util.indexOf(applyTo, name.text) > -1) {
8522 +
8523 + // e.g., -moz-transform is okay to be alone in @-moz-keyframes
8524 + if (!inKeyFrame || typeof inKeyFrame !== "string" ||
8525 + name.text.indexOf("-" + inKeyFrame + "-") !== 0) {
8526 + properties.push(name);
8527 + }
8528 + }
8529 + });
8530 +
8531 + parser.addListener("endrule", function () {
8532 + if (!properties.length) {
8533 + return;
8534 + }
8535 +
8536 + var propertyGroups = {},
8537 + i,
8538 + len,
8539 + name,
8540 + prop,
8541 + variations,
8542 + value,
8543 + full,
8544 + actual,
8545 + item,
8546 + propertiesSpecified;
8547 +
8548 + for (i = 0, len = properties.length; i < len; i++) {
8549 + name = properties[i];
8550 +
8551 + for (prop in compatiblePrefixes) {
8552 + if (compatiblePrefixes.hasOwnProperty(prop)) {
8553 + variations = compatiblePrefixes[prop];
8554 + if (CSSLint.Util.indexOf(variations, name.text) > -1) {
8555 + if (!propertyGroups[prop]) {
8556 + propertyGroups[prop] = {
8557 + full: variations.slice(0),
8558 + actual: [],
8559 + actualNodes: []
8560 + };
8561 + }
8562 + if (CSSLint.Util.indexOf(propertyGroups[prop].actual, name.text) === -1) {
8563 + propertyGroups[prop].actual.push(name.text);
8564 + propertyGroups[prop].actualNodes.push(name);
8565 + }
8566 + }
8567 + }
8568 + }
8569 + }
8570 +
8571 + for (prop in propertyGroups) {
8572 + if (propertyGroups.hasOwnProperty(prop)) {
8573 + value = propertyGroups[prop];
8574 + full = value.full;
8575 + actual = value.actual;
8576 +
8577 + if (full.length > actual.length) {
8578 + for (i = 0, len = full.length; i < len; i++) {
8579 + item = full[i];
8580 + if (CSSLint.Util.indexOf(actual, item) === -1) {
8581 + propertiesSpecified = (actual.length === 1) ? actual[0] : (actual.length === 2) ? actual.join(" and ") : actual.join(", ");
8582 + reporter.report("The property " + item + " is compatible with " + propertiesSpecified + " and should be included as well.", value.actualNodes[0].line, value.actualNodes[0].col, rule);
8583 + }
8584 + }
8585 +
8586 + }
8587 + }
8588 + }
8589 + });
8590 + }
8591 + });
8592 +
8593 + /*
8594 + * Rule: Certain properties don't play well with certain display values.
8595 + * - float should not be used with inline-block
8596 + * - height, width, margin-top, margin-bottom, float should not be used with inline
8597 + * - vertical-align should not be used with block
8598 + * - margin, float should not be used with table-*
8599 + */
8600 +
8601 + CSSLint.addRule({
8602 +
8603 + // rule information
8604 + id: "display-property-grouping",
8605 + name: "Require properties appropriate for display",
8606 + desc: "Certain properties shouldn't be used with certain display property values.",
8607 + url: "https://github.com/CSSLint/csslint/wiki/Require-properties-appropriate-for-display",
8608 + browsers: "All",
8609 +
8610 + // initialization
8611 + init: function(parser, reporter) {
8612 + "use strict";
8613 + var rule = this;
8614 +
8615 + var propertiesToCheck = {
8616 + display: 1,
8617 + "float": "none",
8618 + height: 1,
8619 + width: 1,
8620 + margin: 1,
8621 + "margin-left": 1,
8622 + "margin-right": 1,
8623 + "margin-bottom": 1,
8624 + "margin-top": 1,
8625 + padding: 1,
8626 + "padding-left": 1,
8627 + "padding-right": 1,
8628 + "padding-bottom": 1,
8629 + "padding-top": 1,
8630 + "vertical-align": 1
8631 + },
8632 + properties;
8633 +
8634 + function reportProperty(name, display, msg) {
8635 + if (properties[name]) {
8636 + if (typeof propertiesToCheck[name] !== "string" || properties[name].value.toLowerCase() !== propertiesToCheck[name]) {
8637 + reporter.report(msg || name + " can't be used with display: " + display + ".", properties[name].line, properties[name].col, rule);
8638 + }
8639 + }
8640 + }
8641 +
8642 + function startRule() {
8643 + properties = {};
8644 + }
8645 +
8646 + function endRule() {
8647 +
8648 + var display = properties.display ? properties.display.value : null;
8649 + if (display) {
8650 + switch (display) {
8651 +
8652 + case "inline":
8653 + // height, width, margin-top, margin-bottom, float should not be used with inline
8654 + reportProperty("height", display);
8655 + reportProperty("width", display);
8656 + reportProperty("margin", display);
8657 + reportProperty("margin-top", display);
8658 + reportProperty("margin-bottom", display);
8659 + reportProperty("float", display, "display:inline has no effect on floated elements (but may be used to fix the IE6 double-margin bug).");
8660 + break;
8661 +
8662 + case "block":
8663 + // vertical-align should not be used with block
8664 + reportProperty("vertical-align", display);
8665 + break;
8666 +
8667 + case "inline-block":
8668 + // float should not be used with inline-block
8669 + reportProperty("float", display);
8670 + break;
8671 +
8672 + default:
8673 + // margin, float should not be used with table
8674 + if (display.indexOf("table-") === 0) {
8675 + reportProperty("margin", display);
8676 + reportProperty("margin-left", display);
8677 + reportProperty("margin-right", display);
8678 + reportProperty("margin-top", display);
8679 + reportProperty("margin-bottom", display);
8680 + reportProperty("float", display);
8681 + }
8682 +
8683 + // otherwise do nothing
8684 + }
8685 + }
8686 +
8687 + }
8688 +
8689 + parser.addListener("startrule", startRule);
8690 + parser.addListener("startfontface", startRule);
8691 + parser.addListener("startkeyframerule", startRule);
8692 + parser.addListener("startpagemargin", startRule);
8693 + parser.addListener("startpage", startRule);
8694 + parser.addListener("startviewport", startRule);
8695 +
8696 + parser.addListener("property", function(event) {
8697 + var name = event.property.text.toLowerCase();
8698 +
8699 + if (propertiesToCheck[name]) {
8700 + properties[name] = {
8701 + value: event.value.text,
8702 + line: event.property.line,
8703 + col: event.property.col
8704 + };
8705 + }
8706 + });
8707 +
8708 + parser.addListener("endrule", endRule);
8709 + parser.addListener("endfontface", endRule);
8710 + parser.addListener("endkeyframerule", endRule);
8711 + parser.addListener("endpagemargin", endRule);
8712 + parser.addListener("endpage", endRule);
8713 + parser.addListener("endviewport", endRule);
8714 +
8715 + }
8716 +
8717 + });
8718 +
8719 + /*
8720 + * Rule: Disallow duplicate background-images (using url).
8721 + */
8722 +
8723 + CSSLint.addRule({
8724 +
8725 + // rule information
8726 + id: "duplicate-background-images",
8727 + name: "Disallow duplicate background images",
8728 + desc: "Every background-image should be unique. Use a common class for e.g. sprites.",
8729 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-background-images",
8730 + browsers: "All",
8731 +
8732 + // initialization
8733 + init: function(parser, reporter) {
8734 + "use strict";
8735 + var rule = this,
8736 + stack = {};
8737 +
8738 + parser.addListener("property", function(event) {
8739 + var name = event.property.text,
8740 + value = event.value,
8741 + i, len;
8742 +
8743 + if (name.match(/background/i)) {
8744 + for (i=0, len=value.parts.length; i < len; i++) {
8745 + if (value.parts[i].type === "uri") {
8746 + if (typeof stack[value.parts[i].uri] === "undefined") {
8747 + stack[value.parts[i].uri] = event;
8748 + } else {
8749 + reporter.report("Background image '" + value.parts[i].uri + "' was used multiple times, first declared at line " + stack[value.parts[i].uri].line + ", col " + stack[value.parts[i].uri].col + ".", event.line, event.col, rule);
8750 + }
8751 + }
8752 + }
8753 + }
8754 + });
8755 + }
8756 + });
8757 +
8758 + /*
8759 + * Rule: Duplicate properties must appear one after the other. If an already-defined
8760 + * property appears somewhere else in the rule, then it's likely an error.
8761 + */
8762 +
8763 + CSSLint.addRule({
8764 +
8765 + // rule information
8766 + id: "duplicate-properties",
8767 + name: "Disallow duplicate properties",
8768 + desc: "Duplicate properties must appear one after the other.",
8769 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-properties",
8770 + browsers: "All",
8771 +
8772 + // initialization
8773 + init: function(parser, reporter) {
8774 + "use strict";
8775 + var rule = this,
8776 + properties,
8777 + lastProperty;
8778 +
8779 + function startRule() {
8780 + properties = {};
8781 + }
8782 +
8783 + parser.addListener("startrule", startRule);
8784 + parser.addListener("startfontface", startRule);
8785 + parser.addListener("startpage", startRule);
8786 + parser.addListener("startpagemargin", startRule);
8787 + parser.addListener("startkeyframerule", startRule);
8788 + parser.addListener("startviewport", startRule);
8789 +
8790 + parser.addListener("property", function(event) {
8791 + var property = event.property,
8792 + name = property.text.toLowerCase();
8793 +
8794 + if (properties[name] && (lastProperty !== name || properties[name] === event.value.text)) {
8795 + reporter.report("Duplicate property '" + event.property + "' found.", event.line, event.col, rule);
8796 + }
8797 +
8798 + properties[name] = event.value.text;
8799 + lastProperty = name;
8800 +
8801 + });
8802 +
8803 +
8804 + }
8805 +
8806 + });
8807 +
8808 + /*
8809 + * Rule: Style rules without any properties defined should be removed.
8810 + */
8811 +
8812 + CSSLint.addRule({
8813 +
8814 + // rule information
8815 + id: "empty-rules",
8816 + name: "Disallow empty rules",
8817 + desc: "Rules without any properties specified should be removed.",
8818 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-empty-rules",
8819 + browsers: "All",
8820 +
8821 + // initialization
8822 + init: function(parser, reporter) {
8823 + "use strict";
8824 + var rule = this,
8825 + count = 0;
8826 +
8827 + parser.addListener("startrule", function() {
8828 + count=0;
8829 + });
8830 +
8831 + parser.addListener("property", function() {
8832 + count++;
8833 + });
8834 +
8835 + parser.addListener("endrule", function(event) {
8836 + var selectors = event.selectors;
8837 + if (count === 0) {
8838 + reporter.report("Rule is empty.", selectors[0].line, selectors[0].col, rule);
8839 + }
8840 + });
8841 + }
8842 +
8843 + });
8844 +
8845 + /*
8846 + * Rule: There should be no syntax errors. (Duh.)
8847 + */
8848 +
8849 + CSSLint.addRule({
8850 +
8851 + // rule information
8852 + id: "errors",
8853 + name: "Parsing Errors",
8854 + desc: "This rule looks for recoverable syntax errors.",
8855 + browsers: "All",
8856 +
8857 + // initialization
8858 + init: function(parser, reporter) {
8859 + "use strict";
8860 + var rule = this;
8861 +
8862 + parser.addListener("error", function(event) {
8863 + reporter.error(event.message, event.line, event.col, rule);
8864 + });
8865 +
8866 + }
8867 +
8868 + });
8869 +
8870 + CSSLint.addRule({
8871 +
8872 + // rule information
8873 + id: "fallback-colors",
8874 + name: "Require fallback colors",
8875 + desc: "For older browsers that don't support RGBA, HSL, or HSLA, provide a fallback color.",
8876 + url: "https://github.com/CSSLint/csslint/wiki/Require-fallback-colors",
8877 + browsers: "IE6,IE7,IE8",
8878 +
8879 + // initialization
8880 + init: function(parser, reporter) {
8881 + "use strict";
8882 + var rule = this,
8883 + lastProperty,
8884 + propertiesToCheck = {
8885 + color: 1,
8886 + background: 1,
8887 + "border-color": 1,
8888 + "border-top-color": 1,
8889 + "border-right-color": 1,
8890 + "border-bottom-color": 1,
8891 + "border-left-color": 1,
8892 + border: 1,
8893 + "border-top": 1,
8894 + "border-right": 1,
8895 + "border-bottom": 1,
8896 + "border-left": 1,
8897 + "background-color": 1
8898 + };
8899 +
8900 + function startRule() {
8901 + lastProperty = null;
8902 + }
8903 +
8904 + parser.addListener("startrule", startRule);
8905 + parser.addListener("startfontface", startRule);
8906 + parser.addListener("startpage", startRule);
8907 + parser.addListener("startpagemargin", startRule);
8908 + parser.addListener("startkeyframerule", startRule);
8909 + parser.addListener("startviewport", startRule);
8910 +
8911 + parser.addListener("property", function(event) {
8912 + var property = event.property,
8913 + name = property.text.toLowerCase(),
8914 + parts = event.value.parts,
8915 + i = 0,
8916 + colorType = "",
8917 + len = parts.length;
8918 +
8919 + if (propertiesToCheck[name]) {
8920 + while (i < len) {
8921 + if (parts[i].type === "color") {
8922 + if ("alpha" in parts[i] || "hue" in parts[i]) {
8923 +
8924 + if (/([^\)]+)\(/.test(parts[i])) {
8925 + colorType = RegExp.$1.toUpperCase();
8926 + }
8927 +
8928 + if (!lastProperty || (lastProperty.property.text.toLowerCase() !== name || lastProperty.colorType !== "compat")) {
8929 + reporter.report("Fallback " + name + " (hex or RGB) should precede " + colorType + " " + name + ".", event.line, event.col, rule);
8930 + }
8931 + } else {
8932 + event.colorType = "compat";
8933 + }
8934 + }
8935 +
8936 + i++;
8937 + }
8938 + }
8939 +
8940 + lastProperty = event;
8941 + });
8942 +
8943 + }
8944 +
8945 + });
8946 +
8947 + /*
8948 + * Rule: You shouldn't use more than 10 floats. If you do, there's probably
8949 + * room for some abstraction.
8950 + */
8951 +
8952 + CSSLint.addRule({
8953 +
8954 + // rule information
8955 + id: "floats",
8956 + name: "Disallow too many floats",
8957 + desc: "This rule tests if the float property is used too many times",
8958 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-too-many-floats",
8959 + browsers: "All",
8960 +
8961 + // initialization
8962 + init: function(parser, reporter) {
8963 + "use strict";
8964 + var rule = this;
8965 + var count = 0;
8966 +
8967 + // count how many times "float" is used
8968 + parser.addListener("property", function(event) {
8969 + if (event.property.text.toLowerCase() === "float" &&
8970 + event.value.text.toLowerCase() !== "none") {
8971 + count++;
8972 + }
8973 + });
8974 +
8975 + // report the results
8976 + parser.addListener("endstylesheet", function() {
8977 + reporter.stat("floats", count);
8978 + if (count >= 10) {
8979 + reporter.rollupWarn("Too many floats (" + count + "), you're probably using them for layout. Consider using a grid system instead.", rule);
8980 + }
8981 + });
8982 + }
8983 +
8984 + });
8985 +
8986 + /*
8987 + * Rule: Avoid too many @font-face declarations in the same stylesheet.
8988 + */
8989 +
8990 + CSSLint.addRule({
8991 +
8992 + // rule information
8993 + id: "font-faces",
8994 + name: "Don't use too many web fonts",
8995 + desc: "Too many different web fonts in the same stylesheet.",
8996 + url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-web-fonts",
8997 + browsers: "All",
8998 +
8999 + // initialization
9000 + init: function(parser, reporter) {
9001 + "use strict";
9002 + var rule = this,
9003 + count = 0;
9004 +
9005 +
9006 + parser.addListener("startfontface", function() {
9007 + count++;
9008 + });
9009 +
9010 + parser.addListener("endstylesheet", function() {
9011 + if (count > 5) {
9012 + reporter.rollupWarn("Too many @font-face declarations (" + count + ").", rule);
9013 + }
9014 + });
9015 + }
9016 +
9017 + });
9018 +
9019 + /*
9020 + * Rule: You shouldn't need more than 9 font-size declarations.
9021 + */
9022 +
9023 + CSSLint.addRule({
9024 +
9025 + // rule information
9026 + id: "font-sizes",
9027 + name: "Disallow too many font sizes",
9028 + desc: "Checks the number of font-size declarations.",
9029 + url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-font-size-declarations",
9030 + browsers: "All",
9031 +
9032 + // initialization
9033 + init: function(parser, reporter) {
9034 + "use strict";
9035 + var rule = this,
9036 + count = 0;
9037 +
9038 + // check for use of "font-size"
9039 + parser.addListener("property", function(event) {
9040 + if (event.property.toString() === "font-size") {
9041 + count++;
9042 + }
9043 + });
9044 +
9045 + // report the results
9046 + parser.addListener("endstylesheet", function() {
9047 + reporter.stat("font-sizes", count);
9048 + if (count >= 10) {
9049 + reporter.rollupWarn("Too many font-size declarations (" + count + "), abstraction needed.", rule);
9050 + }
9051 + });
9052 + }
9053 +
9054 + });
9055 +
9056 + /*
9057 + * Rule: When using a vendor-prefixed gradient, make sure to use them all.
9058 + */
9059 +
9060 + CSSLint.addRule({
9061 +
9062 + // rule information
9063 + id: "gradients",
9064 + name: "Require all gradient definitions",
9065 + desc: "When using a vendor-prefixed gradient, make sure to use them all.",
9066 + url: "https://github.com/CSSLint/csslint/wiki/Require-all-gradient-definitions",
9067 + browsers: "All",
9068 +
9069 + // initialization
9070 + init: function(parser, reporter) {
9071 + "use strict";
9072 + var rule = this,
9073 + gradients;
9074 +
9075 + parser.addListener("startrule", function() {
9076 + gradients = {
9077 + moz: 0,
9078 + webkit: 0,
9079 + oldWebkit: 0,
9080 + o: 0
9081 + };
9082 + });
9083 +
9084 + parser.addListener("property", function(event) {
9085 +
9086 + if (/\-(moz|o|webkit)(?:\-(?:linear|radial))\-gradient/i.test(event.value)) {
9087 + gradients[RegExp.$1] = 1;
9088 + } else if (/\-webkit\-gradient/i.test(event.value)) {
9089 + gradients.oldWebkit = 1;
9090 + }
9091 +
9092 + });
9093 +
9094 + parser.addListener("endrule", function(event) {
9095 + var missing = [];
9096 +
9097 + if (!gradients.moz) {
9098 + missing.push("Firefox 3.6+");
9099 + }
9100 +
9101 + if (!gradients.webkit) {
9102 + missing.push("Webkit (Safari 5+, Chrome)");
9103 + }
9104 +
9105 + if (!gradients.oldWebkit) {
9106 + missing.push("Old Webkit (Safari 4+, Chrome)");
9107 + }
9108 +
9109 + if (!gradients.o) {
9110 + missing.push("Opera 11.1+");
9111 + }
9112 +
9113 + if (missing.length && missing.length < 4) {
9114 + reporter.report("Missing vendor-prefixed CSS gradients for " + missing.join(", ") + ".", event.selectors[0].line, event.selectors[0].col, rule);
9115 + }
9116 +
9117 + });
9118 +
9119 + }
9120 +
9121 + });
9122 +
9123 + /*
9124 + * Rule: Don't use IDs for selectors.
9125 + */
9126 +
9127 + CSSLint.addRule({
9128 +
9129 + // rule information
9130 + id: "ids",
9131 + name: "Disallow IDs in selectors",
9132 + desc: "Selectors should not contain IDs.",
9133 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-IDs-in-selectors",
9134 + browsers: "All",
9135 +
9136 + // initialization
9137 + init: function(parser, reporter) {
9138 + "use strict";
9139 + var rule = this;
9140 + parser.addListener("startrule", function(event) {
9141 + var selectors = event.selectors,
9142 + selector,
9143 + part,
9144 + modifier,
9145 + idCount,
9146 + i, j, k;
9147 +
9148 + for (i=0; i < selectors.length; i++) {
9149 + selector = selectors[i];
9150 + idCount = 0;
9151 +
9152 + for (j=0; j < selector.parts.length; j++) {
9153 + part = selector.parts[j];
9154 + if (part.type === parser.SELECTOR_PART_TYPE) {
9155 + for (k=0; k < part.modifiers.length; k++) {
9156 + modifier = part.modifiers[k];
9157 + if (modifier.type === "id") {
9158 + idCount++;
9159 + }
9160 + }
9161 + }
9162 + }
9163 +
9164 + if (idCount === 1) {
9165 + reporter.report("Don't use IDs in selectors.", selector.line, selector.col, rule);
9166 + } else if (idCount > 1) {
9167 + reporter.report(idCount + " IDs in the selector, really?", selector.line, selector.col, rule);
9168 + }
9169 + }
9170 +
9171 + });
9172 + }
9173 +
9174 + });
9175 +
9176 + /*
9177 + * Rule: IE6-9 supports up to 31 stylesheet import.
9178 + * Reference:
9179 + * http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/internet-explorer-stylesheet-rule-selector-import-sheet-limit-maximum.aspx
9180 + */
9181 +
9182 + CSSLint.addRule({
9183 +
9184 + // rule information
9185 + id: "import-ie-limit",
9186 + name: "@import limit on IE6-IE9",
9187 + desc: "IE6-9 supports up to 31 @import per stylesheet",
9188 + browsers: "IE6, IE7, IE8, IE9",
9189 +
9190 + // initialization
9191 + init: function(parser, reporter) {
9192 + "use strict";
9193 + var rule = this,
9194 + MAX_IMPORT_COUNT = 31,
9195 + count = 0;
9196 +
9197 + function startPage() {
9198 + count = 0;
9199 + }
9200 +
9201 + parser.addListener("startpage", startPage);
9202 +
9203 + parser.addListener("import", function() {
9204 + count++;
9205 + });
9206 +
9207 + parser.addListener("endstylesheet", function() {
9208 + if (count > MAX_IMPORT_COUNT) {
9209 + reporter.rollupError(
9210 + "Too many @import rules (" + count + "). IE6-9 supports up to 31 import per stylesheet.",
9211 + rule
9212 + );
9213 + }
9214 + });
9215 + }
9216 +
9217 + });
9218 +
9219 + /*
9220 + * Rule: Don't use @import, use <link> instead.
9221 + */
9222 +
9223 + CSSLint.addRule({
9224 +
9225 + // rule information
9226 + id: "import",
9227 + name: "Disallow @import",
9228 + desc: "Don't use @import, use <link> instead.",
9229 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-%40import",
9230 + browsers: "All",
9231 +
9232 + // initialization
9233 + init: function(parser, reporter) {
9234 + "use strict";
9235 + var rule = this;
9236 +
9237 + parser.addListener("import", function(event) {
9238 + reporter.report("@import prevents parallel downloads, use <link> instead.", event.line, event.col, rule);
9239 + });
9240 +
9241 + }
9242 +
9243 + });
9244 +
9245 + /*
9246 + * Rule: Make sure !important is not overused, this could lead to specificity
9247 + * war. Display a warning on !important declarations, an error if it's
9248 + * used more at least 10 times.
9249 + */
9250 +
9251 + CSSLint.addRule({
9252 +
9253 + // rule information
9254 + id: "important",
9255 + name: "Disallow !important",
9256 + desc: "Be careful when using !important declaration",
9257 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-%21important",
9258 + browsers: "All",
9259 +
9260 + // initialization
9261 + init: function(parser, reporter) {
9262 + "use strict";
9263 + var rule = this,
9264 + count = 0;
9265 +
9266 + // warn that important is used and increment the declaration counter
9267 + parser.addListener("property", function(event) {
9268 + if (event.important === true) {
9269 + count++;
9270 + reporter.report("Use of !important", event.line, event.col, rule);
9271 + }
9272 + });
9273 +
9274 + // if there are more than 10, show an error
9275 + parser.addListener("endstylesheet", function() {
9276 + reporter.stat("important", count);
9277 + if (count >= 10) {
9278 + reporter.rollupWarn("Too many !important declarations (" + count + "), try to use less than 10 to avoid specificity issues.", rule);
9279 + }
9280 + });
9281 + }
9282 +
9283 + });
9284 +
9285 + /*
9286 + * Rule: Properties should be known (listed in CSS3 specification) or
9287 + * be a vendor-prefixed property.
9288 + */
9289 +
9290 + CSSLint.addRule({
9291 +
9292 + // rule information
9293 + id: "known-properties",
9294 + name: "Require use of known properties",
9295 + desc: "Properties should be known (listed in CSS3 specification) or be a vendor-prefixed property.",
9296 + url: "https://github.com/CSSLint/csslint/wiki/Require-use-of-known-properties",
9297 + browsers: "All",
9298 +
9299 + // initialization
9300 + init: function(parser, reporter) {
9301 + "use strict";
9302 + var rule = this;
9303 +
9304 + parser.addListener("property", function(event) {
9305 +
9306 + // the check is handled entirely by the parser-lib (https://github.com/nzakas/parser-lib)
9307 + if (event.invalid) {
9308 + reporter.report(event.invalid.message, event.line, event.col, rule);
9309 + }
9310 +
9311 + });
9312 + }
9313 +
9314 + });
9315 +
9316 + /*
9317 + * Rule: All properties should be in alphabetical order.
9318 + */
9319 +
9320 + CSSLint.addRule({
9321 +
9322 + // rule information
9323 + id: "order-alphabetical",
9324 + name: "Alphabetical order",
9325 + desc: "Assure properties are in alphabetical order",
9326 + browsers: "All",
9327 +
9328 + // initialization
9329 + init: function(parser, reporter) {
9330 + "use strict";
9331 + var rule = this,
9332 + properties;
9333 +
9334 + var startRule = function () {
9335 + properties = [];
9336 + };
9337 +
9338 + var endRule = function(event) {
9339 + var currentProperties = properties.join(","),
9340 + expectedProperties = properties.sort().join(",");
9341 +
9342 + if (currentProperties !== expectedProperties) {
9343 + reporter.report("Rule doesn't have all its properties in alphabetical order.", event.line, event.col, rule);
9344 + }
9345 + };
9346 +
9347 + parser.addListener("startrule", startRule);
9348 + parser.addListener("startfontface", startRule);
9349 + parser.addListener("startpage", startRule);
9350 + parser.addListener("startpagemargin", startRule);
9351 + parser.addListener("startkeyframerule", startRule);
9352 + parser.addListener("startviewport", startRule);
9353 +
9354 + parser.addListener("property", function(event) {
9355 + var name = event.property.text,
9356 + lowerCasePrefixLessName = name.toLowerCase().replace(/^-.*?-/, "");
9357 +
9358 + properties.push(lowerCasePrefixLessName);
9359 + });
9360 +
9361 + parser.addListener("endrule", endRule);
9362 + parser.addListener("endfontface", endRule);
9363 + parser.addListener("endpage", endRule);
9364 + parser.addListener("endpagemargin", endRule);
9365 + parser.addListener("endkeyframerule", endRule);
9366 + parser.addListener("endviewport", endRule);
9367 + }
9368 +
9369 + });
9370 +
9371 + /*
9372 + * Rule: outline: none or outline: 0 should only be used in a :focus rule
9373 + * and only if there are other properties in the same rule.
9374 + */
9375 +
9376 + CSSLint.addRule({
9377 +
9378 + // rule information
9379 + id: "outline-none",
9380 + name: "Disallow outline: none",
9381 + desc: "Use of outline: none or outline: 0 should be limited to :focus rules.",
9382 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-outline%3Anone",
9383 + browsers: "All",
9384 + tags: ["Accessibility"],
9385 +
9386 + // initialization
9387 + init: function(parser, reporter) {
9388 + "use strict";
9389 + var rule = this,
9390 + lastRule;
9391 +
9392 + function startRule(event) {
9393 + if (event.selectors) {
9394 + lastRule = {
9395 + line: event.line,
9396 + col: event.col,
9397 + selectors: event.selectors,
9398 + propCount: 0,
9399 + outline: false
9400 + };
9401 + } else {
9402 + lastRule = null;
9403 + }
9404 + }
9405 +
9406 + function endRule() {
9407 + if (lastRule) {
9408 + if (lastRule.outline) {
9409 + if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") === -1) {
9410 + reporter.report("Outlines should only be modified using :focus.", lastRule.line, lastRule.col, rule);
9411 + } else if (lastRule.propCount === 1) {
9412 + reporter.report("Outlines shouldn't be hidden unless other visual changes are made.", lastRule.line, lastRule.col, rule);
9413 + }
9414 + }
9415 + }
9416 + }
9417 +
9418 + parser.addListener("startrule", startRule);
9419 + parser.addListener("startfontface", startRule);
9420 + parser.addListener("startpage", startRule);
9421 + parser.addListener("startpagemargin", startRule);
9422 + parser.addListener("startkeyframerule", startRule);
9423 + parser.addListener("startviewport", startRule);
9424 +
9425 + parser.addListener("property", function(event) {
9426 + var name = event.property.text.toLowerCase(),
9427 + value = event.value;
9428 +
9429 + if (lastRule) {
9430 + lastRule.propCount++;
9431 + if (name === "outline" && (value.toString() === "none" || value.toString() === "0")) {
9432 + lastRule.outline = true;
9433 + }
9434 + }
9435 +
9436 + });
9437 +
9438 + parser.addListener("endrule", endRule);
9439 + parser.addListener("endfontface", endRule);
9440 + parser.addListener("endpage", endRule);
9441 + parser.addListener("endpagemargin", endRule);
9442 + parser.addListener("endkeyframerule", endRule);
9443 + parser.addListener("endviewport", endRule);
9444 +
9445 + }
9446 +
9447 + });
9448 +
9449 + /*
9450 + * Rule: Don't use classes or IDs with elements (a.foo or a#foo).
9451 + */
9452 +
9453 + CSSLint.addRule({
9454 +
9455 + // rule information
9456 + id: "overqualified-elements",
9457 + name: "Disallow overqualified elements",
9458 + desc: "Don't use classes or IDs with elements (a.foo or a#foo).",
9459 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-overqualified-elements",
9460 + browsers: "All",
9461 +
9462 + // initialization
9463 + init: function(parser, reporter) {
9464 + "use strict";
9465 + var rule = this,
9466 + classes = {};
9467 +
9468 + parser.addListener("startrule", function(event) {
9469 + var selectors = event.selectors,
9470 + selector,
9471 + part,
9472 + modifier,
9473 + i, j, k;
9474 +
9475 + for (i=0; i < selectors.length; i++) {
9476 + selector = selectors[i];
9477 +
9478 + for (j=0; j < selector.parts.length; j++) {
9479 + part = selector.parts[j];
9480 + if (part.type === parser.SELECTOR_PART_TYPE) {
9481 + for (k=0; k < part.modifiers.length; k++) {
9482 + modifier = part.modifiers[k];
9483 + if (part.elementName && modifier.type === "id") {
9484 + reporter.report("Element (" + part + ") is overqualified, just use " + modifier + " without element name.", part.line, part.col, rule);
9485 + } else if (modifier.type === "class") {
9486 +
9487 + if (!classes[modifier]) {
9488 + classes[modifier] = [];
9489 + }
9490 + classes[modifier].push({
9491 + modifier: modifier,
9492 + part: part
9493 + });
9494 + }
9495 + }
9496 + }
9497 + }
9498 + }
9499 + });
9500 +
9501 + parser.addListener("endstylesheet", function() {
9502 +
9503 + var prop;
9504 + for (prop in classes) {
9505 + if (classes.hasOwnProperty(prop)) {
9506 +
9507 + // one use means that this is overqualified
9508 + if (classes[prop].length === 1 && classes[prop][0].part.elementName) {
9509 + reporter.report("Element (" + classes[prop][0].part + ") is overqualified, just use " + classes[prop][0].modifier + " without element name.", classes[prop][0].part.line, classes[prop][0].part.col, rule);
9510 + }
9511 + }
9512 + }
9513 + });
9514 + }
9515 +
9516 + });
9517 +
9518 + /*
9519 + * Rule: Headings (h1-h6) should not be qualified (namespaced).
9520 + */
9521 +
9522 + CSSLint.addRule({
9523 +
9524 + // rule information
9525 + id: "qualified-headings",
9526 + name: "Disallow qualified headings",
9527 + desc: "Headings should not be qualified (namespaced).",
9528 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-qualified-headings",
9529 + browsers: "All",
9530 +
9531 + // initialization
9532 + init: function(parser, reporter) {
9533 + "use strict";
9534 + var rule = this;
9535 +
9536 + parser.addListener("startrule", function(event) {
9537 + var selectors = event.selectors,
9538 + selector,
9539 + part,
9540 + i, j;
9541 +
9542 + for (i=0; i < selectors.length; i++) {
9543 + selector = selectors[i];
9544 +
9545 + for (j=0; j < selector.parts.length; j++) {
9546 + part = selector.parts[j];
9547 + if (part.type === parser.SELECTOR_PART_TYPE) {
9548 + if (part.elementName && /h[1-6]/.test(part.elementName.toString()) && j > 0) {
9549 + reporter.report("Heading (" + part.elementName + ") should not be qualified.", part.line, part.col, rule);
9550 + }
9551 + }
9552 + }
9553 + }
9554 + });
9555 + }
9556 +
9557 + });
9558 +
9559 + /*
9560 + * Rule: Selectors that look like regular expressions are slow and should be avoided.
9561 + */
9562 +
9563 + CSSLint.addRule({
9564 +
9565 + // rule information
9566 + id: "regex-selectors",
9567 + name: "Disallow selectors that look like regexs",
9568 + desc: "Selectors that look like regular expressions are slow and should be avoided.",
9569 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-selectors-that-look-like-regular-expressions",
9570 + browsers: "All",
9571 +
9572 + // initialization
9573 + init: function(parser, reporter) {
9574 + "use strict";
9575 + var rule = this;
9576 +
9577 + parser.addListener("startrule", function(event) {
9578 + var selectors = event.selectors,
9579 + selector,
9580 + part,
9581 + modifier,
9582 + i, j, k;
9583 +
9584 + for (i=0; i < selectors.length; i++) {
9585 + selector = selectors[i];
9586 + for (j=0; j < selector.parts.length; j++) {
9587 + part = selector.parts[j];
9588 + if (part.type === parser.SELECTOR_PART_TYPE) {
9589 + for (k=0; k < part.modifiers.length; k++) {
9590 + modifier = part.modifiers[k];
9591 + if (modifier.type === "attribute") {
9592 + if (/([~\|\^\$\*]=)/.test(modifier)) {
9593 + reporter.report("Attribute selectors with " + RegExp.$1 + " are slow!", modifier.line, modifier.col, rule);
9594 + }
9595 + }
9596 +
9597 + }
9598 + }
9599 + }
9600 + }
9601 + });
9602 + }
9603 +
9604 + });
9605 +
9606 + /*
9607 + * Rule: Total number of rules should not exceed x.
9608 + */
9609 +
9610 + CSSLint.addRule({
9611 +
9612 + // rule information
9613 + id: "rules-count",
9614 + name: "Rules Count",
9615 + desc: "Track how many rules there are.",
9616 + browsers: "All",
9617 +
9618 + // initialization
9619 + init: function(parser, reporter) {
9620 + "use strict";
9621 + var count = 0;
9622 +
9623 + // count each rule
9624 + parser.addListener("startrule", function() {
9625 + count++;
9626 + });
9627 +
9628 + parser.addListener("endstylesheet", function() {
9629 + reporter.stat("rule-count", count);
9630 + });
9631 + }
9632 +
9633 + });
9634 +
9635 + /*
9636 + * Rule: Warn people with approaching the IE 4095 limit
9637 + */
9638 +
9639 + CSSLint.addRule({
9640 +
9641 + // rule information
9642 + id: "selector-max-approaching",
9643 + name: "Warn when approaching the 4095 selector limit for IE",
9644 + desc: "Will warn when selector count is >= 3800 selectors.",
9645 + browsers: "IE",
9646 +
9647 + // initialization
9648 + init: function(parser, reporter) {
9649 + "use strict";
9650 + var rule = this, count = 0;
9651 +
9652 + parser.addListener("startrule", function(event) {
9653 + count += event.selectors.length;
9654 + });
9655 +
9656 + parser.addListener("endstylesheet", function() {
9657 + if (count >= 3800) {
9658 + reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule);
9659 + }
9660 + });
9661 + }
9662 +
9663 + });
9664 +
9665 + /*
9666 + * Rule: Warn people past the IE 4095 limit
9667 + */
9668 +
9669 + CSSLint.addRule({
9670 +
9671 + // rule information
9672 + id: "selector-max",
9673 + name: "Error when past the 4095 selector limit for IE",
9674 + desc: "Will error when selector count is > 4095.",
9675 + browsers: "IE",
9676 +
9677 + // initialization
9678 + init: function(parser, reporter) {
9679 + "use strict";
9680 + var rule = this, count = 0;
9681 +
9682 + parser.addListener("startrule", function(event) {
9683 + count += event.selectors.length;
9684 + });
9685 +
9686 + parser.addListener("endstylesheet", function() {
9687 + if (count > 4095) {
9688 + reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule);
9689 + }
9690 + });
9691 + }
9692 +
9693 + });
9694 +
9695 + /*
9696 + * Rule: Avoid new-line characters in selectors.
9697 + */
9698 +
9699 + CSSLint.addRule({
9700 +
9701 + // rule information
9702 + id: "selector-newline",
9703 + name: "Disallow new-line characters in selectors",
9704 + desc: "New-line characters in selectors are usually a forgotten comma and not a descendant combinator.",
9705 + browsers: "All",
9706 +
9707 + // initialization
9708 + init: function(parser, reporter) {
9709 + "use strict";
9710 + var rule = this;
9711 +
9712 + function startRule(event) {
9713 + var i, len, selector, p, n, pLen, part, part2, type, currentLine, nextLine,
9714 + selectors = event.selectors;
9715 +
9716 + for (i = 0, len = selectors.length; i < len; i++) {
9717 + selector = selectors[i];
9718 + for (p = 0, pLen = selector.parts.length; p < pLen; p++) {
9719 + for (n = p + 1; n < pLen; n++) {
9720 + part = selector.parts[p];
9721 + part2 = selector.parts[n];
9722 + type = part.type;
9723 + currentLine = part.line;
9724 + nextLine = part2.line;
9725 +
9726 + if (type === "descendant" && nextLine > currentLine) {
9727 + reporter.report("newline character found in selector (forgot a comma?)", currentLine, selectors[i].parts[0].col, rule);
9728 + }
9729 + }
9730 + }
9731 +
9732 + }
9733 + }
9734 +
9735 + parser.addListener("startrule", startRule);
9736 +
9737 + }
9738 + });
9739 +
9740 + /*
9741 + * Rule: Use shorthand properties where possible.
9742 + *
9743 + */
9744 +
9745 + CSSLint.addRule({
9746 +
9747 + // rule information
9748 + id: "shorthand",
9749 + name: "Require shorthand properties",
9750 + desc: "Use shorthand properties where possible.",
9751 + url: "https://github.com/CSSLint/csslint/wiki/Require-shorthand-properties",
9752 + browsers: "All",
9753 +
9754 + // initialization
9755 + init: function(parser, reporter) {
9756 + "use strict";
9757 + var rule = this,
9758 + prop, i, len,
9759 + propertiesToCheck = {},
9760 + properties,
9761 + mapping = {
9762 + "margin": [
9763 + "margin-top",
9764 + "margin-bottom",
9765 + "margin-left",
9766 + "margin-right"
9767 + ],
9768 + "padding": [
9769 + "padding-top",
9770 + "padding-bottom",
9771 + "padding-left",
9772 + "padding-right"
9773 + ]
9774 + };
9775 +
9776 + // initialize propertiesToCheck
9777 + for (prop in mapping) {
9778 + if (mapping.hasOwnProperty(prop)) {
9779 + for (i=0, len=mapping[prop].length; i < len; i++) {
9780 + propertiesToCheck[mapping[prop][i]] = prop;
9781 + }
9782 + }
9783 + }
9784 +
9785 + function startRule() {
9786 + properties = {};
9787 + }
9788 +
9789 + // event handler for end of rules
9790 + function endRule(event) {
9791 +
9792 + var prop, i, len, total;
9793 +
9794 + // check which properties this rule has
9795 + for (prop in mapping) {
9796 + if (mapping.hasOwnProperty(prop)) {
9797 + total=0;
9798 +
9799 + for (i=0, len=mapping[prop].length; i < len; i++) {
9800 + total += properties[mapping[prop][i]] ? 1 : 0;
9801 + }
9802 +
9803 + if (total === mapping[prop].length) {
9804 + reporter.report("The properties " + mapping[prop].join(", ") + " can be replaced by " + prop + ".", event.line, event.col, rule);
9805 + }
9806 + }
9807 + }
9808 + }
9809 +
9810 + parser.addListener("startrule", startRule);
9811 + parser.addListener("startfontface", startRule);
9812 +
9813 + // check for use of "font-size"
9814 + parser.addListener("property", function(event) {
9815 + var name = event.property.toString().toLowerCase();
9816 +
9817 + if (propertiesToCheck[name]) {
9818 + properties[name] = 1;
9819 + }
9820 + });
9821 +
9822 + parser.addListener("endrule", endRule);
9823 + parser.addListener("endfontface", endRule);
9824 +
9825 + }
9826 +
9827 + });
9828 +
9829 + /*
9830 + * Rule: Don't use properties with a star prefix.
9831 + *
9832 + */
9833 +
9834 + CSSLint.addRule({
9835 +
9836 + // rule information
9837 + id: "star-property-hack",
9838 + name: "Disallow properties with a star prefix",
9839 + desc: "Checks for the star property hack (targets IE6/7)",
9840 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-star-hack",
9841 + browsers: "All",
9842 +
9843 + // initialization
9844 + init: function(parser, reporter) {
9845 + "use strict";
9846 + var rule = this;
9847 +
9848 + // check if property name starts with "*"
9849 + parser.addListener("property", function(event) {
9850 + var property = event.property;
9851 +
9852 + if (property.hack === "*") {
9853 + reporter.report("Property with star prefix found.", event.property.line, event.property.col, rule);
9854 + }
9855 + });
9856 + }
9857 + });
9858 +
9859 + /*
9860 + * Rule: Don't use text-indent for image replacement if you need to support rtl.
9861 + *
9862 + */
9863 +
9864 + CSSLint.addRule({
9865 +
9866 + // rule information
9867 + id: "text-indent",
9868 + name: "Disallow negative text-indent",
9869 + desc: "Checks for text indent less than -99px",
9870 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-negative-text-indent",
9871 + browsers: "All",
9872 +
9873 + // initialization
9874 + init: function(parser, reporter) {
9875 + "use strict";
9876 + var rule = this,
9877 + textIndent,
9878 + direction;
9879 +
9880 +
9881 + function startRule() {
9882 + textIndent = false;
9883 + direction = "inherit";
9884 + }
9885 +
9886 + // event handler for end of rules
9887 + function endRule() {
9888 + if (textIndent && direction !== "ltr") {
9889 + reporter.report("Negative text-indent doesn't work well with RTL. If you use text-indent for image replacement explicitly set direction for that item to ltr.", textIndent.line, textIndent.col, rule);
9890 + }
9891 + }
9892 +
9893 + parser.addListener("startrule", startRule);
9894 + parser.addListener("startfontface", startRule);
9895 +
9896 + // check for use of "font-size"
9897 + parser.addListener("property", function(event) {
9898 + var name = event.property.toString().toLowerCase(),
9899 + value = event.value;
9900 +
9901 + if (name === "text-indent" && value.parts[0].value < -99) {
9902 + textIndent = event.property;
9903 + } else if (name === "direction" && value.toString() === "ltr") {
9904 + direction = "ltr";
9905 + }
9906 + });
9907 +
9908 + parser.addListener("endrule", endRule);
9909 + parser.addListener("endfontface", endRule);
9910 +
9911 + }
9912 +
9913 + });
9914 +
9915 + /*
9916 + * Rule: Don't use properties with a underscore prefix.
9917 + *
9918 + */
9919 +
9920 + CSSLint.addRule({
9921 +
9922 + // rule information
9923 + id: "underscore-property-hack",
9924 + name: "Disallow properties with an underscore prefix",
9925 + desc: "Checks for the underscore property hack (targets IE6)",
9926 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-underscore-hack",
9927 + browsers: "All",
9928 +
9929 + // initialization
9930 + init: function(parser, reporter) {
9931 + "use strict";
9932 + var rule = this;
9933 +
9934 + // check if property name starts with "_"
9935 + parser.addListener("property", function(event) {
9936 + var property = event.property;
9937 +
9938 + if (property.hack === "_") {
9939 + reporter.report("Property with underscore prefix found.", event.property.line, event.property.col, rule);
9940 + }
9941 + });
9942 + }
9943 + });
9944 +
9945 + /*
9946 + * Rule: Headings (h1-h6) should be defined only once.
9947 + */
9948 +
9949 + CSSLint.addRule({
9950 +
9951 + // rule information
9952 + id: "unique-headings",
9953 + name: "Headings should only be defined once",
9954 + desc: "Headings should be defined only once.",
9955 + url: "https://github.com/CSSLint/csslint/wiki/Headings-should-only-be-defined-once",
9956 + browsers: "All",
9957 +
9958 + // initialization
9959 + init: function(parser, reporter) {
9960 + "use strict";
9961 + var rule = this;
9962 +
9963 + var headings = {
9964 + h1: 0,
9965 + h2: 0,
9966 + h3: 0,
9967 + h4: 0,
9968 + h5: 0,
9969 + h6: 0
9970 + };
9971 +
9972 + parser.addListener("startrule", function(event) {
9973 + var selectors = event.selectors,
9974 + selector,
9975 + part,
9976 + pseudo,
9977 + i, j;
9978 +
9979 + for (i=0; i < selectors.length; i++) {
9980 + selector = selectors[i];
9981 + part = selector.parts[selector.parts.length-1];
9982 +
9983 + if (part.elementName && /(h[1-6])/i.test(part.elementName.toString())) {
9984 +
9985 + for (j=0; j < part.modifiers.length; j++) {
9986 + if (part.modifiers[j].type === "pseudo") {
9987 + pseudo = true;
9988 + break;
9989 + }
9990 + }
9991 +
9992 + if (!pseudo) {
9993 + headings[RegExp.$1]++;
9994 + if (headings[RegExp.$1] > 1) {
9995 + reporter.report("Heading (" + part.elementName + ") has already been defined.", part.line, part.col, rule);
9996 + }
9997 + }
9998 + }
9999 + }
10000 + });
10001 +
10002 + parser.addListener("endstylesheet", function() {
10003 + var prop,
10004 + messages = [];
10005 +
10006 + for (prop in headings) {
10007 + if (headings.hasOwnProperty(prop)) {
10008 + if (headings[prop] > 1) {
10009 + messages.push(headings[prop] + " " + prop + "s");
10010 + }
10011 + }
10012 + }
10013 +
10014 + if (messages.length) {
10015 + reporter.rollupWarn("You have " + messages.join(", ") + " defined in this stylesheet.", rule);
10016 + }
10017 + });
10018 + }
10019 +
10020 + });
10021 +
10022 + /*
10023 + * Rule: Don't use universal selector because it's slow.
10024 + */
10025 +
10026 + CSSLint.addRule({
10027 +
10028 + // rule information
10029 + id: "universal-selector",
10030 + name: "Disallow universal selector",
10031 + desc: "The universal selector (*) is known to be slow.",
10032 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-universal-selector",
10033 + browsers: "All",
10034 +
10035 + // initialization
10036 + init: function(parser, reporter) {
10037 + "use strict";
10038 + var rule = this;
10039 +
10040 + parser.addListener("startrule", function(event) {
10041 + var selectors = event.selectors,
10042 + selector,
10043 + part,
10044 + i;
10045 +
10046 + for (i=0; i < selectors.length; i++) {
10047 + selector = selectors[i];
10048 +
10049 + part = selector.parts[selector.parts.length-1];
10050 + if (part.elementName === "*") {
10051 + reporter.report(rule.desc, part.line, part.col, rule);
10052 + }
10053 + }
10054 + });
10055 + }
10056 +
10057 + });
10058 +
10059 + /*
10060 + * Rule: Don't use unqualified attribute selectors because they're just like universal selectors.
10061 + */
10062 +
10063 + CSSLint.addRule({
10064 +
10065 + // rule information
10066 + id: "unqualified-attributes",
10067 + name: "Disallow unqualified attribute selectors",
10068 + desc: "Unqualified attribute selectors are known to be slow.",
10069 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-unqualified-attribute-selectors",
10070 + browsers: "All",
10071 +
10072 + // initialization
10073 + init: function(parser, reporter) {
10074 + "use strict";
10075 +
10076 + var rule = this;
10077 +
10078 + parser.addListener("startrule", function(event) {
10079 +
10080 + var selectors = event.selectors,
10081 + selectorContainsClassOrId = false,
10082 + selector,
10083 + part,
10084 + modifier,
10085 + i, k;
10086 +
10087 + for (i=0; i < selectors.length; i++) {
10088 + selector = selectors[i];
10089 +
10090 + part = selector.parts[selector.parts.length-1];
10091 + if (part.type === parser.SELECTOR_PART_TYPE) {
10092 + for (k=0; k < part.modifiers.length; k++) {
10093 + modifier = part.modifiers[k];
10094 +
10095 + if (modifier.type === "class" || modifier.type === "id") {
10096 + selectorContainsClassOrId = true;
10097 + break;
10098 + }
10099 + }
10100 +
10101 + if (!selectorContainsClassOrId) {
10102 + for (k=0; k < part.modifiers.length; k++) {
10103 + modifier = part.modifiers[k];
10104 + if (modifier.type === "attribute" && (!part.elementName || part.elementName === "*")) {
10105 + reporter.report(rule.desc, part.line, part.col, rule);
10106 + }
10107 + }
10108 + }
10109 + }
10110 +
10111 + }
10112 + });
10113 + }
10114 +
10115 + });
10116 +
10117 + /*
10118 + * Rule: When using a vendor-prefixed property, make sure to
10119 + * include the standard one.
10120 + */
10121 +
10122 + CSSLint.addRule({
10123 +
10124 + // rule information
10125 + id: "vendor-prefix",
10126 + name: "Require standard property with vendor prefix",
10127 + desc: "When using a vendor-prefixed property, make sure to include the standard one.",
10128 + url: "https://github.com/CSSLint/csslint/wiki/Require-standard-property-with-vendor-prefix",
10129 + browsers: "All",
10130 +
10131 + // initialization
10132 + init: function(parser, reporter) {
10133 + "use strict";
10134 + var rule = this,
10135 + properties,
10136 + num,
10137 + propertiesToCheck = {
10138 + "-webkit-border-radius": "border-radius",
10139 + "-webkit-border-top-left-radius": "border-top-left-radius",
10140 + "-webkit-border-top-right-radius": "border-top-right-radius",
10141 + "-webkit-border-bottom-left-radius": "border-bottom-left-radius",
10142 + "-webkit-border-bottom-right-radius": "border-bottom-right-radius",
10143 +
10144 + "-o-border-radius": "border-radius",
10145 + "-o-border-top-left-radius": "border-top-left-radius",
10146 + "-o-border-top-right-radius": "border-top-right-radius",
10147 + "-o-border-bottom-left-radius": "border-bottom-left-radius",
10148 + "-o-border-bottom-right-radius": "border-bottom-right-radius",
10149 +
10150 + "-moz-border-radius": "border-radius",
10151 + "-moz-border-radius-topleft": "border-top-left-radius",
10152 + "-moz-border-radius-topright": "border-top-right-radius",
10153 + "-moz-border-radius-bottomleft": "border-bottom-left-radius",
10154 + "-moz-border-radius-bottomright": "border-bottom-right-radius",
10155 +
10156 + "-moz-column-count": "column-count",
10157 + "-webkit-column-count": "column-count",
10158 +
10159 + "-moz-column-gap": "column-gap",
10160 + "-webkit-column-gap": "column-gap",
10161 +
10162 + "-moz-column-rule": "column-rule",
10163 + "-webkit-column-rule": "column-rule",
10164 +
10165 + "-moz-column-rule-style": "column-rule-style",
10166 + "-webkit-column-rule-style": "column-rule-style",
10167 +
10168 + "-moz-column-rule-color": "column-rule-color",
10169 + "-webkit-column-rule-color": "column-rule-color",
10170 +
10171 + "-moz-column-rule-width": "column-rule-width",
10172 + "-webkit-column-rule-width": "column-rule-width",
10173 +
10174 + "-moz-column-width": "column-width",
10175 + "-webkit-column-width": "column-width",
10176 +
10177 + "-webkit-column-span": "column-span",
10178 + "-webkit-columns": "columns",
10179 +
10180 + "-moz-box-shadow": "box-shadow",
10181 + "-webkit-box-shadow": "box-shadow",
10182 +
10183 + "-moz-transform": "transform",
10184 + "-webkit-transform": "transform",
10185 + "-o-transform": "transform",
10186 + "-ms-transform": "transform",
10187 +
10188 + "-moz-transform-origin": "transform-origin",
10189 + "-webkit-transform-origin": "transform-origin",
10190 + "-o-transform-origin": "transform-origin",
10191 + "-ms-transform-origin": "transform-origin",
10192 +
10193 + "-moz-box-sizing": "box-sizing",
10194 + "-webkit-box-sizing": "box-sizing"
10195 + };
10196 +
10197 + // event handler for beginning of rules
10198 + function startRule() {
10199 + properties = {};
10200 + num = 1;
10201 + }
10202 +
10203 + // event handler for end of rules
10204 + function endRule() {
10205 + var prop,
10206 + i,
10207 + len,
10208 + needed,
10209 + actual,
10210 + needsStandard = [];
10211 +
10212 + for (prop in properties) {
10213 + if (propertiesToCheck[prop]) {
10214 + needsStandard.push({
10215 + actual: prop,
10216 + needed: propertiesToCheck[prop]
10217 + });
10218 + }
10219 + }
10220 +
10221 + for (i=0, len=needsStandard.length; i < len; i++) {
10222 + needed = needsStandard[i].needed;
10223 + actual = needsStandard[i].actual;
10224 +
10225 + if (!properties[needed]) {
10226 + reporter.report("Missing standard property '" + needed + "' to go along with '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
10227 + } else {
10228 + // make sure standard property is last
10229 + if (properties[needed][0].pos < properties[actual][0].pos) {
10230 + reporter.report("Standard property '" + needed + "' should come after vendor-prefixed property '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
10231 + }
10232 + }
10233 + }
10234 +
10235 + }
10236 +
10237 + parser.addListener("startrule", startRule);
10238 + parser.addListener("startfontface", startRule);
10239 + parser.addListener("startpage", startRule);
10240 + parser.addListener("startpagemargin", startRule);
10241 + parser.addListener("startkeyframerule", startRule);
10242 + parser.addListener("startviewport", startRule);
10243 +
10244 + parser.addListener("property", function(event) {
10245 + var name = event.property.text.toLowerCase();
10246 +
10247 + if (!properties[name]) {
10248 + properties[name] = [];
10249 + }
10250 +
10251 + properties[name].push({
10252 + name: event.property,
10253 + value: event.value,
10254 + pos: num++
10255 + });
10256 + });
10257 +
10258 + parser.addListener("endrule", endRule);
10259 + parser.addListener("endfontface", endRule);
10260 + parser.addListener("endpage", endRule);
10261 + parser.addListener("endpagemargin", endRule);
10262 + parser.addListener("endkeyframerule", endRule);
10263 + parser.addListener("endviewport", endRule);
10264 + }
10265 +
10266 + });
10267 +
10268 + /*
10269 + * Rule: You don't need to specify units when a value is 0.
10270 + */
10271 +
10272 + CSSLint.addRule({
10273 +
10274 + // rule information
10275 + id: "zero-units",
10276 + name: "Disallow units for 0 values",
10277 + desc: "You don't need to specify units when a value is 0.",
10278 + url: "https://github.com/CSSLint/csslint/wiki/Disallow-units-for-zero-values",
10279 + browsers: "All",
10280 +
10281 + // initialization
10282 + init: function(parser, reporter) {
10283 + "use strict";
10284 + var rule = this;
10285 +
10286 + // count how many times "float" is used
10287 + parser.addListener("property", function(event) {
10288 + var parts = event.value.parts,
10289 + i = 0,
10290 + len = parts.length;
10291 +
10292 + while (i < len) {
10293 + if ((parts[i].units || parts[i].type === "percentage") && parts[i].value === 0 && parts[i].type !== "time") {
10294 + reporter.report("Values of 0 shouldn't have units specified.", parts[i].line, parts[i].col, rule);
10295 + }
10296 + i++;
10297 + }
10298 +
10299 + });
10300 +
10301 + }
10302 +
10303 + });
10304 +
10305 + (function() {
10306 + "use strict";
10307 +
10308 + /**
10309 + * Replace special characters before write to output.
10310 + *
10311 + * Rules:
10312 + * - single quotes is the escape sequence for double-quotes
10313 + * - &amp; is the escape sequence for &
10314 + * - &lt; is the escape sequence for <
10315 + * - &gt; is the escape sequence for >
10316 + *
10317 + * @param {String} message to escape
10318 + * @return escaped message as {String}
10319 + */
10320 + var xmlEscape = function(str) {
10321 + if (!str || str.constructor !== String) {
10322 + return "";
10323 + }
10324 +
10325 + return str.replace(/["&><]/g, function(match) {
10326 + switch (match) {
10327 + case "\"":
10328 + return "&quot;";
10329 + case "&":
10330 + return "&amp;";
10331 + case "<":
10332 + return "&lt;";
10333 + case ">":
10334 + return "&gt;";
10335 + }
10336 + });
10337 + };
10338 +
10339 + CSSLint.addFormatter({
10340 + // format information
10341 + id: "checkstyle-xml",
10342 + name: "Checkstyle XML format",
10343 +
10344 + /**
10345 + * Return opening root XML tag.
10346 + * @return {String} to prepend before all results
10347 + */
10348 + startFormat: function() {
10349 + return "<?xml version=\"1.0\" encoding=\"utf-8\"?><checkstyle>";
10350 + },
10351 +
10352 + /**
10353 + * Return closing root XML tag.
10354 + * @return {String} to append after all results
10355 + */
10356 + endFormat: function() {
10357 + return "</checkstyle>";
10358 + },
10359 +
10360 + /**
10361 + * Returns message when there is a file read error.
10362 + * @param {String} filename The name of the file that caused the error.
10363 + * @param {String} message The error message
10364 + * @return {String} The error message.
10365 + */
10366 + readError: function(filename, message) {
10367 + return "<file name=\"" + xmlEscape(filename) + "\"><error line=\"0\" column=\"0\" severty=\"error\" message=\"" + xmlEscape(message) + "\"></error></file>";
10368 + },
10369 +
10370 + /**
10371 + * Given CSS Lint results for a file, return output for this format.
10372 + * @param results {Object} with error and warning messages
10373 + * @param filename {String} relative file path
10374 + * @param options {Object} (UNUSED for now) specifies special handling of output
10375 + * @return {String} output for results
10376 + */
10377 + formatResults: function(results, filename/*, options*/) {
10378 + var messages = results.messages,
10379 + output = [];
10380 +
10381 + /**
10382 + * Generate a source string for a rule.
10383 + * Checkstyle source strings usually resemble Java class names e.g
10384 + * net.csslint.SomeRuleName
10385 + * @param {Object} rule
10386 + * @return rule source as {String}
10387 + */
10388 + var generateSource = function(rule) {
10389 + if (!rule || !("name" in rule)) {
10390 + return "";
10391 + }
10392 + return "net.csslint." + rule.name.replace(/\s/g, "");
10393 + };
10394 +
10395 +
10396 + if (messages.length > 0) {
10397 + output.push("<file name=\""+filename+"\">");
10398 + CSSLint.Util.forEach(messages, function (message) {
10399 + // ignore rollups for now
10400 + if (!message.rollup) {
10401 + output.push("<error line=\"" + message.line + "\" column=\"" + message.col + "\" severity=\"" + message.type + "\"" +
10402 + " message=\"" + xmlEscape(message.message) + "\" source=\"" + generateSource(message.rule) +"\"/>");
10403 + }
10404 + });
10405 + output.push("</file>");
10406 + }
10407 +
10408 + return output.join("");
10409 + }
10410 + });
10411 +
10412 + }());
10413 +
10414 + CSSLint.addFormatter({
10415 + // format information
10416 + id: "compact",
10417 + name: "Compact, 'porcelain' format",
10418 +
10419 + /**
10420 + * Return content to be printed before all file results.
10421 + * @return {String} to prepend before all results
10422 + */
10423 + startFormat: function() {
10424 + "use strict";
10425 + return "";
10426 + },
10427 +
10428 + /**
10429 + * Return content to be printed after all file results.
10430 + * @return {String} to append after all results
10431 + */
10432 + endFormat: function() {
10433 + "use strict";
10434 + return "";
10435 + },
10436 +
10437 + /**
10438 + * Given CSS Lint results for a file, return output for this format.
10439 + * @param results {Object} with error and warning messages
10440 + * @param filename {String} relative file path
10441 + * @param options {Object} (Optional) specifies special handling of output
10442 + * @return {String} output for results
10443 + */
10444 + formatResults: function(results, filename, options) {
10445 + "use strict";
10446 + var messages = results.messages,
10447 + output = "";
10448 + options = options || {};
10449 +
10450 + /**
10451 + * Capitalize and return given string.
10452 + * @param str {String} to capitalize
10453 + * @return {String} capitalized
10454 + */
10455 + var capitalize = function(str) {
10456 + return str.charAt(0).toUpperCase() + str.slice(1);
10457 + };
10458 +
10459 + if (messages.length === 0) {
10460 + return options.quiet ? "" : filename + ": Lint Free!";
10461 + }
10462 +
10463 + CSSLint.Util.forEach(messages, function(message) {
10464 + if (message.rollup) {
10465 + output += filename + ": " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
10466 + } else {
10467 + output += filename + ": line " + message.line +
10468 + ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
10469 + }
10470 + });
10471 +
10472 + return output;
10473 + }
10474 + });
10475 +
10476 + CSSLint.addFormatter({
10477 + // format information
10478 + id: "csslint-xml",
10479 + name: "CSSLint XML format",
10480 +
10481 + /**
10482 + * Return opening root XML tag.
10483 + * @return {String} to prepend before all results
10484 + */
10485 + startFormat: function() {
10486 + "use strict";
10487 + return "<?xml version=\"1.0\" encoding=\"utf-8\"?><csslint>";
10488 + },
10489 +
10490 + /**
10491 + * Return closing root XML tag.
10492 + * @return {String} to append after all results
10493 + */
10494 + endFormat: function() {
10495 + "use strict";
10496 + return "</csslint>";
10497 + },
10498 +
10499 + /**
10500 + * Given CSS Lint results for a file, return output for this format.
10501 + * @param results {Object} with error and warning messages
10502 + * @param filename {String} relative file path
10503 + * @param options {Object} (UNUSED for now) specifies special handling of output
10504 + * @return {String} output for results
10505 + */
10506 + formatResults: function(results, filename/*, options*/) {
10507 + "use strict";
10508 + var messages = results.messages,
10509 + output = [];
10510 +
10511 + /**
10512 + * Replace special characters before write to output.
10513 + *
10514 + * Rules:
10515 + * - single quotes is the escape sequence for double-quotes
10516 + * - &amp; is the escape sequence for &
10517 + * - &lt; is the escape sequence for <
10518 + * - &gt; is the escape sequence for >
10519 + *
10520 + * @param {String} message to escape
10521 + * @return escaped message as {String}
10522 + */
10523 + var escapeSpecialCharacters = function(str) {
10524 + if (!str || str.constructor !== String) {
10525 + return "";
10526 + }
10527 + return str.replace(/"/g, "'").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
10528 + };
10529 +
10530 + if (messages.length > 0) {
10531 + output.push("<file name=\""+filename+"\">");
10532 + CSSLint.Util.forEach(messages, function (message) {
10533 + if (message.rollup) {
10534 + output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
10535 + } else {
10536 + output.push("<issue line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
10537 + " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
10538 + }
10539 + });
10540 + output.push("</file>");
10541 + }
10542 +
10543 + return output.join("");
10544 + }
10545 + });
10546 +
10547 + /* globals JSON: true */
10548 +
10549 + CSSLint.addFormatter({
10550 + // format information
10551 + id: "json",
10552 + name: "JSON",
10553 +
10554 + /**
10555 + * Return content to be printed before all file results.
10556 + * @return {String} to prepend before all results
10557 + */
10558 + startFormat: function() {
10559 + "use strict";
10560 + this.json = [];
10561 + return "";
10562 + },
10563 +
10564 + /**
10565 + * Return content to be printed after all file results.
10566 + * @return {String} to append after all results
10567 + */
10568 + endFormat: function() {
10569 + "use strict";
10570 + var ret = "";
10571 + if (this.json.length > 0) {
10572 + if (this.json.length === 1) {
10573 + ret = JSON.stringify(this.json[0]);
10574 + } else {
10575 + ret = JSON.stringify(this.json);
10576 + }
10577 + }
10578 + return ret;
10579 + },
10580 +
10581 + /**
10582 + * Given CSS Lint results for a file, return output for this format.
10583 + * @param results {Object} with error and warning messages
10584 + * @param filename {String} relative file path (Unused)
10585 + * @return {String} output for results
10586 + */
10587 + formatResults: function(results, filename, options) {
10588 + "use strict";
10589 + if (results.messages.length > 0 || !options.quiet) {
10590 + this.json.push({
10591 + filename: filename,
10592 + messages: results.messages,
10593 + stats: results.stats
10594 + });
10595 + }
10596 + return "";
10597 + }
10598 + });
10599 +
10600 + CSSLint.addFormatter({
10601 + // format information
10602 + id: "junit-xml",
10603 + name: "JUNIT XML format",
10604 +
10605 + /**
10606 + * Return opening root XML tag.
10607 + * @return {String} to prepend before all results
10608 + */
10609 + startFormat: function() {
10610 + "use strict";
10611 + return "<?xml version=\"1.0\" encoding=\"utf-8\"?><testsuites>";
10612 + },
10613 +
10614 + /**
10615 + * Return closing root XML tag.
10616 + * @return {String} to append after all results
10617 + */
10618 + endFormat: function() {
10619 + "use strict";
10620 + return "</testsuites>";
10621 + },
10622 +
10623 + /**
10624 + * Given CSS Lint results for a file, return output for this format.
10625 + * @param results {Object} with error and warning messages
10626 + * @param filename {String} relative file path
10627 + * @param options {Object} (UNUSED for now) specifies special handling of output
10628 + * @return {String} output for results
10629 + */
10630 + formatResults: function(results, filename/*, options*/) {
10631 + "use strict";
10632 +
10633 + var messages = results.messages,
10634 + output = [],
10635 + tests = {
10636 + "error": 0,
10637 + "failure": 0
10638 + };
10639 +
10640 + /**
10641 + * Generate a source string for a rule.
10642 + * JUNIT source strings usually resemble Java class names e.g
10643 + * net.csslint.SomeRuleName
10644 + * @param {Object} rule
10645 + * @return rule source as {String}
10646 + */
10647 + var generateSource = function(rule) {
10648 + if (!rule || !("name" in rule)) {
10649 + return "";
10650 + }
10651 + return "net.csslint." + rule.name.replace(/\s/g, "");
10652 + };
10653 +
10654 + /**
10655 + * Replace special characters before write to output.
10656 + *
10657 + * Rules:
10658 + * - single quotes is the escape sequence for double-quotes
10659 + * - &lt; is the escape sequence for <
10660 + * - &gt; is the escape sequence for >
10661 + *
10662 + * @param {String} message to escape
10663 + * @return escaped message as {String}
10664 + */
10665 + var escapeSpecialCharacters = function(str) {
10666 +
10667 + if (!str || str.constructor !== String) {
10668 + return "";
10669 + }
10670 +
10671 + return str.replace(/"/g, "'").replace(/</g, "&lt;").replace(/>/g, "&gt;");
10672 +
10673 + };
10674 +
10675 + if (messages.length > 0) {
10676 +
10677 + messages.forEach(function (message) {
10678 +
10679 + // since junit has no warning class
10680 + // all issues as errors
10681 + var type = message.type === "warning" ? "error" : message.type;
10682 +
10683 + // ignore rollups for now
10684 + if (!message.rollup) {
10685 +
10686 + // build the test case separately, once joined
10687 + // we'll add it to a custom array filtered by type
10688 + output.push("<testcase time=\"0\" name=\"" + generateSource(message.rule) + "\">");
10689 + output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\"><![CDATA[" + message.line + ":" + message.col + ":" + escapeSpecialCharacters(message.evidence) + "]]></" + type + ">");
10690 + output.push("</testcase>");
10691 +
10692 + tests[type] += 1;
10693 +
10694 + }
10695 +
10696 + });
10697 +
10698 + output.unshift("<testsuite time=\"0\" tests=\"" + messages.length + "\" skipped=\"0\" errors=\"" + tests.error + "\" failures=\"" + tests.failure + "\" package=\"net.csslint\" name=\"" + filename + "\">");
10699 + output.push("</testsuite>");
10700 +
10701 + }
10702 +
10703 + return output.join("");
10704 +
10705 + }
10706 + });
10707 +
10708 + CSSLint.addFormatter({
10709 + // format information
10710 + id: "lint-xml",
10711 + name: "Lint XML format",
10712 +
10713 + /**
10714 + * Return opening root XML tag.
10715 + * @return {String} to prepend before all results
10716 + */
10717 + startFormat: function() {
10718 + "use strict";
10719 + return "<?xml version=\"1.0\" encoding=\"utf-8\"?><lint>";
10720 + },
10721 +
10722 + /**
10723 + * Return closing root XML tag.
10724 + * @return {String} to append after all results
10725 + */
10726 + endFormat: function() {
10727 + "use strict";
10728 + return "</lint>";
10729 + },
10730 +
10731 + /**
10732 + * Given CSS Lint results for a file, return output for this format.
10733 + * @param results {Object} with error and warning messages
10734 + * @param filename {String} relative file path
10735 + * @param options {Object} (UNUSED for now) specifies special handling of output
10736 + * @return {String} output for results
10737 + */
10738 + formatResults: function(results, filename/*, options*/) {
10739 + "use strict";
10740 + var messages = results.messages,
10741 + output = [];
10742 +
10743 + /**
10744 + * Replace special characters before write to output.
10745 + *
10746 + * Rules:
10747 + * - single quotes is the escape sequence for double-quotes
10748 + * - &amp; is the escape sequence for &
10749 + * - &lt; is the escape sequence for <
10750 + * - &gt; is the escape sequence for >
10751 + *
10752 + * @param {String} message to escape
10753 + * @return escaped message as {String}
10754 + */
10755 + var escapeSpecialCharacters = function(str) {
10756 + if (!str || str.constructor !== String) {
10757 + return "";
10758 + }
10759 + return str.replace(/"/g, "'").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
10760 + };
10761 +
10762 + if (messages.length > 0) {
10763 +
10764 + output.push("<file name=\""+filename+"\">");
10765 + CSSLint.Util.forEach(messages, function (message) {
10766 + if (message.rollup) {
10767 + output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
10768 + } else {
10769 + var rule = "";
10770 + if (message.rule && message.rule.id) {
10771 + rule = "rule=\"" + escapeSpecialCharacters(message.rule.id) + "\" ";
10772 + }
10773 + output.push("<issue " + rule + "line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
10774 + " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
10775 + }
10776 + });
10777 + output.push("</file>");
10778 + }
10779 +
10780 + return output.join("");
10781 + }
10782 + });
10783 +
10784 + CSSLint.addFormatter({
10785 + // format information
10786 + id: "text",
10787 + name: "Plain Text",
10788 +
10789 + /**
10790 + * Return content to be printed before all file results.
10791 + * @return {String} to prepend before all results
10792 + */
10793 + startFormat: function() {
10794 + "use strict";
10795 + return "";
10796 + },
10797 +
10798 + /**
10799 + * Return content to be printed after all file results.
10800 + * @return {String} to append after all results
10801 + */
10802 + endFormat: function() {
10803 + "use strict";
10804 + return "";
10805 + },
10806 +
10807 + /**
10808 + * Given CSS Lint results for a file, return output for this format.
10809 + * @param results {Object} with error and warning messages
10810 + * @param filename {String} relative file path
10811 + * @param options {Object} (Optional) specifies special handling of output
10812 + * @return {String} output for results
10813 + */
10814 + formatResults: function(results, filename, options) {
10815 + "use strict";
10816 + var messages = results.messages,
10817 + output = "";
10818 + options = options || {};
10819 +
10820 + if (messages.length === 0) {
10821 + return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + ".";
10822 + }
10823 +
10824 + output = "\n\ncsslint: There ";
10825 + if (messages.length === 1) {
10826 + output += "is 1 problem";
10827 + } else {
10828 + output += "are " + messages.length + " problems";
10829 + }
10830 + output += " in " + filename + ".";
10831 +
10832 + var pos = filename.lastIndexOf("/"),
10833 + shortFilename = filename;
10834 +
10835 + if (pos === -1) {
10836 + pos = filename.lastIndexOf("\\");
10837 + }
10838 + if (pos > -1) {
10839 + shortFilename = filename.substring(pos+1);
10840 + }
10841 +
10842 + CSSLint.Util.forEach(messages, function (message, i) {
10843 + output = output + "\n\n" + shortFilename;
10844 + if (message.rollup) {
10845 + output += "\n" + (i+1) + ": " + message.type;
10846 + output += "\n" + message.message;
10847 + } else {
10848 + output += "\n" + (i+1) + ": " + message.type + " at line " + message.line + ", col " + message.col;
10849 + output += "\n" + message.message;
10850 + output += "\n" + message.evidence;
10851 + }
10852 + });
10853 +
10854 + return output;
10855 + }
10856 + });
10857 +
10858 + return CSSLint;
10859 + })();