Diff: STRATO-apps/wordpress_03/app/wp-includes/js/codemirror/csslint.js
Keine Baseline-Datei – Diff nur gegen leer.
1
-
1
+
/*!
2
+
CSSLint v1.0.4
3
+
Copyright (c) 2016 Nicole Sullivan and Nicholas C. Zakas. All rights reserved.
4
+
5
+
Permission is hereby granted, free of charge, to any person obtaining a copy
6
+
of this software and associated documentation files (the 'Software'), to deal
7
+
in the Software without restriction, including without limitation the rights
8
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+
copies of the Software, and to permit persons to whom the Software is
10
+
furnished to do so, subject to the following conditions:
11
+
12
+
The above copyright notice and this permission notice shall be included in
13
+
all copies or substantial portions of the Software.
14
+
15
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+
THE SOFTWARE.
22
+
23
+
*/
24
+
25
+
var CSSLint = (function(){
26
+
var module = module || {},
27
+
exports = exports || {};
28
+
29
+
/*!
30
+
Parser-Lib
31
+
Copyright (c) 2009-2016 Nicholas C. Zakas. All rights reserved.
32
+
33
+
Permission is hereby granted, free of charge, to any person obtaining a copy
34
+
of this software and associated documentation files (the "Software"), to deal
35
+
in the Software without restriction, including without limitation the rights
36
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
37
+
copies of the Software, and to permit persons to whom the Software is
38
+
furnished to do so, subject to the following conditions:
39
+
40
+
The above copyright notice and this permission notice shall be included in
41
+
all copies or substantial portions of the Software.
42
+
43
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
44
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
45
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
46
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
47
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
48
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
49
+
THE SOFTWARE.
50
+
*/
51
+
/* Version v1.1.0, Build time: 6-December-2016 10:31:29 */
52
+
var parserlib = (function () {
53
+
var require;
54
+
require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
55
+
"use strict";
56
+
57
+
/* exported Colors */
58
+
59
+
var Colors = module.exports = {
60
+
__proto__ :null,
61
+
aliceblue :"#f0f8ff",
62
+
antiquewhite :"#faebd7",
63
+
aqua :"#00ffff",
64
+
aquamarine :"#7fffd4",
65
+
azure :"#f0ffff",
66
+
beige :"#f5f5dc",
67
+
bisque :"#ffe4c4",
68
+
black :"#000000",
69
+
blanchedalmond :"#ffebcd",
70
+
blue :"#0000ff",
71
+
blueviolet :"#8a2be2",
72
+
brown :"#a52a2a",
73
+
burlywood :"#deb887",
74
+
cadetblue :"#5f9ea0",
75
+
chartreuse :"#7fff00",
76
+
chocolate :"#d2691e",
77
+
coral :"#ff7f50",
78
+
cornflowerblue :"#6495ed",
79
+
cornsilk :"#fff8dc",
80
+
crimson :"#dc143c",
81
+
cyan :"#00ffff",
82
+
darkblue :"#00008b",
83
+
darkcyan :"#008b8b",
84
+
darkgoldenrod :"#b8860b",
85
+
darkgray :"#a9a9a9",
86
+
darkgrey :"#a9a9a9",
87
+
darkgreen :"#006400",
88
+
darkkhaki :"#bdb76b",
89
+
darkmagenta :"#8b008b",
90
+
darkolivegreen :"#556b2f",
91
+
darkorange :"#ff8c00",
92
+
darkorchid :"#9932cc",
93
+
darkred :"#8b0000",
94
+
darksalmon :"#e9967a",
95
+
darkseagreen :"#8fbc8f",
96
+
darkslateblue :"#483d8b",
97
+
darkslategray :"#2f4f4f",
98
+
darkslategrey :"#2f4f4f",
99
+
darkturquoise :"#00ced1",
100
+
darkviolet :"#9400d3",
101
+
deeppink :"#ff1493",
102
+
deepskyblue :"#00bfff",
103
+
dimgray :"#696969",
104
+
dimgrey :"#696969",
105
+
dodgerblue :"#1e90ff",
106
+
firebrick :"#b22222",
107
+
floralwhite :"#fffaf0",
108
+
forestgreen :"#228b22",
109
+
fuchsia :"#ff00ff",
110
+
gainsboro :"#dcdcdc",
111
+
ghostwhite :"#f8f8ff",
112
+
gold :"#ffd700",
113
+
goldenrod :"#daa520",
114
+
gray :"#808080",
115
+
grey :"#808080",
116
+
green :"#008000",
117
+
greenyellow :"#adff2f",
118
+
honeydew :"#f0fff0",
119
+
hotpink :"#ff69b4",
120
+
indianred :"#cd5c5c",
121
+
indigo :"#4b0082",
122
+
ivory :"#fffff0",
123
+
khaki :"#f0e68c",
124
+
lavender :"#e6e6fa",
125
+
lavenderblush :"#fff0f5",
126
+
lawngreen :"#7cfc00",
127
+
lemonchiffon :"#fffacd",
128
+
lightblue :"#add8e6",
129
+
lightcoral :"#f08080",
130
+
lightcyan :"#e0ffff",
131
+
lightgoldenrodyellow :"#fafad2",
132
+
lightgray :"#d3d3d3",
133
+
lightgrey :"#d3d3d3",
134
+
lightgreen :"#90ee90",
135
+
lightpink :"#ffb6c1",
136
+
lightsalmon :"#ffa07a",
137
+
lightseagreen :"#20b2aa",
138
+
lightskyblue :"#87cefa",
139
+
lightslategray :"#778899",
140
+
lightslategrey :"#778899",
141
+
lightsteelblue :"#b0c4de",
142
+
lightyellow :"#ffffe0",
143
+
lime :"#00ff00",
144
+
limegreen :"#32cd32",
145
+
linen :"#faf0e6",
146
+
magenta :"#ff00ff",
147
+
maroon :"#800000",
148
+
mediumaquamarine:"#66cdaa",
149
+
mediumblue :"#0000cd",
150
+
mediumorchid :"#ba55d3",
151
+
mediumpurple :"#9370d8",
152
+
mediumseagreen :"#3cb371",
153
+
mediumslateblue :"#7b68ee",
154
+
mediumspringgreen :"#00fa9a",
155
+
mediumturquoise :"#48d1cc",
156
+
mediumvioletred :"#c71585",
157
+
midnightblue :"#191970",
158
+
mintcream :"#f5fffa",
159
+
mistyrose :"#ffe4e1",
160
+
moccasin :"#ffe4b5",
161
+
navajowhite :"#ffdead",
162
+
navy :"#000080",
163
+
oldlace :"#fdf5e6",
164
+
olive :"#808000",
165
+
olivedrab :"#6b8e23",
166
+
orange :"#ffa500",
167
+
orangered :"#ff4500",
168
+
orchid :"#da70d6",
169
+
palegoldenrod :"#eee8aa",
170
+
palegreen :"#98fb98",
171
+
paleturquoise :"#afeeee",
172
+
palevioletred :"#d87093",
173
+
papayawhip :"#ffefd5",
174
+
peachpuff :"#ffdab9",
175
+
peru :"#cd853f",
176
+
pink :"#ffc0cb",
177
+
plum :"#dda0dd",
178
+
powderblue :"#b0e0e6",
179
+
purple :"#800080",
180
+
red :"#ff0000",
181
+
rosybrown :"#bc8f8f",
182
+
royalblue :"#4169e1",
183
+
saddlebrown :"#8b4513",
184
+
salmon :"#fa8072",
185
+
sandybrown :"#f4a460",
186
+
seagreen :"#2e8b57",
187
+
seashell :"#fff5ee",
188
+
sienna :"#a0522d",
189
+
silver :"#c0c0c0",
190
+
skyblue :"#87ceeb",
191
+
slateblue :"#6a5acd",
192
+
slategray :"#708090",
193
+
slategrey :"#708090",
194
+
snow :"#fffafa",
195
+
springgreen :"#00ff7f",
196
+
steelblue :"#4682b4",
197
+
tan :"#d2b48c",
198
+
teal :"#008080",
199
+
thistle :"#d8bfd8",
200
+
tomato :"#ff6347",
201
+
turquoise :"#40e0d0",
202
+
violet :"#ee82ee",
203
+
wheat :"#f5deb3",
204
+
white :"#ffffff",
205
+
whitesmoke :"#f5f5f5",
206
+
yellow :"#ffff00",
207
+
yellowgreen :"#9acd32",
208
+
//'currentColor' color keyword https://www.w3.org/TR/css3-color/#currentcolor
209
+
currentColor :"The value of the 'color' property.",
210
+
//CSS2 system colors https://www.w3.org/TR/css3-color/#css2-system
211
+
activeBorder :"Active window border.",
212
+
activecaption :"Active window caption.",
213
+
appworkspace :"Background color of multiple document interface.",
214
+
background :"Desktop background.",
215
+
buttonface :"The face background color for 3-D elements that appear 3-D due to one layer of surrounding border.",
216
+
buttonhighlight :"The color of the border facing the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",
217
+
buttonshadow :"The color of the border away from the light source for 3-D elements that appear 3-D due to one layer of surrounding border.",
218
+
buttontext :"Text on push buttons.",
219
+
captiontext :"Text in caption, size box, and scrollbar arrow box.",
220
+
graytext :"Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.",
221
+
greytext :"Greyed (disabled) text. This color is set to #000 if the current display driver does not support a solid grey color.",
222
+
highlight :"Item(s) selected in a control.",
223
+
highlighttext :"Text of item(s) selected in a control.",
224
+
inactiveborder :"Inactive window border.",
225
+
inactivecaption :"Inactive window caption.",
226
+
inactivecaptiontext :"Color of text in an inactive caption.",
227
+
infobackground :"Background color for tooltip controls.",
228
+
infotext :"Text color for tooltip controls.",
229
+
menu :"Menu background.",
230
+
menutext :"Text in menus.",
231
+
scrollbar :"Scroll bar gray area.",
232
+
threeddarkshadow :"The color of the darker (generally outer) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
233
+
threedface :"The face background color for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
234
+
threedhighlight :"The color of the lighter (generally outer) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
235
+
threedlightshadow :"The color of the darker (generally inner) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
236
+
threedshadow :"The color of the lighter (generally inner) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.",
237
+
window :"Window background.",
238
+
windowframe :"Window frame.",
239
+
windowtext :"Text in windows."
240
+
};
241
+
242
+
},{}],2:[function(require,module,exports){
243
+
"use strict";
244
+
245
+
module.exports = Combinator;
246
+
247
+
var SyntaxUnit = require("../util/SyntaxUnit");
248
+
249
+
var Parser = require("./Parser");
250
+
251
+
/**
252
+
* Represents a selector combinator (whitespace, +, >).
253
+
* @namespace parserlib.css
254
+
* @class Combinator
255
+
* @extends parserlib.util.SyntaxUnit
256
+
* @constructor
257
+
* @param {String} text The text representation of the unit.
258
+
* @param {int} line The line of text on which the unit resides.
259
+
* @param {int} col The column of text on which the unit resides.
260
+
*/
261
+
function Combinator(text, line, col) {
262
+
263
+
SyntaxUnit.call(this, text, line, col, Parser.COMBINATOR_TYPE);
264
+
265
+
/**
266
+
* The type of modifier.
267
+
* @type String
268
+
* @property type
269
+
*/
270
+
this.type = "unknown";
271
+
272
+
//pretty simple
273
+
if (/^\s+$/.test(text)) {
274
+
this.type = "descendant";
275
+
} else if (text === ">") {
276
+
this.type = "child";
277
+
} else if (text === "+") {
278
+
this.type = "adjacent-sibling";
279
+
} else if (text === "~") {
280
+
this.type = "sibling";
281
+
}
282
+
283
+
}
284
+
285
+
Combinator.prototype = new SyntaxUnit();
286
+
Combinator.prototype.constructor = Combinator;
287
+
288
+
289
+
},{"../util/SyntaxUnit":26,"./Parser":6}],3:[function(require,module,exports){
290
+
"use strict";
291
+
292
+
module.exports = Matcher;
293
+
294
+
var StringReader = require("../util/StringReader");
295
+
var SyntaxError = require("../util/SyntaxError");
296
+
297
+
/**
298
+
* This class implements a combinator library for matcher functions.
299
+
* The combinators are described at:
300
+
* https://developer.mozilla.org/en-US/docs/Web/CSS/Value_definition_syntax#Component_value_combinators
301
+
*/
302
+
function Matcher(matchFunc, toString) {
303
+
this.match = function(expression) {
304
+
// Save/restore marks to ensure that failed matches always restore
305
+
// the original location in the expression.
306
+
var result;
307
+
expression.mark();
308
+
result = matchFunc(expression);
309
+
if (result) {
310
+
expression.drop();
311
+
} else {
312
+
expression.restore();
313
+
}
314
+
return result;
315
+
};
316
+
this.toString = typeof toString === "function" ? toString : function() {
317
+
return toString;
318
+
};
319
+
}
320
+
321
+
/** Precedence table of combinators. */
322
+
Matcher.prec = {
323
+
MOD: 5,
324
+
SEQ: 4,
325
+
ANDAND: 3,
326
+
OROR: 2,
327
+
ALT: 1
328
+
};
329
+
330
+
/** Simple recursive-descent grammar to build matchers from strings. */
331
+
Matcher.parse = function(str) {
332
+
var reader, eat, expr, oror, andand, seq, mod, term, result;
333
+
reader = new StringReader(str);
334
+
eat = function(matcher) {
335
+
var result = reader.readMatch(matcher);
336
+
if (result === null) {
337
+
throw new SyntaxError(
338
+
"Expected "+matcher, reader.getLine(), reader.getCol());
339
+
}
340
+
return result;
341
+
};
342
+
expr = function() {
343
+
// expr = oror (" | " oror)*
344
+
var m = [ oror() ];
345
+
while (reader.readMatch(" | ") !== null) {
346
+
m.push(oror());
347
+
}
348
+
return m.length === 1 ? m[0] : Matcher.alt.apply(Matcher, m);
349
+
};
350
+
oror = function() {
351
+
// oror = andand ( " || " andand)*
352
+
var m = [ andand() ];
353
+
while (reader.readMatch(" || ") !== null) {
354
+
m.push(andand());
355
+
}
356
+
return m.length === 1 ? m[0] : Matcher.oror.apply(Matcher, m);
357
+
};
358
+
andand = function() {
359
+
// andand = seq ( " && " seq)*
360
+
var m = [ seq() ];
361
+
while (reader.readMatch(" && ") !== null) {
362
+
m.push(seq());
363
+
}
364
+
return m.length === 1 ? m[0] : Matcher.andand.apply(Matcher, m);
365
+
};
366
+
seq = function() {
367
+
// seq = mod ( " " mod)*
368
+
var m = [ mod() ];
369
+
while (reader.readMatch(/^ (?![&|\]])/) !== null) {
370
+
m.push(mod());
371
+
}
372
+
return m.length === 1 ? m[0] : Matcher.seq.apply(Matcher, m);
373
+
};
374
+
mod = function() {
375
+
// mod = term ( "?" | "*" | "+" | "#" | "{<num>,<num>}" )?
376
+
var m = term();
377
+
if (reader.readMatch("?") !== null) {
378
+
return m.question();
379
+
} else if (reader.readMatch("*") !== null) {
380
+
return m.star();
381
+
} else if (reader.readMatch("+") !== null) {
382
+
return m.plus();
383
+
} else if (reader.readMatch("#") !== null) {
384
+
return m.hash();
385
+
} else if (reader.readMatch(/^\{\s*/) !== null) {
386
+
var min = eat(/^\d+/);
387
+
eat(/^\s*,\s*/);
388
+
var max = eat(/^\d+/);
389
+
eat(/^\s*\}/);
390
+
return m.braces(+min, +max);
391
+
}
392
+
return m;
393
+
};
394
+
term = function() {
395
+
// term = <nt> | literal | "[ " expression " ]"
396
+
if (reader.readMatch("[ ") !== null) {
397
+
var m = expr();
398
+
eat(" ]");
399
+
return m;
400
+
}
401
+
return Matcher.fromType(eat(/^[^ ?*+#{]+/));
402
+
};
403
+
result = expr();
404
+
if (!reader.eof()) {
405
+
throw new SyntaxError(
406
+
"Expected end of string", reader.getLine(), reader.getCol());
407
+
}
408
+
return result;
409
+
};
410
+
411
+
/**
412
+
* Convert a string to a matcher (parsing simple alternations),
413
+
* or do nothing if the argument is already a matcher.
414
+
*/
415
+
Matcher.cast = function(m) {
416
+
if (m instanceof Matcher) {
417
+
return m;
418
+
}
419
+
return Matcher.parse(m);
420
+
};
421
+
422
+
/**
423
+
* Create a matcher for a single type.
424
+
*/
425
+
Matcher.fromType = function(type) {
426
+
// Late require of ValidationTypes to break a dependency cycle.
427
+
var ValidationTypes = require("./ValidationTypes");
428
+
return new Matcher(function(expression) {
429
+
return expression.hasNext() && ValidationTypes.isType(expression, type);
430
+
}, type);
431
+
};
432
+
433
+
/**
434
+
* Create a matcher for one or more juxtaposed words, which all must
435
+
* occur, in the given order.
436
+
*/
437
+
Matcher.seq = function() {
438
+
var ms = Array.prototype.slice.call(arguments).map(Matcher.cast);
439
+
if (ms.length === 1) {
440
+
return ms[0];
441
+
}
442
+
return new Matcher(function(expression) {
443
+
var i, result = true;
444
+
for (i = 0; result && i < ms.length; i++) {
445
+
result = ms[i].match(expression);
446
+
}
447
+
return result;
448
+
}, function(prec) {
449
+
var p = Matcher.prec.SEQ;
450
+
var s = ms.map(function(m) {
451
+
return m.toString(p);
452
+
}).join(" ");
453
+
if (prec > p) {
454
+
s = "[ " + s + " ]";
455
+
}
456
+
return s;
457
+
});
458
+
};
459
+
460
+
/**
461
+
* Create a matcher for one or more alternatives, where exactly one
462
+
* must occur.
463
+
*/
464
+
Matcher.alt = function() {
465
+
var ms = Array.prototype.slice.call(arguments).map(Matcher.cast);
466
+
if (ms.length === 1) {
467
+
return ms[0];
468
+
}
469
+
return new Matcher(function(expression) {
470
+
var i, result = false;
471
+
for (i = 0; !result && i < ms.length; i++) {
472
+
result = ms[i].match(expression);
473
+
}
474
+
return result;
475
+
}, function(prec) {
476
+
var p = Matcher.prec.ALT;
477
+
var s = ms.map(function(m) {
478
+
return m.toString(p);
479
+
}).join(" | ");
480
+
if (prec > p) {
481
+
s = "[ " + s + " ]";
482
+
}
483
+
return s;
484
+
});
485
+
};
486
+
487
+
/**
488
+
* Create a matcher for two or more options. This implements the
489
+
* double bar (||) and double ampersand (&&) operators, as well as
490
+
* variants of && where some of the alternatives are optional.
491
+
* This will backtrack through even successful matches to try to
492
+
* maximize the number of items matched.
493
+
*/
494
+
Matcher.many = function(required) {
495
+
var ms = Array.prototype.slice.call(arguments, 1).reduce(function(acc, v) {
496
+
if (v.expand) {
497
+
// Insert all of the options for the given complex rule as
498
+
// individual options.
499
+
var ValidationTypes = require("./ValidationTypes");
500
+
acc.push.apply(acc, ValidationTypes.complex[v.expand].options);
501
+
} else {
502
+
acc.push(Matcher.cast(v));
503
+
}
504
+
return acc;
505
+
}, []);
506
+
507
+
if (required === true) {
508
+
required = ms.map(function() {
509
+
return true;
510
+
});
511
+
}
512
+
513
+
var result = new Matcher(function(expression) {
514
+
var seen = [], max = 0, pass = 0;
515
+
var success = function(matchCount) {
516
+
if (pass === 0) {
517
+
max = Math.max(matchCount, max);
518
+
return matchCount === ms.length;
519
+
} else {
520
+
return matchCount === max;
521
+
}
522
+
};
523
+
var tryMatch = function(matchCount) {
524
+
for (var i = 0; i < ms.length; i++) {
525
+
if (seen[i]) {
526
+
continue;
527
+
}
528
+
expression.mark();
529
+
if (ms[i].match(expression)) {
530
+
seen[i] = true;
531
+
// Increase matchCount iff this was a required element
532
+
// (or if all the elements are optional)
533
+
if (tryMatch(matchCount + ((required === false || required[i]) ? 1 : 0))) {
534
+
expression.drop();
535
+
return true;
536
+
}
537
+
// Backtrack: try *not* matching using this rule, and
538
+
// let's see if it leads to a better overall match.
539
+
expression.restore();
540
+
seen[i] = false;
541
+
} else {
542
+
expression.drop();
543
+
}
544
+
}
545
+
return success(matchCount);
546
+
};
547
+
if (!tryMatch(0)) {
548
+
// Couldn't get a complete match, retrace our steps to make the
549
+
// match with the maximum # of required elements.
550
+
pass++;
551
+
tryMatch(0);
552
+
}
553
+
554
+
if (required === false) {
555
+
return max > 0;
556
+
}
557
+
// Use finer-grained specification of which matchers are required.
558
+
for (var i = 0; i < ms.length; i++) {
559
+
if (required[i] && !seen[i]) {
560
+
return false;
561
+
}
562
+
}
563
+
return true;
564
+
}, function(prec) {
565
+
var p = required === false ? Matcher.prec.OROR : Matcher.prec.ANDAND;
566
+
var s = ms.map(function(m, i) {
567
+
if (required !== false && !required[i]) {
568
+
return m.toString(Matcher.prec.MOD) + "?";
569
+
}
570
+
return m.toString(p);
571
+
}).join(required === false ? " || " : " && ");
572
+
if (prec > p) {
573
+
s = "[ " + s + " ]";
574
+
}
575
+
return s;
576
+
});
577
+
result.options = ms;
578
+
return result;
579
+
};
580
+
581
+
/**
582
+
* Create a matcher for two or more options, where all options are
583
+
* mandatory but they may appear in any order.
584
+
*/
585
+
Matcher.andand = function() {
586
+
var args = Array.prototype.slice.call(arguments);
587
+
args.unshift(true);
588
+
return Matcher.many.apply(Matcher, args);
589
+
};
590
+
591
+
/**
592
+
* Create a matcher for two or more options, where options are
593
+
* optional and may appear in any order, but at least one must be
594
+
* present.
595
+
*/
596
+
Matcher.oror = function() {
597
+
var args = Array.prototype.slice.call(arguments);
598
+
args.unshift(false);
599
+
return Matcher.many.apply(Matcher, args);
600
+
};
601
+
602
+
/** Instance methods on Matchers. */
603
+
Matcher.prototype = {
604
+
constructor: Matcher,
605
+
// These are expected to be overridden in every instance.
606
+
match: function() { throw new Error("unimplemented"); },
607
+
toString: function() { throw new Error("unimplemented"); },
608
+
// This returns a standalone function to do the matching.
609
+
func: function() { return this.match.bind(this); },
610
+
// Basic combinators
611
+
then: function(m) { return Matcher.seq(this, m); },
612
+
or: function(m) { return Matcher.alt(this, m); },
613
+
andand: function(m) { return Matcher.many(true, this, m); },
614
+
oror: function(m) { return Matcher.many(false, this, m); },
615
+
// Component value multipliers
616
+
star: function() { return this.braces(0, Infinity, "*"); },
617
+
plus: function() { return this.braces(1, Infinity, "+"); },
618
+
question: function() { return this.braces(0, 1, "?"); },
619
+
hash: function() {
620
+
return this.braces(1, Infinity, "#", Matcher.cast(","));
621
+
},
622
+
braces: function(min, max, marker, optSep) {
623
+
var m1 = this, m2 = optSep ? optSep.then(this) : this;
624
+
if (!marker) {
625
+
marker = "{" + min + "," + max + "}";
626
+
}
627
+
return new Matcher(function(expression) {
628
+
var result = true, i;
629
+
for (i = 0; i < max; i++) {
630
+
if (i > 0 && optSep) {
631
+
result = m2.match(expression);
632
+
} else {
633
+
result = m1.match(expression);
634
+
}
635
+
if (!result) {
636
+
break;
637
+
}
638
+
}
639
+
return i >= min;
640
+
}, function() {
641
+
return m1.toString(Matcher.prec.MOD) + marker;
642
+
});
643
+
}
644
+
};
645
+
646
+
},{"../util/StringReader":24,"../util/SyntaxError":25,"./ValidationTypes":21}],4:[function(require,module,exports){
647
+
"use strict";
648
+
649
+
module.exports = MediaFeature;
650
+
651
+
var SyntaxUnit = require("../util/SyntaxUnit");
652
+
653
+
var Parser = require("./Parser");
654
+
655
+
/**
656
+
* Represents a media feature, such as max-width:500.
657
+
* @namespace parserlib.css
658
+
* @class MediaFeature
659
+
* @extends parserlib.util.SyntaxUnit
660
+
* @constructor
661
+
* @param {SyntaxUnit} name The name of the feature.
662
+
* @param {SyntaxUnit} value The value of the feature or null if none.
663
+
*/
664
+
function MediaFeature(name, value) {
665
+
666
+
SyntaxUnit.call(this, "(" + name + (value !== null ? ":" + value : "") + ")", name.startLine, name.startCol, Parser.MEDIA_FEATURE_TYPE);
667
+
668
+
/**
669
+
* The name of the media feature
670
+
* @type String
671
+
* @property name
672
+
*/
673
+
this.name = name;
674
+
675
+
/**
676
+
* The value for the feature or null if there is none.
677
+
* @type SyntaxUnit
678
+
* @property value
679
+
*/
680
+
this.value = value;
681
+
}
682
+
683
+
MediaFeature.prototype = new SyntaxUnit();
684
+
MediaFeature.prototype.constructor = MediaFeature;
685
+
686
+
687
+
},{"../util/SyntaxUnit":26,"./Parser":6}],5:[function(require,module,exports){
688
+
"use strict";
689
+
690
+
module.exports = MediaQuery;
691
+
692
+
var SyntaxUnit = require("../util/SyntaxUnit");
693
+
694
+
var Parser = require("./Parser");
695
+
696
+
/**
697
+
* Represents an individual media query.
698
+
* @namespace parserlib.css
699
+
* @class MediaQuery
700
+
* @extends parserlib.util.SyntaxUnit
701
+
* @constructor
702
+
* @param {String} modifier The modifier "not" or "only" (or null).
703
+
* @param {String} mediaType The type of media (i.e., "print").
704
+
* @param {Array} parts Array of selectors parts making up this selector.
705
+
* @param {int} line The line of text on which the unit resides.
706
+
* @param {int} col The column of text on which the unit resides.
707
+
*/
708
+
function MediaQuery(modifier, mediaType, features, line, col) {
709
+
710
+
SyntaxUnit.call(this, (modifier ? modifier + " ": "") + (mediaType ? mediaType : "") + (mediaType && features.length > 0 ? " and " : "") + features.join(" and "), line, col, Parser.MEDIA_QUERY_TYPE);
711
+
712
+
/**
713
+
* The media modifier ("not" or "only")
714
+
* @type String
715
+
* @property modifier
716
+
*/
717
+
this.modifier = modifier;
718
+
719
+
/**
720
+
* The mediaType (i.e., "print")
721
+
* @type String
722
+
* @property mediaType
723
+
*/
724
+
this.mediaType = mediaType;
725
+
726
+
/**
727
+
* The parts that make up the selector.
728
+
* @type Array
729
+
* @property features
730
+
*/
731
+
this.features = features;
732
+
733
+
}
734
+
735
+
MediaQuery.prototype = new SyntaxUnit();
736
+
MediaQuery.prototype.constructor = MediaQuery;
737
+
738
+
739
+
},{"../util/SyntaxUnit":26,"./Parser":6}],6:[function(require,module,exports){
740
+
"use strict";
741
+
742
+
module.exports = Parser;
743
+
744
+
var EventTarget = require("../util/EventTarget");
745
+
var SyntaxError = require("../util/SyntaxError");
746
+
var SyntaxUnit = require("../util/SyntaxUnit");
747
+
748
+
var Combinator = require("./Combinator");
749
+
var MediaFeature = require("./MediaFeature");
750
+
var MediaQuery = require("./MediaQuery");
751
+
var PropertyName = require("./PropertyName");
752
+
var PropertyValue = require("./PropertyValue");
753
+
var PropertyValuePart = require("./PropertyValuePart");
754
+
var Selector = require("./Selector");
755
+
var SelectorPart = require("./SelectorPart");
756
+
var SelectorSubPart = require("./SelectorSubPart");
757
+
var TokenStream = require("./TokenStream");
758
+
var Tokens = require("./Tokens");
759
+
var Validation = require("./Validation");
760
+
761
+
/**
762
+
* A CSS3 parser.
763
+
* @namespace parserlib.css
764
+
* @class Parser
765
+
* @constructor
766
+
* @param {Object} options (Optional) Various options for the parser:
767
+
* starHack (true|false) to allow IE6 star hack as valid,
768
+
* underscoreHack (true|false) to interpret leading underscores
769
+
* as IE6-7 targeting for known properties, ieFilters (true|false)
770
+
* to indicate that IE < 8 filters should be accepted and not throw
771
+
* syntax errors.
772
+
*/
773
+
function Parser(options) {
774
+
775
+
//inherit event functionality
776
+
EventTarget.call(this);
777
+
778
+
779
+
this.options = options || {};
780
+
781
+
this._tokenStream = null;
782
+
}
783
+
784
+
//Static constants
785
+
Parser.DEFAULT_TYPE = 0;
786
+
Parser.COMBINATOR_TYPE = 1;
787
+
Parser.MEDIA_FEATURE_TYPE = 2;
788
+
Parser.MEDIA_QUERY_TYPE = 3;
789
+
Parser.PROPERTY_NAME_TYPE = 4;
790
+
Parser.PROPERTY_VALUE_TYPE = 5;
791
+
Parser.PROPERTY_VALUE_PART_TYPE = 6;
792
+
Parser.SELECTOR_TYPE = 7;
793
+
Parser.SELECTOR_PART_TYPE = 8;
794
+
Parser.SELECTOR_SUB_PART_TYPE = 9;
795
+
796
+
Parser.prototype = function() {
797
+
798
+
var proto = new EventTarget(), //new prototype
799
+
prop,
800
+
additions = {
801
+
__proto__: null,
802
+
803
+
//restore constructor
804
+
constructor: Parser,
805
+
806
+
//instance constants - yuck
807
+
DEFAULT_TYPE : 0,
808
+
COMBINATOR_TYPE : 1,
809
+
MEDIA_FEATURE_TYPE : 2,
810
+
MEDIA_QUERY_TYPE : 3,
811
+
PROPERTY_NAME_TYPE : 4,
812
+
PROPERTY_VALUE_TYPE : 5,
813
+
PROPERTY_VALUE_PART_TYPE : 6,
814
+
SELECTOR_TYPE : 7,
815
+
SELECTOR_PART_TYPE : 8,
816
+
SELECTOR_SUB_PART_TYPE : 9,
817
+
818
+
//-----------------------------------------------------------------
819
+
// Grammar
820
+
//-----------------------------------------------------------------
821
+
822
+
_stylesheet: function() {
823
+
824
+
/*
825
+
* stylesheet
826
+
* : [ CHARSET_SYM S* STRING S* ';' ]?
827
+
* [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
828
+
* [ namespace [S|CDO|CDC]* ]*
829
+
* [ [ ruleset | media | page | font_face | keyframes_rule | supports_rule ] [S|CDO|CDC]* ]*
830
+
* ;
831
+
*/
832
+
833
+
var tokenStream = this._tokenStream,
834
+
count,
835
+
token,
836
+
tt;
837
+
838
+
this.fire("startstylesheet");
839
+
840
+
//try to read character set
841
+
this._charset();
842
+
843
+
this._skipCruft();
844
+
845
+
//try to read imports - may be more than one
846
+
while (tokenStream.peek() === Tokens.IMPORT_SYM) {
847
+
this._import();
848
+
this._skipCruft();
849
+
}
850
+
851
+
//try to read namespaces - may be more than one
852
+
while (tokenStream.peek() === Tokens.NAMESPACE_SYM) {
853
+
this._namespace();
854
+
this._skipCruft();
855
+
}
856
+
857
+
//get the next token
858
+
tt = tokenStream.peek();
859
+
860
+
//try to read the rest
861
+
while (tt > Tokens.EOF) {
862
+
863
+
try {
864
+
865
+
switch (tt) {
866
+
case Tokens.MEDIA_SYM:
867
+
this._media();
868
+
this._skipCruft();
869
+
break;
870
+
case Tokens.PAGE_SYM:
871
+
this._page();
872
+
this._skipCruft();
873
+
break;
874
+
case Tokens.FONT_FACE_SYM:
875
+
this._font_face();
876
+
this._skipCruft();
877
+
break;
878
+
case Tokens.KEYFRAMES_SYM:
879
+
this._keyframes();
880
+
this._skipCruft();
881
+
break;
882
+
case Tokens.VIEWPORT_SYM:
883
+
this._viewport();
884
+
this._skipCruft();
885
+
break;
886
+
case Tokens.DOCUMENT_SYM:
887
+
this._document();
888
+
this._skipCruft();
889
+
break;
890
+
case Tokens.SUPPORTS_SYM:
891
+
this._supports();
892
+
this._skipCruft();
893
+
break;
894
+
case Tokens.UNKNOWN_SYM: //unknown @ rule
895
+
tokenStream.get();
896
+
if (!this.options.strict) {
897
+
898
+
//fire error event
899
+
this.fire({
900
+
type: "error",
901
+
error: null,
902
+
message: "Unknown @ rule: " + tokenStream.LT(0).value + ".",
903
+
line: tokenStream.LT(0).startLine,
904
+
col: tokenStream.LT(0).startCol
905
+
});
906
+
907
+
//skip braces
908
+
count=0;
909
+
while (tokenStream.advance([Tokens.LBRACE, Tokens.RBRACE]) === Tokens.LBRACE) {
910
+
count++; //keep track of nesting depth
911
+
}
912
+
913
+
while (count) {
914
+
tokenStream.advance([Tokens.RBRACE]);
915
+
count--;
916
+
}
917
+
918
+
} else {
919
+
//not a syntax error, rethrow it
920
+
throw new SyntaxError("Unknown @ rule.", tokenStream.LT(0).startLine, tokenStream.LT(0).startCol);
921
+
}
922
+
break;
923
+
case Tokens.S:
924
+
this._readWhitespace();
925
+
break;
926
+
default:
927
+
if (!this._ruleset()) {
928
+
929
+
//error handling for known issues
930
+
switch (tt) {
931
+
case Tokens.CHARSET_SYM:
932
+
token = tokenStream.LT(1);
933
+
this._charset(false);
934
+
throw new SyntaxError("@charset not allowed here.", token.startLine, token.startCol);
935
+
case Tokens.IMPORT_SYM:
936
+
token = tokenStream.LT(1);
937
+
this._import(false);
938
+
throw new SyntaxError("@import not allowed here.", token.startLine, token.startCol);
939
+
case Tokens.NAMESPACE_SYM:
940
+
token = tokenStream.LT(1);
941
+
this._namespace(false);
942
+
throw new SyntaxError("@namespace not allowed here.", token.startLine, token.startCol);
943
+
default:
944
+
tokenStream.get(); //get the last token
945
+
this._unexpectedToken(tokenStream.token());
946
+
}
947
+
948
+
}
949
+
}
950
+
} catch (ex) {
951
+
if (ex instanceof SyntaxError && !this.options.strict) {
952
+
this.fire({
953
+
type: "error",
954
+
error: ex,
955
+
message: ex.message,
956
+
line: ex.line,
957
+
col: ex.col
958
+
});
959
+
} else {
960
+
throw ex;
961
+
}
962
+
}
963
+
964
+
tt = tokenStream.peek();
965
+
}
966
+
967
+
if (tt !== Tokens.EOF) {
968
+
this._unexpectedToken(tokenStream.token());
969
+
}
970
+
971
+
this.fire("endstylesheet");
972
+
},
973
+
974
+
_charset: function(emit) {
975
+
var tokenStream = this._tokenStream,
976
+
charset,
977
+
token,
978
+
line,
979
+
col;
980
+
981
+
if (tokenStream.match(Tokens.CHARSET_SYM)) {
982
+
line = tokenStream.token().startLine;
983
+
col = tokenStream.token().startCol;
984
+
985
+
this._readWhitespace();
986
+
tokenStream.mustMatch(Tokens.STRING);
987
+
988
+
token = tokenStream.token();
989
+
charset = token.value;
990
+
991
+
this._readWhitespace();
992
+
tokenStream.mustMatch(Tokens.SEMICOLON);
993
+
994
+
if (emit !== false) {
995
+
this.fire({
996
+
type: "charset",
997
+
charset:charset,
998
+
line: line,
999
+
col: col
1000
+
});
1001
+
}
1002
+
}
1003
+
},
1004
+
1005
+
_import: function(emit) {
1006
+
/*
1007
+
* import
1008
+
* : IMPORT_SYM S*
1009
+
* [STRING|URI] S* media_query_list? ';' S*
1010
+
*/
1011
+
1012
+
var tokenStream = this._tokenStream,
1013
+
uri,
1014
+
importToken,
1015
+
mediaList = [];
1016
+
1017
+
//read import symbol
1018
+
tokenStream.mustMatch(Tokens.IMPORT_SYM);
1019
+
importToken = tokenStream.token();
1020
+
this._readWhitespace();
1021
+
1022
+
tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
1023
+
1024
+
//grab the URI value
1025
+
uri = tokenStream.token().value.replace(/^(?:url\()?["']?([^"']+?)["']?\)?$/, "$1");
1026
+
1027
+
this._readWhitespace();
1028
+
1029
+
mediaList = this._media_query_list();
1030
+
1031
+
//must end with a semicolon
1032
+
tokenStream.mustMatch(Tokens.SEMICOLON);
1033
+
this._readWhitespace();
1034
+
1035
+
if (emit !== false) {
1036
+
this.fire({
1037
+
type: "import",
1038
+
uri: uri,
1039
+
media: mediaList,
1040
+
line: importToken.startLine,
1041
+
col: importToken.startCol
1042
+
});
1043
+
}
1044
+
1045
+
},
1046
+
1047
+
_namespace: function(emit) {
1048
+
/*
1049
+
* namespace
1050
+
* : NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
1051
+
*/
1052
+
1053
+
var tokenStream = this._tokenStream,
1054
+
line,
1055
+
col,
1056
+
prefix,
1057
+
uri;
1058
+
1059
+
//read import symbol
1060
+
tokenStream.mustMatch(Tokens.NAMESPACE_SYM);
1061
+
line = tokenStream.token().startLine;
1062
+
col = tokenStream.token().startCol;
1063
+
this._readWhitespace();
1064
+
1065
+
//it's a namespace prefix - no _namespace_prefix() method because it's just an IDENT
1066
+
if (tokenStream.match(Tokens.IDENT)) {
1067
+
prefix = tokenStream.token().value;
1068
+
this._readWhitespace();
1069
+
}
1070
+
1071
+
tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
1072
+
/*if (!tokenStream.match(Tokens.STRING)){
1073
+
tokenStream.mustMatch(Tokens.URI);
1074
+
}*/
1075
+
1076
+
//grab the URI value
1077
+
uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1");
1078
+
1079
+
this._readWhitespace();
1080
+
1081
+
//must end with a semicolon
1082
+
tokenStream.mustMatch(Tokens.SEMICOLON);
1083
+
this._readWhitespace();
1084
+
1085
+
if (emit !== false) {
1086
+
this.fire({
1087
+
type: "namespace",
1088
+
prefix: prefix,
1089
+
uri: uri,
1090
+
line: line,
1091
+
col: col
1092
+
});
1093
+
}
1094
+
1095
+
},
1096
+
1097
+
_supports: function(emit) {
1098
+
/*
1099
+
* supports_rule
1100
+
* : SUPPORTS_SYM S* supports_condition S* group_rule_body
1101
+
* ;
1102
+
*/
1103
+
var tokenStream = this._tokenStream,
1104
+
line,
1105
+
col;
1106
+
1107
+
if (tokenStream.match(Tokens.SUPPORTS_SYM)) {
1108
+
line = tokenStream.token().startLine;
1109
+
col = tokenStream.token().startCol;
1110
+
1111
+
this._readWhitespace();
1112
+
this._supports_condition();
1113
+
this._readWhitespace();
1114
+
1115
+
tokenStream.mustMatch(Tokens.LBRACE);
1116
+
this._readWhitespace();
1117
+
1118
+
if (emit !== false) {
1119
+
this.fire({
1120
+
type: "startsupports",
1121
+
line: line,
1122
+
col: col
1123
+
});
1124
+
}
1125
+
1126
+
while (true) {
1127
+
if (!this._ruleset()) {
1128
+
break;
1129
+
}
1130
+
}
1131
+
1132
+
tokenStream.mustMatch(Tokens.RBRACE);
1133
+
this._readWhitespace();
1134
+
1135
+
this.fire({
1136
+
type: "endsupports",
1137
+
line: line,
1138
+
col: col
1139
+
});
1140
+
}
1141
+
},
1142
+
1143
+
_supports_condition: function() {
1144
+
/*
1145
+
* supports_condition
1146
+
* : supports_negation | supports_conjunction | supports_disjunction |
1147
+
* supports_condition_in_parens
1148
+
* ;
1149
+
*/
1150
+
var tokenStream = this._tokenStream,
1151
+
ident;
1152
+
1153
+
if (tokenStream.match(Tokens.IDENT)) {
1154
+
ident = tokenStream.token().value.toLowerCase();
1155
+
1156
+
if (ident === "not") {
1157
+
tokenStream.mustMatch(Tokens.S);
1158
+
this._supports_condition_in_parens();
1159
+
} else {
1160
+
tokenStream.unget();
1161
+
}
1162
+
} else {
1163
+
this._supports_condition_in_parens();
1164
+
this._readWhitespace();
1165
+
1166
+
while (tokenStream.peek() === Tokens.IDENT) {
1167
+
ident = tokenStream.LT(1).value.toLowerCase();
1168
+
if (ident === "and" || ident === "or") {
1169
+
tokenStream.mustMatch(Tokens.IDENT);
1170
+
this._readWhitespace();
1171
+
this._supports_condition_in_parens();
1172
+
this._readWhitespace();
1173
+
}
1174
+
}
1175
+
}
1176
+
},
1177
+
1178
+
_supports_condition_in_parens: function() {
1179
+
/*
1180
+
* supports_condition_in_parens
1181
+
* : ( '(' S* supports_condition S* ')' ) | supports_declaration_condition |
1182
+
* general_enclosed
1183
+
* ;
1184
+
*/
1185
+
var tokenStream = this._tokenStream,
1186
+
ident;
1187
+
1188
+
if (tokenStream.match(Tokens.LPAREN)) {
1189
+
this._readWhitespace();
1190
+
if (tokenStream.match(Tokens.IDENT)) {
1191
+
// look ahead for not keyword, if not given, continue with declaration condition.
1192
+
ident = tokenStream.token().value.toLowerCase();
1193
+
if (ident === "not") {
1194
+
this._readWhitespace();
1195
+
this._supports_condition();
1196
+
this._readWhitespace();
1197
+
tokenStream.mustMatch(Tokens.RPAREN);
1198
+
} else {
1199
+
tokenStream.unget();
1200
+
this._supports_declaration_condition(false);
1201
+
}
1202
+
} else {
1203
+
this._supports_condition();
1204
+
this._readWhitespace();
1205
+
tokenStream.mustMatch(Tokens.RPAREN);
1206
+
}
1207
+
} else {
1208
+
this._supports_declaration_condition();
1209
+
}
1210
+
},
1211
+
1212
+
_supports_declaration_condition: function(requireStartParen) {
1213
+
/*
1214
+
* supports_declaration_condition
1215
+
* : '(' S* declaration ')'
1216
+
* ;
1217
+
*/
1218
+
var tokenStream = this._tokenStream;
1219
+
1220
+
if (requireStartParen !== false) {
1221
+
tokenStream.mustMatch(Tokens.LPAREN);
1222
+
}
1223
+
this._readWhitespace();
1224
+
this._declaration();
1225
+
tokenStream.mustMatch(Tokens.RPAREN);
1226
+
},
1227
+
1228
+
_media: function() {
1229
+
/*
1230
+
* media
1231
+
* : MEDIA_SYM S* media_query_list S* '{' S* ruleset* '}' S*
1232
+
* ;
1233
+
*/
1234
+
var tokenStream = this._tokenStream,
1235
+
line,
1236
+
col,
1237
+
mediaList;// = [];
1238
+
1239
+
//look for @media
1240
+
tokenStream.mustMatch(Tokens.MEDIA_SYM);
1241
+
line = tokenStream.token().startLine;
1242
+
col = tokenStream.token().startCol;
1243
+
1244
+
this._readWhitespace();
1245
+
1246
+
mediaList = this._media_query_list();
1247
+
1248
+
tokenStream.mustMatch(Tokens.LBRACE);
1249
+
this._readWhitespace();
1250
+
1251
+
this.fire({
1252
+
type: "startmedia",
1253
+
media: mediaList,
1254
+
line: line,
1255
+
col: col
1256
+
});
1257
+
1258
+
while (true) {
1259
+
if (tokenStream.peek() === Tokens.PAGE_SYM) {
1260
+
this._page();
1261
+
} else if (tokenStream.peek() === Tokens.FONT_FACE_SYM) {
1262
+
this._font_face();
1263
+
} else if (tokenStream.peek() === Tokens.VIEWPORT_SYM) {
1264
+
this._viewport();
1265
+
} else if (tokenStream.peek() === Tokens.DOCUMENT_SYM) {
1266
+
this._document();
1267
+
} else if (tokenStream.peek() === Tokens.SUPPORTS_SYM) {
1268
+
this._supports();
1269
+
} else if (tokenStream.peek() === Tokens.MEDIA_SYM) {
1270
+
this._media();
1271
+
} else if (!this._ruleset()) {
1272
+
break;
1273
+
}
1274
+
}
1275
+
1276
+
tokenStream.mustMatch(Tokens.RBRACE);
1277
+
this._readWhitespace();
1278
+
1279
+
this.fire({
1280
+
type: "endmedia",
1281
+
media: mediaList,
1282
+
line: line,
1283
+
col: col
1284
+
});
1285
+
},
1286
+
1287
+
1288
+
//CSS3 Media Queries
1289
+
_media_query_list: function() {
1290
+
/*
1291
+
* media_query_list
1292
+
* : S* [media_query [ ',' S* media_query ]* ]?
1293
+
* ;
1294
+
*/
1295
+
var tokenStream = this._tokenStream,
1296
+
mediaList = [];
1297
+
1298
+
1299
+
this._readWhitespace();
1300
+
1301
+
if (tokenStream.peek() === Tokens.IDENT || tokenStream.peek() === Tokens.LPAREN) {
1302
+
mediaList.push(this._media_query());
1303
+
}
1304
+
1305
+
while (tokenStream.match(Tokens.COMMA)) {
1306
+
this._readWhitespace();
1307
+
mediaList.push(this._media_query());
1308
+
}
1309
+
1310
+
return mediaList;
1311
+
},
1312
+
1313
+
/*
1314
+
* Note: "expression" in the grammar maps to the _media_expression
1315
+
* method.
1316
+
1317
+
*/
1318
+
_media_query: function() {
1319
+
/*
1320
+
* media_query
1321
+
* : [ONLY | NOT]? S* media_type S* [ AND S* expression ]*
1322
+
* | expression [ AND S* expression ]*
1323
+
* ;
1324
+
*/
1325
+
var tokenStream = this._tokenStream,
1326
+
type = null,
1327
+
ident = null,
1328
+
token = null,
1329
+
expressions = [];
1330
+
1331
+
if (tokenStream.match(Tokens.IDENT)) {
1332
+
ident = tokenStream.token().value.toLowerCase();
1333
+
1334
+
//since there's no custom tokens for these, need to manually check
1335
+
if (ident !== "only" && ident !== "not") {
1336
+
tokenStream.unget();
1337
+
ident = null;
1338
+
} else {
1339
+
token = tokenStream.token();
1340
+
}
1341
+
}
1342
+
1343
+
this._readWhitespace();
1344
+
1345
+
if (tokenStream.peek() === Tokens.IDENT) {
1346
+
type = this._media_type();
1347
+
if (token === null) {
1348
+
token = tokenStream.token();
1349
+
}
1350
+
} else if (tokenStream.peek() === Tokens.LPAREN) {
1351
+
if (token === null) {
1352
+
token = tokenStream.LT(1);
1353
+
}
1354
+
expressions.push(this._media_expression());
1355
+
}
1356
+
1357
+
if (type === null && expressions.length === 0) {
1358
+
return null;
1359
+
} else {
1360
+
this._readWhitespace();
1361
+
while (tokenStream.match(Tokens.IDENT)) {
1362
+
if (tokenStream.token().value.toLowerCase() !== "and") {
1363
+
this._unexpectedToken(tokenStream.token());
1364
+
}
1365
+
1366
+
this._readWhitespace();
1367
+
expressions.push(this._media_expression());
1368
+
}
1369
+
}
1370
+
1371
+
return new MediaQuery(ident, type, expressions, token.startLine, token.startCol);
1372
+
},
1373
+
1374
+
//CSS3 Media Queries
1375
+
_media_type: function() {
1376
+
/*
1377
+
* media_type
1378
+
* : IDENT
1379
+
* ;
1380
+
*/
1381
+
return this._media_feature();
1382
+
},
1383
+
1384
+
/**
1385
+
* Note: in CSS3 Media Queries, this is called "expression".
1386
+
* Renamed here to avoid conflict with CSS3 Selectors
1387
+
* definition of "expression". Also note that "expr" in the
1388
+
* grammar now maps to "expression" from CSS3 selectors.
1389
+
* @method _media_expression
1390
+
* @private
1391
+
*/
1392
+
_media_expression: function() {
1393
+
/*
1394
+
* expression
1395
+
* : '(' S* media_feature S* [ ':' S* expr ]? ')' S*
1396
+
* ;
1397
+
*/
1398
+
var tokenStream = this._tokenStream,
1399
+
feature = null,
1400
+
token,
1401
+
expression = null;
1402
+
1403
+
tokenStream.mustMatch(Tokens.LPAREN);
1404
+
1405
+
feature = this._media_feature();
1406
+
this._readWhitespace();
1407
+
1408
+
if (tokenStream.match(Tokens.COLON)) {
1409
+
this._readWhitespace();
1410
+
token = tokenStream.LT(1);
1411
+
expression = this._expression();
1412
+
}
1413
+
1414
+
tokenStream.mustMatch(Tokens.RPAREN);
1415
+
this._readWhitespace();
1416
+
1417
+
return new MediaFeature(feature, expression ? new SyntaxUnit(expression, token.startLine, token.startCol) : null);
1418
+
},
1419
+
1420
+
//CSS3 Media Queries
1421
+
_media_feature: function() {
1422
+
/*
1423
+
* media_feature
1424
+
* : IDENT
1425
+
* ;
1426
+
*/
1427
+
var tokenStream = this._tokenStream;
1428
+
1429
+
this._readWhitespace();
1430
+
1431
+
tokenStream.mustMatch(Tokens.IDENT);
1432
+
1433
+
return SyntaxUnit.fromToken(tokenStream.token());
1434
+
},
1435
+
1436
+
//CSS3 Paged Media
1437
+
_page: function() {
1438
+
/*
1439
+
* page:
1440
+
* PAGE_SYM S* IDENT? pseudo_page? S*
1441
+
* '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
1442
+
* ;
1443
+
*/
1444
+
var tokenStream = this._tokenStream,
1445
+
line,
1446
+
col,
1447
+
identifier = null,
1448
+
pseudoPage = null;
1449
+
1450
+
//look for @page
1451
+
tokenStream.mustMatch(Tokens.PAGE_SYM);
1452
+
line = tokenStream.token().startLine;
1453
+
col = tokenStream.token().startCol;
1454
+
1455
+
this._readWhitespace();
1456
+
1457
+
if (tokenStream.match(Tokens.IDENT)) {
1458
+
identifier = tokenStream.token().value;
1459
+
1460
+
//The value 'auto' may not be used as a page name and MUST be treated as a syntax error.
1461
+
if (identifier.toLowerCase() === "auto") {
1462
+
this._unexpectedToken(tokenStream.token());
1463
+
}
1464
+
}
1465
+
1466
+
//see if there's a colon upcoming
1467
+
if (tokenStream.peek() === Tokens.COLON) {
1468
+
pseudoPage = this._pseudo_page();
1469
+
}
1470
+
1471
+
this._readWhitespace();
1472
+
1473
+
this.fire({
1474
+
type: "startpage",
1475
+
id: identifier,
1476
+
pseudo: pseudoPage,
1477
+
line: line,
1478
+
col: col
1479
+
});
1480
+
1481
+
this._readDeclarations(true, true);
1482
+
1483
+
this.fire({
1484
+
type: "endpage",
1485
+
id: identifier,
1486
+
pseudo: pseudoPage,
1487
+
line: line,
1488
+
col: col
1489
+
});
1490
+
1491
+
},
1492
+
1493
+
//CSS3 Paged Media
1494
+
_margin: function() {
1495
+
/*
1496
+
* margin :
1497
+
* margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S*
1498
+
* ;
1499
+
*/
1500
+
var tokenStream = this._tokenStream,
1501
+
line,
1502
+
col,
1503
+
marginSym = this._margin_sym();
1504
+
1505
+
if (marginSym) {
1506
+
line = tokenStream.token().startLine;
1507
+
col = tokenStream.token().startCol;
1508
+
1509
+
this.fire({
1510
+
type: "startpagemargin",
1511
+
margin: marginSym,
1512
+
line: line,
1513
+
col: col
1514
+
});
1515
+
1516
+
this._readDeclarations(true);
1517
+
1518
+
this.fire({
1519
+
type: "endpagemargin",
1520
+
margin: marginSym,
1521
+
line: line,
1522
+
col: col
1523
+
});
1524
+
return true;
1525
+
} else {
1526
+
return false;
1527
+
}
1528
+
},
1529
+
1530
+
//CSS3 Paged Media
1531
+
_margin_sym: function() {
1532
+
1533
+
/*
1534
+
* margin_sym :
1535
+
* TOPLEFTCORNER_SYM |
1536
+
* TOPLEFT_SYM |
1537
+
* TOPCENTER_SYM |
1538
+
* TOPRIGHT_SYM |
1539
+
* TOPRIGHTCORNER_SYM |
1540
+
* BOTTOMLEFTCORNER_SYM |
1541
+
* BOTTOMLEFT_SYM |
1542
+
* BOTTOMCENTER_SYM |
1543
+
* BOTTOMRIGHT_SYM |
1544
+
* BOTTOMRIGHTCORNER_SYM |
1545
+
* LEFTTOP_SYM |
1546
+
* LEFTMIDDLE_SYM |
1547
+
* LEFTBOTTOM_SYM |
1548
+
* RIGHTTOP_SYM |
1549
+
* RIGHTMIDDLE_SYM |
1550
+
* RIGHTBOTTOM_SYM
1551
+
* ;
1552
+
*/
1553
+
1554
+
var tokenStream = this._tokenStream;
1555
+
1556
+
if (tokenStream.match([Tokens.TOPLEFTCORNER_SYM, Tokens.TOPLEFT_SYM,
1557
+
Tokens.TOPCENTER_SYM, Tokens.TOPRIGHT_SYM, Tokens.TOPRIGHTCORNER_SYM,
1558
+
Tokens.BOTTOMLEFTCORNER_SYM, Tokens.BOTTOMLEFT_SYM,
1559
+
Tokens.BOTTOMCENTER_SYM, Tokens.BOTTOMRIGHT_SYM,
1560
+
Tokens.BOTTOMRIGHTCORNER_SYM, Tokens.LEFTTOP_SYM,
1561
+
Tokens.LEFTMIDDLE_SYM, Tokens.LEFTBOTTOM_SYM, Tokens.RIGHTTOP_SYM,
1562
+
Tokens.RIGHTMIDDLE_SYM, Tokens.RIGHTBOTTOM_SYM])) {
1563
+
return SyntaxUnit.fromToken(tokenStream.token());
1564
+
} else {
1565
+
return null;
1566
+
}
1567
+
1568
+
},
1569
+
1570
+
_pseudo_page: function() {
1571
+
/*
1572
+
* pseudo_page
1573
+
* : ':' IDENT
1574
+
* ;
1575
+
*/
1576
+
1577
+
var tokenStream = this._tokenStream;
1578
+
1579
+
tokenStream.mustMatch(Tokens.COLON);
1580
+
tokenStream.mustMatch(Tokens.IDENT);
1581
+
1582
+
//TODO: CSS3 Paged Media says only "left", "center", and "right" are allowed
1583
+
1584
+
return tokenStream.token().value;
1585
+
},
1586
+
1587
+
_font_face: function() {
1588
+
/*
1589
+
* font_face
1590
+
* : FONT_FACE_SYM S*
1591
+
* '{' S* declaration [ ';' S* declaration ]* '}' S*
1592
+
* ;
1593
+
*/
1594
+
var tokenStream = this._tokenStream,
1595
+
line,
1596
+
col;
1597
+
1598
+
//look for @page
1599
+
tokenStream.mustMatch(Tokens.FONT_FACE_SYM);
1600
+
line = tokenStream.token().startLine;
1601
+
col = tokenStream.token().startCol;
1602
+
1603
+
this._readWhitespace();
1604
+
1605
+
this.fire({
1606
+
type: "startfontface",
1607
+
line: line,
1608
+
col: col
1609
+
});
1610
+
1611
+
this._readDeclarations(true);
1612
+
1613
+
this.fire({
1614
+
type: "endfontface",
1615
+
line: line,
1616
+
col: col
1617
+
});
1618
+
},
1619
+
1620
+
_viewport: function() {
1621
+
/*
1622
+
* viewport
1623
+
* : VIEWPORT_SYM S*
1624
+
* '{' S* declaration? [ ';' S* declaration? ]* '}' S*
1625
+
* ;
1626
+
*/
1627
+
var tokenStream = this._tokenStream,
1628
+
line,
1629
+
col;
1630
+
1631
+
tokenStream.mustMatch(Tokens.VIEWPORT_SYM);
1632
+
line = tokenStream.token().startLine;
1633
+
col = tokenStream.token().startCol;
1634
+
1635
+
this._readWhitespace();
1636
+
1637
+
this.fire({
1638
+
type: "startviewport",
1639
+
line: line,
1640
+
col: col
1641
+
});
1642
+
1643
+
this._readDeclarations(true);
1644
+
1645
+
this.fire({
1646
+
type: "endviewport",
1647
+
line: line,
1648
+
col: col
1649
+
});
1650
+
1651
+
},
1652
+
1653
+
_document: function() {
1654
+
/*
1655
+
* document
1656
+
* : DOCUMENT_SYM S*
1657
+
* _document_function [ ',' S* _document_function ]* S*
1658
+
* '{' S* ruleset* '}'
1659
+
* ;
1660
+
*/
1661
+
1662
+
var tokenStream = this._tokenStream,
1663
+
token,
1664
+
functions = [],
1665
+
prefix = "";
1666
+
1667
+
tokenStream.mustMatch(Tokens.DOCUMENT_SYM);
1668
+
token = tokenStream.token();
1669
+
if (/^@\-([^\-]+)\-/.test(token.value)) {
1670
+
prefix = RegExp.$1;
1671
+
}
1672
+
1673
+
this._readWhitespace();
1674
+
functions.push(this._document_function());
1675
+
1676
+
while (tokenStream.match(Tokens.COMMA)) {
1677
+
this._readWhitespace();
1678
+
functions.push(this._document_function());
1679
+
}
1680
+
1681
+
tokenStream.mustMatch(Tokens.LBRACE);
1682
+
this._readWhitespace();
1683
+
1684
+
this.fire({
1685
+
type: "startdocument",
1686
+
functions: functions,
1687
+
prefix: prefix,
1688
+
line: token.startLine,
1689
+
col: token.startCol
1690
+
});
1691
+
1692
+
var ok = true;
1693
+
while (ok) {
1694
+
switch (tokenStream.peek()) {
1695
+
case Tokens.PAGE_SYM:
1696
+
this._page();
1697
+
break;
1698
+
case Tokens.FONT_FACE_SYM:
1699
+
this._font_face();
1700
+
break;
1701
+
case Tokens.VIEWPORT_SYM:
1702
+
this._viewport();
1703
+
break;
1704
+
case Tokens.MEDIA_SYM:
1705
+
this._media();
1706
+
break;
1707
+
case Tokens.KEYFRAMES_SYM:
1708
+
this._keyframes();
1709
+
break;
1710
+
case Tokens.DOCUMENT_SYM:
1711
+
this._document();
1712
+
break;
1713
+
default:
1714
+
ok = Boolean(this._ruleset());
1715
+
}
1716
+
}
1717
+
1718
+
tokenStream.mustMatch(Tokens.RBRACE);
1719
+
token = tokenStream.token();
1720
+
this._readWhitespace();
1721
+
1722
+
this.fire({
1723
+
type: "enddocument",
1724
+
functions: functions,
1725
+
prefix: prefix,
1726
+
line: token.startLine,
1727
+
col: token.startCol
1728
+
});
1729
+
},
1730
+
1731
+
_document_function: function() {
1732
+
/*
1733
+
* document_function
1734
+
* : function | URI S*
1735
+
* ;
1736
+
*/
1737
+
1738
+
var tokenStream = this._tokenStream,
1739
+
value;
1740
+
1741
+
if (tokenStream.match(Tokens.URI)) {
1742
+
value = tokenStream.token().value;
1743
+
this._readWhitespace();
1744
+
} else {
1745
+
value = this._function();
1746
+
}
1747
+
1748
+
return value;
1749
+
},
1750
+
1751
+
_operator: function(inFunction) {
1752
+
1753
+
/*
1754
+
* operator (outside function)
1755
+
* : '/' S* | ',' S* | /( empty )/
1756
+
* operator (inside function)
1757
+
* : '/' S* | '+' S* | '*' S* | '-' S* /( empty )/
1758
+
* ;
1759
+
*/
1760
+
1761
+
var tokenStream = this._tokenStream,
1762
+
token = null;
1763
+
1764
+
if (tokenStream.match([Tokens.SLASH, Tokens.COMMA]) ||
1765
+
(inFunction && tokenStream.match([Tokens.PLUS, Tokens.STAR, Tokens.MINUS]))) {
1766
+
token = tokenStream.token();
1767
+
this._readWhitespace();
1768
+
}
1769
+
return token ? PropertyValuePart.fromToken(token) : null;
1770
+
1771
+
},
1772
+
1773
+
_combinator: function() {
1774
+
1775
+
/*
1776
+
* combinator
1777
+
* : PLUS S* | GREATER S* | TILDE S* | S+
1778
+
* ;
1779
+
*/
1780
+
1781
+
var tokenStream = this._tokenStream,
1782
+
value = null,
1783
+
token;
1784
+
1785
+
if (tokenStream.match([Tokens.PLUS, Tokens.GREATER, Tokens.TILDE])) {
1786
+
token = tokenStream.token();
1787
+
value = new Combinator(token.value, token.startLine, token.startCol);
1788
+
this._readWhitespace();
1789
+
}
1790
+
1791
+
return value;
1792
+
},
1793
+
1794
+
_unary_operator: function() {
1795
+
1796
+
/*
1797
+
* unary_operator
1798
+
* : '-' | '+'
1799
+
* ;
1800
+
*/
1801
+
1802
+
var tokenStream = this._tokenStream;
1803
+
1804
+
if (tokenStream.match([Tokens.MINUS, Tokens.PLUS])) {
1805
+
return tokenStream.token().value;
1806
+
} else {
1807
+
return null;
1808
+
}
1809
+
},
1810
+
1811
+
_property: function() {
1812
+
1813
+
/*
1814
+
* property
1815
+
* : IDENT S*
1816
+
* ;
1817
+
*/
1818
+
1819
+
var tokenStream = this._tokenStream,
1820
+
value = null,
1821
+
hack = null,
1822
+
tokenValue,
1823
+
token,
1824
+
line,
1825
+
col;
1826
+
1827
+
//check for star hack - throws error if not allowed
1828
+
if (tokenStream.peek() === Tokens.STAR && this.options.starHack) {
1829
+
tokenStream.get();
1830
+
token = tokenStream.token();
1831
+
hack = token.value;
1832
+
line = token.startLine;
1833
+
col = token.startCol;
1834
+
}
1835
+
1836
+
if (tokenStream.match(Tokens.IDENT)) {
1837
+
token = tokenStream.token();
1838
+
tokenValue = token.value;
1839
+
1840
+
//check for underscore hack - no error if not allowed because it's valid CSS syntax
1841
+
if (tokenValue.charAt(0) === "_" && this.options.underscoreHack) {
1842
+
hack = "_";
1843
+
tokenValue = tokenValue.substring(1);
1844
+
}
1845
+
1846
+
value = new PropertyName(tokenValue, hack, (line||token.startLine), (col||token.startCol));
1847
+
this._readWhitespace();
1848
+
}
1849
+
1850
+
return value;
1851
+
},
1852
+
1853
+
//Augmented with CSS3 Selectors
1854
+
_ruleset: function() {
1855
+
/*
1856
+
* ruleset
1857
+
* : selectors_group
1858
+
* '{' S* declaration? [ ';' S* declaration? ]* '}' S*
1859
+
* ;
1860
+
*/
1861
+
1862
+
var tokenStream = this._tokenStream,
1863
+
tt,
1864
+
selectors;
1865
+
1866
+
1867
+
/*
1868
+
* Error Recovery: If even a single selector fails to parse,
1869
+
* then the entire ruleset should be thrown away.
1870
+
*/
1871
+
try {
1872
+
selectors = this._selectors_group();
1873
+
} catch (ex) {
1874
+
if (ex instanceof SyntaxError && !this.options.strict) {
1875
+
1876
+
//fire error event
1877
+
this.fire({
1878
+
type: "error",
1879
+
error: ex,
1880
+
message: ex.message,
1881
+
line: ex.line,
1882
+
col: ex.col
1883
+
});
1884
+
1885
+
//skip over everything until closing brace
1886
+
tt = tokenStream.advance([Tokens.RBRACE]);
1887
+
if (tt === Tokens.RBRACE) {
1888
+
//if there's a right brace, the rule is finished so don't do anything
1889
+
} else {
1890
+
//otherwise, rethrow the error because it wasn't handled properly
1891
+
throw ex;
1892
+
}
1893
+
1894
+
} else {
1895
+
//not a syntax error, rethrow it
1896
+
throw ex;
1897
+
}
1898
+
1899
+
//trigger parser to continue
1900
+
return true;
1901
+
}
1902
+
1903
+
//if it got here, all selectors parsed
1904
+
if (selectors) {
1905
+
1906
+
this.fire({
1907
+
type: "startrule",
1908
+
selectors: selectors,
1909
+
line: selectors[0].line,
1910
+
col: selectors[0].col
1911
+
});
1912
+
1913
+
this._readDeclarations(true);
1914
+
1915
+
this.fire({
1916
+
type: "endrule",
1917
+
selectors: selectors,
1918
+
line: selectors[0].line,
1919
+
col: selectors[0].col
1920
+
});
1921
+
1922
+
}
1923
+
1924
+
return selectors;
1925
+
1926
+
},
1927
+
1928
+
//CSS3 Selectors
1929
+
_selectors_group: function() {
1930
+
1931
+
/*
1932
+
* selectors_group
1933
+
* : selector [ COMMA S* selector ]*
1934
+
* ;
1935
+
*/
1936
+
var tokenStream = this._tokenStream,
1937
+
selectors = [],
1938
+
selector;
1939
+
1940
+
selector = this._selector();
1941
+
if (selector !== null) {
1942
+
1943
+
selectors.push(selector);
1944
+
while (tokenStream.match(Tokens.COMMA)) {
1945
+
this._readWhitespace();
1946
+
selector = this._selector();
1947
+
if (selector !== null) {
1948
+
selectors.push(selector);
1949
+
} else {
1950
+
this._unexpectedToken(tokenStream.LT(1));
1951
+
}
1952
+
}
1953
+
}
1954
+
1955
+
return selectors.length ? selectors : null;
1956
+
},
1957
+
1958
+
//CSS3 Selectors
1959
+
_selector: function() {
1960
+
/*
1961
+
* selector
1962
+
* : simple_selector_sequence [ combinator simple_selector_sequence ]*
1963
+
* ;
1964
+
*/
1965
+
1966
+
var tokenStream = this._tokenStream,
1967
+
selector = [],
1968
+
nextSelector = null,
1969
+
combinator = null,
1970
+
ws = null;
1971
+
1972
+
//if there's no simple selector, then there's no selector
1973
+
nextSelector = this._simple_selector_sequence();
1974
+
if (nextSelector === null) {
1975
+
return null;
1976
+
}
1977
+
1978
+
selector.push(nextSelector);
1979
+
1980
+
do {
1981
+
1982
+
//look for a combinator
1983
+
combinator = this._combinator();
1984
+
1985
+
if (combinator !== null) {
1986
+
selector.push(combinator);
1987
+
nextSelector = this._simple_selector_sequence();
1988
+
1989
+
//there must be a next selector
1990
+
if (nextSelector === null) {
1991
+
this._unexpectedToken(tokenStream.LT(1));
1992
+
} else {
1993
+
1994
+
//nextSelector is an instance of SelectorPart
1995
+
selector.push(nextSelector);
1996
+
}
1997
+
} else {
1998
+
1999
+
//if there's not whitespace, we're done
2000
+
if (this._readWhitespace()) {
2001
+
2002
+
//add whitespace separator
2003
+
ws = new Combinator(tokenStream.token().value, tokenStream.token().startLine, tokenStream.token().startCol);
2004
+
2005
+
//combinator is not required
2006
+
combinator = this._combinator();
2007
+
2008
+
//selector is required if there's a combinator
2009
+
nextSelector = this._simple_selector_sequence();
2010
+
if (nextSelector === null) {
2011
+
if (combinator !== null) {
2012
+
this._unexpectedToken(tokenStream.LT(1));
2013
+
}
2014
+
} else {
2015
+
2016
+
if (combinator !== null) {
2017
+
selector.push(combinator);
2018
+
} else {
2019
+
selector.push(ws);
2020
+
}
2021
+
2022
+
selector.push(nextSelector);
2023
+
}
2024
+
} else {
2025
+
break;
2026
+
}
2027
+
2028
+
}
2029
+
} while (true);
2030
+
2031
+
return new Selector(selector, selector[0].line, selector[0].col);
2032
+
},
2033
+
2034
+
//CSS3 Selectors
2035
+
_simple_selector_sequence: function() {
2036
+
/*
2037
+
* simple_selector_sequence
2038
+
* : [ type_selector | universal ]
2039
+
* [ HASH | class | attrib | pseudo | negation ]*
2040
+
* | [ HASH | class | attrib | pseudo | negation ]+
2041
+
* ;
2042
+
*/
2043
+
2044
+
var tokenStream = this._tokenStream,
2045
+
2046
+
//parts of a simple selector
2047
+
elementName = null,
2048
+
modifiers = [],
2049
+
2050
+
//complete selector text
2051
+
selectorText= "",
2052
+
2053
+
//the different parts after the element name to search for
2054
+
components = [
2055
+
//HASH
2056
+
function() {
2057
+
return tokenStream.match(Tokens.HASH) ?
2058
+
new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
2059
+
null;
2060
+
},
2061
+
this._class,
2062
+
this._attrib,
2063
+
this._pseudo,
2064
+
this._negation
2065
+
],
2066
+
i = 0,
2067
+
len = components.length,
2068
+
component = null,
2069
+
line,
2070
+
col;
2071
+
2072
+
2073
+
//get starting line and column for the selector
2074
+
line = tokenStream.LT(1).startLine;
2075
+
col = tokenStream.LT(1).startCol;
2076
+
2077
+
elementName = this._type_selector();
2078
+
if (!elementName) {
2079
+
elementName = this._universal();
2080
+
}
2081
+
2082
+
if (elementName !== null) {
2083
+
selectorText += elementName;
2084
+
}
2085
+
2086
+
while (true) {
2087
+
2088
+
//whitespace means we're done
2089
+
if (tokenStream.peek() === Tokens.S) {
2090
+
break;
2091
+
}
2092
+
2093
+
//check for each component
2094
+
while (i < len && component === null) {
2095
+
component = components[i++].call(this);
2096
+
}
2097
+
2098
+
if (component === null) {
2099
+
2100
+
//we don't have a selector
2101
+
if (selectorText === "") {
2102
+
return null;
2103
+
} else {
2104
+
break;
2105
+
}
2106
+
} else {
2107
+
i = 0;
2108
+
modifiers.push(component);
2109
+
selectorText += component.toString();
2110
+
component = null;
2111
+
}
2112
+
}
2113
+
2114
+
2115
+
return selectorText !== "" ?
2116
+
new SelectorPart(elementName, modifiers, selectorText, line, col) :
2117
+
null;
2118
+
},
2119
+
2120
+
//CSS3 Selectors
2121
+
_type_selector: function() {
2122
+
/*
2123
+
* type_selector
2124
+
* : [ namespace_prefix ]? element_name
2125
+
* ;
2126
+
*/
2127
+
2128
+
var tokenStream = this._tokenStream,
2129
+
ns = this._namespace_prefix(),
2130
+
elementName = this._element_name();
2131
+
2132
+
if (!elementName) {
2133
+
/*
2134
+
* Need to back out the namespace that was read due to both
2135
+
* type_selector and universal reading namespace_prefix
2136
+
* first. Kind of hacky, but only way I can figure out
2137
+
* right now how to not change the grammar.
2138
+
*/
2139
+
if (ns) {
2140
+
tokenStream.unget();
2141
+
if (ns.length > 1) {
2142
+
tokenStream.unget();
2143
+
}
2144
+
}
2145
+
2146
+
return null;
2147
+
} else {
2148
+
if (ns) {
2149
+
elementName.text = ns + elementName.text;
2150
+
elementName.col -= ns.length;
2151
+
}
2152
+
return elementName;
2153
+
}
2154
+
},
2155
+
2156
+
//CSS3 Selectors
2157
+
_class: function() {
2158
+
/*
2159
+
* class
2160
+
* : '.' IDENT
2161
+
* ;
2162
+
*/
2163
+
2164
+
var tokenStream = this._tokenStream,
2165
+
token;
2166
+
2167
+
if (tokenStream.match(Tokens.DOT)) {
2168
+
tokenStream.mustMatch(Tokens.IDENT);
2169
+
token = tokenStream.token();
2170
+
return new SelectorSubPart("." + token.value, "class", token.startLine, token.startCol - 1);
2171
+
} else {
2172
+
return null;
2173
+
}
2174
+
2175
+
},
2176
+
2177
+
//CSS3 Selectors
2178
+
_element_name: function() {
2179
+
/*
2180
+
* element_name
2181
+
* : IDENT
2182
+
* ;
2183
+
*/
2184
+
2185
+
var tokenStream = this._tokenStream,
2186
+
token;
2187
+
2188
+
if (tokenStream.match(Tokens.IDENT)) {
2189
+
token = tokenStream.token();
2190
+
return new SelectorSubPart(token.value, "elementName", token.startLine, token.startCol);
2191
+
2192
+
} else {
2193
+
return null;
2194
+
}
2195
+
},
2196
+
2197
+
//CSS3 Selectors
2198
+
_namespace_prefix: function() {
2199
+
/*
2200
+
* namespace_prefix
2201
+
* : [ IDENT | '*' ]? '|'
2202
+
* ;
2203
+
*/
2204
+
var tokenStream = this._tokenStream,
2205
+
value = "";
2206
+
2207
+
//verify that this is a namespace prefix
2208
+
if (tokenStream.LA(1) === Tokens.PIPE || tokenStream.LA(2) === Tokens.PIPE) {
2209
+
2210
+
if (tokenStream.match([Tokens.IDENT, Tokens.STAR])) {
2211
+
value += tokenStream.token().value;
2212
+
}
2213
+
2214
+
tokenStream.mustMatch(Tokens.PIPE);
2215
+
value += "|";
2216
+
2217
+
}
2218
+
2219
+
return value.length ? value : null;
2220
+
},
2221
+
2222
+
//CSS3 Selectors
2223
+
_universal: function() {
2224
+
/*
2225
+
* universal
2226
+
* : [ namespace_prefix ]? '*'
2227
+
* ;
2228
+
*/
2229
+
var tokenStream = this._tokenStream,
2230
+
value = "",
2231
+
ns;
2232
+
2233
+
ns = this._namespace_prefix();
2234
+
if (ns) {
2235
+
value += ns;
2236
+
}
2237
+
2238
+
if (tokenStream.match(Tokens.STAR)) {
2239
+
value += "*";
2240
+
}
2241
+
2242
+
return value.length ? value : null;
2243
+
2244
+
},
2245
+
2246
+
//CSS3 Selectors
2247
+
_attrib: function() {
2248
+
/*
2249
+
* attrib
2250
+
* : '[' S* [ namespace_prefix ]? IDENT S*
2251
+
* [ [ PREFIXMATCH |
2252
+
* SUFFIXMATCH |
2253
+
* SUBSTRINGMATCH |
2254
+
* '=' |
2255
+
* INCLUDES |
2256
+
* DASHMATCH ] S* [ IDENT | STRING ] S*
2257
+
* ]? ']'
2258
+
* ;
2259
+
*/
2260
+
2261
+
var tokenStream = this._tokenStream,
2262
+
value = null,
2263
+
ns,
2264
+
token;
2265
+
2266
+
if (tokenStream.match(Tokens.LBRACKET)) {
2267
+
token = tokenStream.token();
2268
+
value = token.value;
2269
+
value += this._readWhitespace();
2270
+
2271
+
ns = this._namespace_prefix();
2272
+
2273
+
if (ns) {
2274
+
value += ns;
2275
+
}
2276
+
2277
+
tokenStream.mustMatch(Tokens.IDENT);
2278
+
value += tokenStream.token().value;
2279
+
value += this._readWhitespace();
2280
+
2281
+
if (tokenStream.match([Tokens.PREFIXMATCH, Tokens.SUFFIXMATCH, Tokens.SUBSTRINGMATCH,
2282
+
Tokens.EQUALS, Tokens.INCLUDES, Tokens.DASHMATCH])) {
2283
+
2284
+
value += tokenStream.token().value;
2285
+
value += this._readWhitespace();
2286
+
2287
+
tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
2288
+
value += tokenStream.token().value;
2289
+
value += this._readWhitespace();
2290
+
}
2291
+
2292
+
tokenStream.mustMatch(Tokens.RBRACKET);
2293
+
2294
+
return new SelectorSubPart(value + "]", "attribute", token.startLine, token.startCol);
2295
+
} else {
2296
+
return null;
2297
+
}
2298
+
},
2299
+
2300
+
//CSS3 Selectors
2301
+
_pseudo: function() {
2302
+
2303
+
/*
2304
+
* pseudo
2305
+
* : ':' ':'? [ IDENT | functional_pseudo ]
2306
+
* ;
2307
+
*/
2308
+
2309
+
var tokenStream = this._tokenStream,
2310
+
pseudo = null,
2311
+
colons = ":",
2312
+
line,
2313
+
col;
2314
+
2315
+
if (tokenStream.match(Tokens.COLON)) {
2316
+
2317
+
if (tokenStream.match(Tokens.COLON)) {
2318
+
colons += ":";
2319
+
}
2320
+
2321
+
if (tokenStream.match(Tokens.IDENT)) {
2322
+
pseudo = tokenStream.token().value;
2323
+
line = tokenStream.token().startLine;
2324
+
col = tokenStream.token().startCol - colons.length;
2325
+
} else if (tokenStream.peek() === Tokens.FUNCTION) {
2326
+
line = tokenStream.LT(1).startLine;
2327
+
col = tokenStream.LT(1).startCol - colons.length;
2328
+
pseudo = this._functional_pseudo();
2329
+
}
2330
+
2331
+
if (pseudo) {
2332
+
pseudo = new SelectorSubPart(colons + pseudo, "pseudo", line, col);
2333
+
} else {
2334
+
var startLine = tokenStream.LT(1).startLine,
2335
+
startCol = tokenStream.LT(0).startCol;
2336
+
throw new SyntaxError("Expected a `FUNCTION` or `IDENT` after colon at line " + startLine + ", col " + startCol + ".", startLine, startCol);
2337
+
}
2338
+
}
2339
+
2340
+
return pseudo;
2341
+
},
2342
+
2343
+
//CSS3 Selectors
2344
+
_functional_pseudo: function() {
2345
+
/*
2346
+
* functional_pseudo
2347
+
* : FUNCTION S* expression ')'
2348
+
* ;
2349
+
*/
2350
+
2351
+
var tokenStream = this._tokenStream,
2352
+
value = null;
2353
+
2354
+
if (tokenStream.match(Tokens.FUNCTION)) {
2355
+
value = tokenStream.token().value;
2356
+
value += this._readWhitespace();
2357
+
value += this._expression();
2358
+
tokenStream.mustMatch(Tokens.RPAREN);
2359
+
value += ")";
2360
+
}
2361
+
2362
+
return value;
2363
+
},
2364
+
2365
+
//CSS3 Selectors
2366
+
_expression: function() {
2367
+
/*
2368
+
* expression
2369
+
* : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+
2370
+
* ;
2371
+
*/
2372
+
2373
+
var tokenStream = this._tokenStream,
2374
+
value = "";
2375
+
2376
+
while (tokenStream.match([Tokens.PLUS, Tokens.MINUS, Tokens.DIMENSION,
2377
+
Tokens.NUMBER, Tokens.STRING, Tokens.IDENT, Tokens.LENGTH,
2378
+
Tokens.FREQ, Tokens.ANGLE, Tokens.TIME,
2379
+
Tokens.RESOLUTION, Tokens.SLASH])) {
2380
+
2381
+
value += tokenStream.token().value;
2382
+
value += this._readWhitespace();
2383
+
}
2384
+
2385
+
return value.length ? value : null;
2386
+
2387
+
},
2388
+
2389
+
//CSS3 Selectors
2390
+
_negation: function() {
2391
+
/*
2392
+
* negation
2393
+
* : NOT S* negation_arg S* ')'
2394
+
* ;
2395
+
*/
2396
+
2397
+
var tokenStream = this._tokenStream,
2398
+
line,
2399
+
col,
2400
+
value = "",
2401
+
arg,
2402
+
subpart = null;
2403
+
2404
+
if (tokenStream.match(Tokens.NOT)) {
2405
+
value = tokenStream.token().value;
2406
+
line = tokenStream.token().startLine;
2407
+
col = tokenStream.token().startCol;
2408
+
value += this._readWhitespace();
2409
+
arg = this._negation_arg();
2410
+
value += arg;
2411
+
value += this._readWhitespace();
2412
+
tokenStream.match(Tokens.RPAREN);
2413
+
value += tokenStream.token().value;
2414
+
2415
+
subpart = new SelectorSubPart(value, "not", line, col);
2416
+
subpart.args.push(arg);
2417
+
}
2418
+
2419
+
return subpart;
2420
+
},
2421
+
2422
+
//CSS3 Selectors
2423
+
_negation_arg: function() {
2424
+
/*
2425
+
* negation_arg
2426
+
* : type_selector | universal | HASH | class | attrib | pseudo
2427
+
* ;
2428
+
*/
2429
+
2430
+
var tokenStream = this._tokenStream,
2431
+
args = [
2432
+
this._type_selector,
2433
+
this._universal,
2434
+
function() {
2435
+
return tokenStream.match(Tokens.HASH) ?
2436
+
new SelectorSubPart(tokenStream.token().value, "id", tokenStream.token().startLine, tokenStream.token().startCol) :
2437
+
null;
2438
+
},
2439
+
this._class,
2440
+
this._attrib,
2441
+
this._pseudo
2442
+
],
2443
+
arg = null,
2444
+
i = 0,
2445
+
len = args.length,
2446
+
line,
2447
+
col,
2448
+
part;
2449
+
2450
+
line = tokenStream.LT(1).startLine;
2451
+
col = tokenStream.LT(1).startCol;
2452
+
2453
+
while (i < len && arg === null) {
2454
+
2455
+
arg = args[i].call(this);
2456
+
i++;
2457
+
}
2458
+
2459
+
//must be a negation arg
2460
+
if (arg === null) {
2461
+
this._unexpectedToken(tokenStream.LT(1));
2462
+
}
2463
+
2464
+
//it's an element name
2465
+
if (arg.type === "elementName") {
2466
+
part = new SelectorPart(arg, [], arg.toString(), line, col);
2467
+
} else {
2468
+
part = new SelectorPart(null, [arg], arg.toString(), line, col);
2469
+
}
2470
+
2471
+
return part;
2472
+
},
2473
+
2474
+
_declaration: function() {
2475
+
2476
+
/*
2477
+
* declaration
2478
+
* : property ':' S* expr prio?
2479
+
* | /( empty )/
2480
+
* ;
2481
+
*/
2482
+
2483
+
var tokenStream = this._tokenStream,
2484
+
property = null,
2485
+
expr = null,
2486
+
prio = null,
2487
+
invalid = null,
2488
+
propertyName= "";
2489
+
2490
+
property = this._property();
2491
+
if (property !== null) {
2492
+
2493
+
tokenStream.mustMatch(Tokens.COLON);
2494
+
this._readWhitespace();
2495
+
2496
+
expr = this._expr();
2497
+
2498
+
//if there's no parts for the value, it's an error
2499
+
if (!expr || expr.length === 0) {
2500
+
this._unexpectedToken(tokenStream.LT(1));
2501
+
}
2502
+
2503
+
prio = this._prio();
2504
+
2505
+
/*
2506
+
* If hacks should be allowed, then only check the root
2507
+
* property. If hacks should not be allowed, treat
2508
+
* _property or *property as invalid properties.
2509
+
*/
2510
+
propertyName = property.toString();
2511
+
if (this.options.starHack && property.hack === "*" ||
2512
+
this.options.underscoreHack && property.hack === "_") {
2513
+
2514
+
propertyName = property.text;
2515
+
}
2516
+
2517
+
try {
2518
+
this._validateProperty(propertyName, expr);
2519
+
} catch (ex) {
2520
+
invalid = ex;
2521
+
}
2522
+
2523
+
this.fire({
2524
+
type: "property",
2525
+
property: property,
2526
+
value: expr,
2527
+
important: prio,
2528
+
line: property.line,
2529
+
col: property.col,
2530
+
invalid: invalid
2531
+
});
2532
+
2533
+
return true;
2534
+
} else {
2535
+
return false;
2536
+
}
2537
+
},
2538
+
2539
+
_prio: function() {
2540
+
/*
2541
+
* prio
2542
+
* : IMPORTANT_SYM S*
2543
+
* ;
2544
+
*/
2545
+
2546
+
var tokenStream = this._tokenStream,
2547
+
result = tokenStream.match(Tokens.IMPORTANT_SYM);
2548
+
2549
+
this._readWhitespace();
2550
+
return result;
2551
+
},
2552
+
2553
+
_expr: function(inFunction) {
2554
+
/*
2555
+
* expr
2556
+
* : term [ operator term ]*
2557
+
* ;
2558
+
*/
2559
+
2560
+
var values = [],
2561
+
//valueParts = [],
2562
+
value = null,
2563
+
operator = null;
2564
+
2565
+
value = this._term(inFunction);
2566
+
if (value !== null) {
2567
+
2568
+
values.push(value);
2569
+
2570
+
do {
2571
+
operator = this._operator(inFunction);
2572
+
2573
+
//if there's an operator, keep building up the value parts
2574
+
if (operator) {
2575
+
values.push(operator);
2576
+
} /*else {
2577
+
//if there's not an operator, you have a full value
2578
+
values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
2579
+
valueParts = [];
2580
+
}*/
2581
+
2582
+
value = this._term(inFunction);
2583
+
2584
+
if (value === null) {
2585
+
break;
2586
+
} else {
2587
+
values.push(value);
2588
+
}
2589
+
} while (true);
2590
+
}
2591
+
2592
+
//cleanup
2593
+
/*if (valueParts.length) {
2594
+
values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
2595
+
}*/
2596
+
2597
+
return values.length > 0 ? new PropertyValue(values, values[0].line, values[0].col) : null;
2598
+
},
2599
+
2600
+
_term: function(inFunction) {
2601
+
2602
+
/*
2603
+
* term
2604
+
* : unary_operator?
2605
+
* [ NUMBER S* | PERCENTAGE S* | LENGTH S* | ANGLE S* |
2606
+
* TIME S* | FREQ S* | function | ie_function ]
2607
+
* | STRING S* | IDENT S* | URI S* | UNICODERANGE S* | hexcolor
2608
+
* ;
2609
+
*/
2610
+
2611
+
var tokenStream = this._tokenStream,
2612
+
unary = null,
2613
+
value = null,
2614
+
endChar = null,
2615
+
part = null,
2616
+
token,
2617
+
line,
2618
+
col;
2619
+
2620
+
//returns the operator or null
2621
+
unary = this._unary_operator();
2622
+
if (unary !== null) {
2623
+
line = tokenStream.token().startLine;
2624
+
col = tokenStream.token().startCol;
2625
+
}
2626
+
2627
+
//exception for IE filters
2628
+
if (tokenStream.peek() === Tokens.IE_FUNCTION && this.options.ieFilters) {
2629
+
2630
+
value = this._ie_function();
2631
+
if (unary === null) {
2632
+
line = tokenStream.token().startLine;
2633
+
col = tokenStream.token().startCol;
2634
+
}
2635
+
2636
+
//see if it's a simple block
2637
+
} else if (inFunction && tokenStream.match([Tokens.LPAREN, Tokens.LBRACE, Tokens.LBRACKET])) {
2638
+
2639
+
token = tokenStream.token();
2640
+
endChar = token.endChar;
2641
+
value = token.value + this._expr(inFunction).text;
2642
+
if (unary === null) {
2643
+
line = tokenStream.token().startLine;
2644
+
col = tokenStream.token().startCol;
2645
+
}
2646
+
tokenStream.mustMatch(Tokens.type(endChar));
2647
+
value += endChar;
2648
+
this._readWhitespace();
2649
+
2650
+
//see if there's a simple match
2651
+
} else if (tokenStream.match([Tokens.NUMBER, Tokens.PERCENTAGE, Tokens.LENGTH,
2652
+
Tokens.ANGLE, Tokens.TIME,
2653
+
Tokens.FREQ, Tokens.STRING, Tokens.IDENT, Tokens.URI, Tokens.UNICODE_RANGE])) {
2654
+
2655
+
value = tokenStream.token().value;
2656
+
if (unary === null) {
2657
+
line = tokenStream.token().startLine;
2658
+
col = tokenStream.token().startCol;
2659
+
// Correct potentially-inaccurate IDENT parsing in
2660
+
// PropertyValuePart constructor.
2661
+
part = PropertyValuePart.fromToken(tokenStream.token());
2662
+
}
2663
+
this._readWhitespace();
2664
+
} else {
2665
+
2666
+
//see if it's a color
2667
+
token = this._hexcolor();
2668
+
if (token === null) {
2669
+
2670
+
//if there's no unary, get the start of the next token for line/col info
2671
+
if (unary === null) {
2672
+
line = tokenStream.LT(1).startLine;
2673
+
col = tokenStream.LT(1).startCol;
2674
+
}
2675
+
2676
+
//has to be a function
2677
+
if (value === null) {
2678
+
2679
+
/*
2680
+
* This checks for alpha(opacity=0) style of IE
2681
+
* functions. IE_FUNCTION only presents progid: style.
2682
+
*/
2683
+
if (tokenStream.LA(3) === Tokens.EQUALS && this.options.ieFilters) {
2684
+
value = this._ie_function();
2685
+
} else {
2686
+
value = this._function();
2687
+
}
2688
+
}
2689
+
2690
+
/*if (value === null) {
2691
+
return null;
2692
+
//throw new Error("Expected identifier at line " + tokenStream.token().startLine + ", character " + tokenStream.token().startCol + ".");
2693
+
}*/
2694
+
2695
+
} else {
2696
+
value = token.value;
2697
+
if (unary === null) {
2698
+
line = token.startLine;
2699
+
col = token.startCol;
2700
+
}
2701
+
}
2702
+
2703
+
}
2704
+
2705
+
return part !== null ? part : value !== null ?
2706
+
new PropertyValuePart(unary !== null ? unary + value : value, line, col) :
2707
+
null;
2708
+
2709
+
},
2710
+
2711
+
_function: function() {
2712
+
2713
+
/*
2714
+
* function
2715
+
* : FUNCTION S* expr ')' S*
2716
+
* ;
2717
+
*/
2718
+
2719
+
var tokenStream = this._tokenStream,
2720
+
functionText = null,
2721
+
expr = null,
2722
+
lt;
2723
+
2724
+
if (tokenStream.match(Tokens.FUNCTION)) {
2725
+
functionText = tokenStream.token().value;
2726
+
this._readWhitespace();
2727
+
expr = this._expr(true);
2728
+
functionText += expr;
2729
+
2730
+
//START: Horrible hack in case it's an IE filter
2731
+
if (this.options.ieFilters && tokenStream.peek() === Tokens.EQUALS) {
2732
+
do {
2733
+
2734
+
if (this._readWhitespace()) {
2735
+
functionText += tokenStream.token().value;
2736
+
}
2737
+
2738
+
//might be second time in the loop
2739
+
if (tokenStream.LA(0) === Tokens.COMMA) {
2740
+
functionText += tokenStream.token().value;
2741
+
}
2742
+
2743
+
tokenStream.match(Tokens.IDENT);
2744
+
functionText += tokenStream.token().value;
2745
+
2746
+
tokenStream.match(Tokens.EQUALS);
2747
+
functionText += tokenStream.token().value;
2748
+
2749
+
//functionText += this._term();
2750
+
lt = tokenStream.peek();
2751
+
while (lt !== Tokens.COMMA && lt !== Tokens.S && lt !== Tokens.RPAREN) {
2752
+
tokenStream.get();
2753
+
functionText += tokenStream.token().value;
2754
+
lt = tokenStream.peek();
2755
+
}
2756
+
} while (tokenStream.match([Tokens.COMMA, Tokens.S]));
2757
+
}
2758
+
2759
+
//END: Horrible Hack
2760
+
2761
+
tokenStream.match(Tokens.RPAREN);
2762
+
functionText += ")";
2763
+
this._readWhitespace();
2764
+
}
2765
+
2766
+
return functionText;
2767
+
},
2768
+
2769
+
_ie_function: function() {
2770
+
2771
+
/* (My own extension)
2772
+
* ie_function
2773
+
* : IE_FUNCTION S* IDENT '=' term [S* ','? IDENT '=' term]+ ')' S*
2774
+
* ;
2775
+
*/
2776
+
2777
+
var tokenStream = this._tokenStream,
2778
+
functionText = null,
2779
+
lt;
2780
+
2781
+
//IE function can begin like a regular function, too
2782
+
if (tokenStream.match([Tokens.IE_FUNCTION, Tokens.FUNCTION])) {
2783
+
functionText = tokenStream.token().value;
2784
+
2785
+
do {
2786
+
2787
+
if (this._readWhitespace()) {
2788
+
functionText += tokenStream.token().value;
2789
+
}
2790
+
2791
+
//might be second time in the loop
2792
+
if (tokenStream.LA(0) === Tokens.COMMA) {
2793
+
functionText += tokenStream.token().value;
2794
+
}
2795
+
2796
+
tokenStream.match(Tokens.IDENT);
2797
+
functionText += tokenStream.token().value;
2798
+
2799
+
tokenStream.match(Tokens.EQUALS);
2800
+
functionText += tokenStream.token().value;
2801
+
2802
+
//functionText += this._term();
2803
+
lt = tokenStream.peek();
2804
+
while (lt !== Tokens.COMMA && lt !== Tokens.S && lt !== Tokens.RPAREN) {
2805
+
tokenStream.get();
2806
+
functionText += tokenStream.token().value;
2807
+
lt = tokenStream.peek();
2808
+
}
2809
+
} while (tokenStream.match([Tokens.COMMA, Tokens.S]));
2810
+
2811
+
tokenStream.match(Tokens.RPAREN);
2812
+
functionText += ")";
2813
+
this._readWhitespace();
2814
+
}
2815
+
2816
+
return functionText;
2817
+
},
2818
+
2819
+
_hexcolor: function() {
2820
+
/*
2821
+
* There is a constraint on the color that it must
2822
+
* have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
2823
+
* after the "#"; e.g., "#000" is OK, but "#abcd" is not.
2824
+
*
2825
+
* hexcolor
2826
+
* : HASH S*
2827
+
* ;
2828
+
*/
2829
+
2830
+
var tokenStream = this._tokenStream,
2831
+
token = null,
2832
+
color;
2833
+
2834
+
if (tokenStream.match(Tokens.HASH)) {
2835
+
2836
+
//need to do some validation here
2837
+
2838
+
token = tokenStream.token();
2839
+
color = token.value;
2840
+
if (!/#[a-f0-9]{3,6}/i.test(color)) {
2841
+
throw new SyntaxError("Expected a hex color but found '" + color + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
2842
+
}
2843
+
this._readWhitespace();
2844
+
}
2845
+
2846
+
return token;
2847
+
},
2848
+
2849
+
//-----------------------------------------------------------------
2850
+
// Animations methods
2851
+
//-----------------------------------------------------------------
2852
+
2853
+
_keyframes: function() {
2854
+
2855
+
/*
2856
+
* keyframes:
2857
+
* : KEYFRAMES_SYM S* keyframe_name S* '{' S* keyframe_rule* '}' {
2858
+
* ;
2859
+
*/
2860
+
var tokenStream = this._tokenStream,
2861
+
token,
2862
+
tt,
2863
+
name,
2864
+
prefix = "";
2865
+
2866
+
tokenStream.mustMatch(Tokens.KEYFRAMES_SYM);
2867
+
token = tokenStream.token();
2868
+
if (/^@\-([^\-]+)\-/.test(token.value)) {
2869
+
prefix = RegExp.$1;
2870
+
}
2871
+
2872
+
this._readWhitespace();
2873
+
name = this._keyframe_name();
2874
+
2875
+
this._readWhitespace();
2876
+
tokenStream.mustMatch(Tokens.LBRACE);
2877
+
2878
+
this.fire({
2879
+
type: "startkeyframes",
2880
+
name: name,
2881
+
prefix: prefix,
2882
+
line: token.startLine,
2883
+
col: token.startCol
2884
+
});
2885
+
2886
+
this._readWhitespace();
2887
+
tt = tokenStream.peek();
2888
+
2889
+
//check for key
2890
+
while (tt === Tokens.IDENT || tt === Tokens.PERCENTAGE) {
2891
+
this._keyframe_rule();
2892
+
this._readWhitespace();
2893
+
tt = tokenStream.peek();
2894
+
}
2895
+
2896
+
this.fire({
2897
+
type: "endkeyframes",
2898
+
name: name,
2899
+
prefix: prefix,
2900
+
line: token.startLine,
2901
+
col: token.startCol
2902
+
});
2903
+
2904
+
this._readWhitespace();
2905
+
tokenStream.mustMatch(Tokens.RBRACE);
2906
+
this._readWhitespace();
2907
+
2908
+
},
2909
+
2910
+
_keyframe_name: function() {
2911
+
2912
+
/*
2913
+
* keyframe_name:
2914
+
* : IDENT
2915
+
* | STRING
2916
+
* ;
2917
+
*/
2918
+
var tokenStream = this._tokenStream;
2919
+
2920
+
tokenStream.mustMatch([Tokens.IDENT, Tokens.STRING]);
2921
+
return SyntaxUnit.fromToken(tokenStream.token());
2922
+
},
2923
+
2924
+
_keyframe_rule: function() {
2925
+
2926
+
/*
2927
+
* keyframe_rule:
2928
+
* : key_list S*
2929
+
* '{' S* declaration [ ';' S* declaration ]* '}' S*
2930
+
* ;
2931
+
*/
2932
+
var keyList = this._key_list();
2933
+
2934
+
this.fire({
2935
+
type: "startkeyframerule",
2936
+
keys: keyList,
2937
+
line: keyList[0].line,
2938
+
col: keyList[0].col
2939
+
});
2940
+
2941
+
this._readDeclarations(true);
2942
+
2943
+
this.fire({
2944
+
type: "endkeyframerule",
2945
+
keys: keyList,
2946
+
line: keyList[0].line,
2947
+
col: keyList[0].col
2948
+
});
2949
+
2950
+
},
2951
+
2952
+
_key_list: function() {
2953
+
2954
+
/*
2955
+
* key_list:
2956
+
* : key [ S* ',' S* key]*
2957
+
* ;
2958
+
*/
2959
+
var tokenStream = this._tokenStream,
2960
+
keyList = [];
2961
+
2962
+
//must be least one key
2963
+
keyList.push(this._key());
2964
+
2965
+
this._readWhitespace();
2966
+
2967
+
while (tokenStream.match(Tokens.COMMA)) {
2968
+
this._readWhitespace();
2969
+
keyList.push(this._key());
2970
+
this._readWhitespace();
2971
+
}
2972
+
2973
+
return keyList;
2974
+
},
2975
+
2976
+
_key: function() {
2977
+
/*
2978
+
* There is a restriction that IDENT can be only "from" or "to".
2979
+
*
2980
+
* key
2981
+
* : PERCENTAGE
2982
+
* | IDENT
2983
+
* ;
2984
+
*/
2985
+
2986
+
var tokenStream = this._tokenStream,
2987
+
token;
2988
+
2989
+
if (tokenStream.match(Tokens.PERCENTAGE)) {
2990
+
return SyntaxUnit.fromToken(tokenStream.token());
2991
+
} else if (tokenStream.match(Tokens.IDENT)) {
2992
+
token = tokenStream.token();
2993
+
2994
+
if (/from|to/i.test(token.value)) {
2995
+
return SyntaxUnit.fromToken(token);
2996
+
}
2997
+
2998
+
tokenStream.unget();
2999
+
}
3000
+
3001
+
//if it gets here, there wasn't a valid token, so time to explode
3002
+
this._unexpectedToken(tokenStream.LT(1));
3003
+
},
3004
+
3005
+
//-----------------------------------------------------------------
3006
+
// Helper methods
3007
+
//-----------------------------------------------------------------
3008
+
3009
+
/**
3010
+
* Not part of CSS grammar, but useful for skipping over
3011
+
* combination of white space and HTML-style comments.
3012
+
* @return {void}
3013
+
* @method _skipCruft
3014
+
* @private
3015
+
*/
3016
+
_skipCruft: function() {
3017
+
while (this._tokenStream.match([Tokens.S, Tokens.CDO, Tokens.CDC])) {
3018
+
//noop
3019
+
}
3020
+
},
3021
+
3022
+
/**
3023
+
* Not part of CSS grammar, but this pattern occurs frequently
3024
+
* in the official CSS grammar. Split out here to eliminate
3025
+
* duplicate code.
3026
+
* @param {Boolean} checkStart Indicates if the rule should check
3027
+
* for the left brace at the beginning.
3028
+
* @param {Boolean} readMargins Indicates if the rule should check
3029
+
* for margin patterns.
3030
+
* @return {void}
3031
+
* @method _readDeclarations
3032
+
* @private
3033
+
*/
3034
+
_readDeclarations: function(checkStart, readMargins) {
3035
+
/*
3036
+
* Reads the pattern
3037
+
* S* '{' S* declaration [ ';' S* declaration ]* '}' S*
3038
+
* or
3039
+
* S* '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
3040
+
* Note that this is how it is described in CSS3 Paged Media, but is actually incorrect.
3041
+
* A semicolon is only necessary following a declaration if there's another declaration
3042
+
* or margin afterwards.
3043
+
*/
3044
+
var tokenStream = this._tokenStream,
3045
+
tt;
3046
+
3047
+
3048
+
this._readWhitespace();
3049
+
3050
+
if (checkStart) {
3051
+
tokenStream.mustMatch(Tokens.LBRACE);
3052
+
}
3053
+
3054
+
this._readWhitespace();
3055
+
3056
+
try {
3057
+
3058
+
while (true) {
3059
+
3060
+
if (tokenStream.match(Tokens.SEMICOLON) || (readMargins && this._margin())) {
3061
+
//noop
3062
+
} else if (this._declaration()) {
3063
+
if (!tokenStream.match(Tokens.SEMICOLON)) {
3064
+
break;
3065
+
}
3066
+
} else {
3067
+
break;
3068
+
}
3069
+
3070
+
//if ((!this._margin() && !this._declaration()) || !tokenStream.match(Tokens.SEMICOLON)){
3071
+
// break;
3072
+
//}
3073
+
this._readWhitespace();
3074
+
}
3075
+
3076
+
tokenStream.mustMatch(Tokens.RBRACE);
3077
+
this._readWhitespace();
3078
+
3079
+
} catch (ex) {
3080
+
if (ex instanceof SyntaxError && !this.options.strict) {
3081
+
3082
+
//fire error event
3083
+
this.fire({
3084
+
type: "error",
3085
+
error: ex,
3086
+
message: ex.message,
3087
+
line: ex.line,
3088
+
col: ex.col
3089
+
});
3090
+
3091
+
//see if there's another declaration
3092
+
tt = tokenStream.advance([Tokens.SEMICOLON, Tokens.RBRACE]);
3093
+
if (tt === Tokens.SEMICOLON) {
3094
+
//if there's a semicolon, then there might be another declaration
3095
+
this._readDeclarations(false, readMargins);
3096
+
} else if (tt !== Tokens.RBRACE) {
3097
+
//if there's a right brace, the rule is finished so don't do anything
3098
+
//otherwise, rethrow the error because it wasn't handled properly
3099
+
throw ex;
3100
+
}
3101
+
3102
+
} else {
3103
+
//not a syntax error, rethrow it
3104
+
throw ex;
3105
+
}
3106
+
}
3107
+
3108
+
},
3109
+
3110
+
/**
3111
+
* In some cases, you can end up with two white space tokens in a
3112
+
* row. Instead of making a change in every function that looks for
3113
+
* white space, this function is used to match as much white space
3114
+
* as necessary.
3115
+
* @method _readWhitespace
3116
+
* @return {String} The white space if found, empty string if not.
3117
+
* @private
3118
+
*/
3119
+
_readWhitespace: function() {
3120
+
3121
+
var tokenStream = this._tokenStream,
3122
+
ws = "";
3123
+
3124
+
while (tokenStream.match(Tokens.S)) {
3125
+
ws += tokenStream.token().value;
3126
+
}
3127
+
3128
+
return ws;
3129
+
},
3130
+
3131
+
3132
+
/**
3133
+
* Throws an error when an unexpected token is found.
3134
+
* @param {Object} token The token that was found.
3135
+
* @method _unexpectedToken
3136
+
* @return {void}
3137
+
* @private
3138
+
*/
3139
+
_unexpectedToken: function(token) {
3140
+
throw new SyntaxError("Unexpected token '" + token.value + "' at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
3141
+
},
3142
+
3143
+
/**
3144
+
* Helper method used for parsing subparts of a style sheet.
3145
+
* @return {void}
3146
+
* @method _verifyEnd
3147
+
* @private
3148
+
*/
3149
+
_verifyEnd: function() {
3150
+
if (this._tokenStream.LA(1) !== Tokens.EOF) {
3151
+
this._unexpectedToken(this._tokenStream.LT(1));
3152
+
}
3153
+
},
3154
+
3155
+
//-----------------------------------------------------------------
3156
+
// Validation methods
3157
+
//-----------------------------------------------------------------
3158
+
_validateProperty: function(property, value) {
3159
+
Validation.validate(property, value);
3160
+
},
3161
+
3162
+
//-----------------------------------------------------------------
3163
+
// Parsing methods
3164
+
//-----------------------------------------------------------------
3165
+
3166
+
parse: function(input) {
3167
+
this._tokenStream = new TokenStream(input, Tokens);
3168
+
this._stylesheet();
3169
+
},
3170
+
3171
+
parseStyleSheet: function(input) {
3172
+
//just passthrough
3173
+
return this.parse(input);
3174
+
},
3175
+
3176
+
parseMediaQuery: function(input) {
3177
+
this._tokenStream = new TokenStream(input, Tokens);
3178
+
var result = this._media_query();
3179
+
3180
+
//if there's anything more, then it's an invalid selector
3181
+
this._verifyEnd();
3182
+
3183
+
//otherwise return result
3184
+
return result;
3185
+
},
3186
+
3187
+
/**
3188
+
* Parses a property value (everything after the semicolon).
3189
+
* @return {parserlib.css.PropertyValue} The property value.
3190
+
* @throws parserlib.util.SyntaxError If an unexpected token is found.
3191
+
* @method parserPropertyValue
3192
+
*/
3193
+
parsePropertyValue: function(input) {
3194
+
3195
+
this._tokenStream = new TokenStream(input, Tokens);
3196
+
this._readWhitespace();
3197
+
3198
+
var result = this._expr();
3199
+
3200
+
//okay to have a trailing white space
3201
+
this._readWhitespace();
3202
+
3203
+
//if there's anything more, then it's an invalid selector
3204
+
this._verifyEnd();
3205
+
3206
+
//otherwise return result
3207
+
return result;
3208
+
},
3209
+
3210
+
/**
3211
+
* Parses a complete CSS rule, including selectors and
3212
+
* properties.
3213
+
* @param {String} input The text to parser.
3214
+
* @return {Boolean} True if the parse completed successfully, false if not.
3215
+
* @method parseRule
3216
+
*/
3217
+
parseRule: function(input) {
3218
+
this._tokenStream = new TokenStream(input, Tokens);
3219
+
3220
+
//skip any leading white space
3221
+
this._readWhitespace();
3222
+
3223
+
var result = this._ruleset();
3224
+
3225
+
//skip any trailing white space
3226
+
this._readWhitespace();
3227
+
3228
+
//if there's anything more, then it's an invalid selector
3229
+
this._verifyEnd();
3230
+
3231
+
//otherwise return result
3232
+
return result;
3233
+
},
3234
+
3235
+
/**
3236
+
* Parses a single CSS selector (no comma)
3237
+
* @param {String} input The text to parse as a CSS selector.
3238
+
* @return {Selector} An object representing the selector.
3239
+
* @throws parserlib.util.SyntaxError If an unexpected token is found.
3240
+
* @method parseSelector
3241
+
*/
3242
+
parseSelector: function(input) {
3243
+
3244
+
this._tokenStream = new TokenStream(input, Tokens);
3245
+
3246
+
//skip any leading white space
3247
+
this._readWhitespace();
3248
+
3249
+
var result = this._selector();
3250
+
3251
+
//skip any trailing white space
3252
+
this._readWhitespace();
3253
+
3254
+
//if there's anything more, then it's an invalid selector
3255
+
this._verifyEnd();
3256
+
3257
+
//otherwise return result
3258
+
return result;
3259
+
},
3260
+
3261
+
/**
3262
+
* Parses an HTML style attribute: a set of CSS declarations
3263
+
* separated by semicolons.
3264
+
* @param {String} input The text to parse as a style attribute
3265
+
* @return {void}
3266
+
* @method parseStyleAttribute
3267
+
*/
3268
+
parseStyleAttribute: function(input) {
3269
+
input += "}"; // for error recovery in _readDeclarations()
3270
+
this._tokenStream = new TokenStream(input, Tokens);
3271
+
this._readDeclarations();
3272
+
}
3273
+
};
3274
+
3275
+
//copy over onto prototype
3276
+
for (prop in additions) {
3277
+
if (Object.prototype.hasOwnProperty.call(additions, prop)) {
3278
+
proto[prop] = additions[prop];
3279
+
}
3280
+
}
3281
+
3282
+
return proto;
3283
+
}();
3284
+
3285
+
3286
+
/*
3287
+
nth
3288
+
: S* [ ['-'|'+']? INTEGER? {N} [ S* ['-'|'+'] S* INTEGER ]? |
3289
+
['-'|'+']? INTEGER | {O}{D}{D} | {E}{V}{E}{N} ] S*
3290
+
;
3291
+
*/
3292
+
3293
+
},{"../util/EventTarget":23,"../util/SyntaxError":25,"../util/SyntaxUnit":26,"./Combinator":2,"./MediaFeature":4,"./MediaQuery":5,"./PropertyName":8,"./PropertyValue":9,"./PropertyValuePart":11,"./Selector":13,"./SelectorPart":14,"./SelectorSubPart":15,"./TokenStream":17,"./Tokens":18,"./Validation":19}],7:[function(require,module,exports){
3294
+
"use strict";
3295
+
3296
+
/* exported Properties */
3297
+
3298
+
var Properties = module.exports = {
3299
+
__proto__: null,
3300
+
3301
+
//A
3302
+
"align-items" : "flex-start | flex-end | center | baseline | stretch",
3303
+
"align-content" : "flex-start | flex-end | center | space-between | space-around | stretch",
3304
+
"align-self" : "auto | flex-start | flex-end | center | baseline | stretch",
3305
+
"all" : "initial | inherit | unset",
3306
+
"-webkit-align-items" : "flex-start | flex-end | center | baseline | stretch",
3307
+
"-webkit-align-content" : "flex-start | flex-end | center | space-between | space-around | stretch",
3308
+
"-webkit-align-self" : "auto | flex-start | flex-end | center | baseline | stretch",
3309
+
"alignment-adjust" : "auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | <percentage> | <length>",
3310
+
"alignment-baseline" : "auto | baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
3311
+
"animation" : 1,
3312
+
"animation-delay" : "<time>#",
3313
+
"animation-direction" : "<single-animation-direction>#",
3314
+
"animation-duration" : "<time>#",
3315
+
"animation-fill-mode" : "[ none | forwards | backwards | both ]#",
3316
+
"animation-iteration-count" : "[ <number> | infinite ]#",
3317
+
"animation-name" : "[ none | <single-animation-name> ]#",
3318
+
"animation-play-state" : "[ running | paused ]#",
3319
+
"animation-timing-function" : 1,
3320
+
3321
+
//vendor prefixed
3322
+
"-moz-animation-delay" : "<time>#",
3323
+
"-moz-animation-direction" : "[ normal | alternate ]#",
3324
+
"-moz-animation-duration" : "<time>#",
3325
+
"-moz-animation-iteration-count" : "[ <number> | infinite ]#",
3326
+
"-moz-animation-name" : "[ none | <single-animation-name> ]#",
3327
+
"-moz-animation-play-state" : "[ running | paused ]#",
3328
+
3329
+
"-ms-animation-delay" : "<time>#",
3330
+
"-ms-animation-direction" : "[ normal | alternate ]#",
3331
+
"-ms-animation-duration" : "<time>#",
3332
+
"-ms-animation-iteration-count" : "[ <number> | infinite ]#",
3333
+
"-ms-animation-name" : "[ none | <single-animation-name> ]#",
3334
+
"-ms-animation-play-state" : "[ running | paused ]#",
3335
+
3336
+
"-webkit-animation-delay" : "<time>#",
3337
+
"-webkit-animation-direction" : "[ normal | alternate ]#",
3338
+
"-webkit-animation-duration" : "<time>#",
3339
+
"-webkit-animation-fill-mode" : "[ none | forwards | backwards | both ]#",
3340
+
"-webkit-animation-iteration-count" : "[ <number> | infinite ]#",
3341
+
"-webkit-animation-name" : "[ none | <single-animation-name> ]#",
3342
+
"-webkit-animation-play-state" : "[ running | paused ]#",
3343
+
3344
+
"-o-animation-delay" : "<time>#",
3345
+
"-o-animation-direction" : "[ normal | alternate ]#",
3346
+
"-o-animation-duration" : "<time>#",
3347
+
"-o-animation-iteration-count" : "[ <number> | infinite ]#",
3348
+
"-o-animation-name" : "[ none | <single-animation-name> ]#",
3349
+
"-o-animation-play-state" : "[ running | paused ]#",
3350
+
3351
+
"appearance" : "none | auto",
3352
+
"-moz-appearance" : "none | button | button-arrow-down | button-arrow-next | button-arrow-previous | button-arrow-up | button-bevel | button-focus | caret | checkbox | checkbox-container | checkbox-label | checkmenuitem | dualbutton | groupbox | listbox | listitem | menuarrow | menubar | menucheckbox | menuimage | menuitem | menuitemtext | menulist | menulist-button | menulist-text | menulist-textfield | menupopup | menuradio | menuseparator | meterbar | meterchunk | progressbar | progressbar-vertical | progresschunk | progresschunk-vertical | radio | radio-container | radio-label | radiomenuitem | range | range-thumb | resizer | resizerpanel | scale-horizontal | scalethumbend | scalethumb-horizontal | scalethumbstart | scalethumbtick | scalethumb-vertical | scale-vertical | scrollbarbutton-down | scrollbarbutton-left | scrollbarbutton-right | scrollbarbutton-up | scrollbarthumb-horizontal | scrollbarthumb-vertical | scrollbartrack-horizontal | scrollbartrack-vertical | searchfield | separator | sheet | spinner | spinner-downbutton | spinner-textfield | spinner-upbutton | splitter | statusbar | statusbarpanel | tab | tabpanel | tabpanels | tab-scroll-arrow-back | tab-scroll-arrow-forward | textfield | textfield-multiline | toolbar | toolbarbutton | toolbarbutton-dropdown | toolbargripper | toolbox | tooltip | treeheader | treeheadercell | treeheadersortarrow | treeitem | treeline | treetwisty | treetwistyopen | treeview | -moz-mac-unified-toolbar | -moz-win-borderless-glass | -moz-win-browsertabbar-toolbox | -moz-win-communicationstext | -moz-win-communications-toolbox | -moz-win-exclude-glass | -moz-win-glass | -moz-win-mediatext | -moz-win-media-toolbox | -moz-window-button-box | -moz-window-button-box-maximized | -moz-window-button-close | -moz-window-button-maximize | -moz-window-button-minimize | -moz-window-button-restore | -moz-window-frame-bottom | -moz-window-frame-left | -moz-window-frame-right | -moz-window-titlebar | -moz-window-titlebar-maximized",
3353
+
"-ms-appearance" : "none | icon | window | desktop | workspace | document | tooltip | dialog | button | push-button | hyperlink | radio | radio-button | checkbox | menu-item | tab | menu | menubar | pull-down-menu | pop-up-menu | list-menu | radio-group | checkbox-group | outline-tree | range | field | combo-box | signature | password | normal",
3354
+
"-webkit-appearance" : "none | button | button-bevel | caps-lock-indicator | caret | checkbox | default-button | listbox | listitem | media-fullscreen-button | media-mute-button | media-play-button | media-seek-back-button | media-seek-forward-button | media-slider | media-sliderthumb | menulist | menulist-button | menulist-text | menulist-textfield | push-button | radio | searchfield | searchfield-cancel-button | searchfield-decoration | searchfield-results-button | searchfield-results-decoration | slider-horizontal | slider-vertical | sliderthumb-horizontal | sliderthumb-vertical | square-button | textarea | textfield | scrollbarbutton-down | scrollbarbutton-left | scrollbarbutton-right | scrollbarbutton-up | scrollbargripper-horizontal | scrollbargripper-vertical | scrollbarthumb-horizontal | scrollbarthumb-vertical | scrollbartrack-horizontal | scrollbartrack-vertical",
3355
+
"-o-appearance" : "none | window | desktop | workspace | document | tooltip | dialog | button | push-button | hyperlink | radio | radio-button | checkbox | menu-item | tab | menu | menubar | pull-down-menu | pop-up-menu | list-menu | radio-group | checkbox-group | outline-tree | range | field | combo-box | signature | password | normal",
3356
+
3357
+
"azimuth" : "<azimuth>",
3358
+
3359
+
//B
3360
+
"backface-visibility" : "visible | hidden",
3361
+
"background" : 1,
3362
+
"background-attachment" : "<attachment>#",
3363
+
"background-clip" : "<box>#",
3364
+
"background-color" : "<color>",
3365
+
"background-image" : "<bg-image>#",
3366
+
"background-origin" : "<box>#",
3367
+
"background-position" : "<bg-position>",
3368
+
"background-repeat" : "<repeat-style>#",
3369
+
"background-size" : "<bg-size>#",
3370
+
"baseline-shift" : "baseline | sub | super | <percentage> | <length>",
3371
+
"behavior" : 1,
3372
+
"binding" : 1,
3373
+
"bleed" : "<length>",
3374
+
"bookmark-label" : "<content> | <attr> | <string>",
3375
+
"bookmark-level" : "none | <integer>",
3376
+
"bookmark-state" : "open | closed",
3377
+
"bookmark-target" : "none | <uri> | <attr>",
3378
+
"border" : "<border-width> || <border-style> || <color>",
3379
+
"border-bottom" : "<border-width> || <border-style> || <color>",
3380
+
"border-bottom-color" : "<color>",
3381
+
"border-bottom-left-radius" : "<x-one-radius>",
3382
+
"border-bottom-right-radius" : "<x-one-radius>",
3383
+
"border-bottom-style" : "<border-style>",
3384
+
"border-bottom-width" : "<border-width>",
3385
+
"border-collapse" : "collapse | separate",
3386
+
"border-color" : "<color>{1,4}",
3387
+
"border-image" : 1,
3388
+
"border-image-outset" : "[ <length> | <number> ]{1,4}",
3389
+
"border-image-repeat" : "[ stretch | repeat | round ]{1,2}",
3390
+
"border-image-slice" : "<border-image-slice>",
3391
+
"border-image-source" : "<image> | none",
3392
+
"border-image-width" : "[ <length> | <percentage> | <number> | auto ]{1,4}",
3393
+
"border-left" : "<border-width> || <border-style> || <color>",
3394
+
"border-left-color" : "<color>",
3395
+
"border-left-style" : "<border-style>",
3396
+
"border-left-width" : "<border-width>",
3397
+
"border-radius" : "<border-radius>",
3398
+
"border-right" : "<border-width> || <border-style> || <color>",
3399
+
"border-right-color" : "<color>",
3400
+
"border-right-style" : "<border-style>",
3401
+
"border-right-width" : "<border-width>",
3402
+
"border-spacing" : "<length>{1,2}",
3403
+
"border-style" : "<border-style>{1,4}",
3404
+
"border-top" : "<border-width> || <border-style> || <color>",
3405
+
"border-top-color" : "<color>",
3406
+
"border-top-left-radius" : "<x-one-radius>",
3407
+
"border-top-right-radius" : "<x-one-radius>",
3408
+
"border-top-style" : "<border-style>",
3409
+
"border-top-width" : "<border-width>",
3410
+
"border-width" : "<border-width>{1,4}",
3411
+
"bottom" : "<margin-width>",
3412
+
"-moz-box-align" : "start | end | center | baseline | stretch",
3413
+
"-moz-box-decoration-break" : "slice | clone",
3414
+
"-moz-box-direction" : "normal | reverse",
3415
+
"-moz-box-flex" : "<number>",
3416
+
"-moz-box-flex-group" : "<integer>",
3417
+
"-moz-box-lines" : "single | multiple",
3418
+
"-moz-box-ordinal-group" : "<integer>",
3419
+
"-moz-box-orient" : "horizontal | vertical | inline-axis | block-axis",
3420
+
"-moz-box-pack" : "start | end | center | justify",
3421
+
"-o-box-decoration-break" : "slice | clone",
3422
+
"-webkit-box-align" : "start | end | center | baseline | stretch",
3423
+
"-webkit-box-decoration-break" : "slice | clone",
3424
+
"-webkit-box-direction" : "normal | reverse",
3425
+
"-webkit-box-flex" : "<number>",
3426
+
"-webkit-box-flex-group" : "<integer>",
3427
+
"-webkit-box-lines" : "single | multiple",
3428
+
"-webkit-box-ordinal-group" : "<integer>",
3429
+
"-webkit-box-orient" : "horizontal | vertical | inline-axis | block-axis",
3430
+
"-webkit-box-pack" : "start | end | center | justify",
3431
+
"box-decoration-break" : "slice | clone",
3432
+
"box-shadow" : "<box-shadow>",
3433
+
"box-sizing" : "content-box | border-box",
3434
+
"break-after" : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
3435
+
"break-before" : "auto | always | avoid | left | right | page | column | avoid-page | avoid-column",
3436
+
"break-inside" : "auto | avoid | avoid-page | avoid-column",
3437
+
3438
+
//C
3439
+
"caption-side" : "top | bottom",
3440
+
"clear" : "none | right | left | both",
3441
+
"clip" : "<shape> | auto",
3442
+
"-webkit-clip-path" : "<clip-source> | <clip-path> | none",
3443
+
"clip-path" : "<clip-source> | <clip-path> | none",
3444
+
"clip-rule" : "nonzero | evenodd",
3445
+
"color" : "<color>",
3446
+
"color-interpolation" : "auto | sRGB | linearRGB",
3447
+
"color-interpolation-filters" : "auto | sRGB | linearRGB",
3448
+
"color-profile" : 1,
3449
+
"color-rendering" : "auto | optimizeSpeed | optimizeQuality",
3450
+
"column-count" : "<integer> | auto", //https://www.w3.org/TR/css3-multicol/
3451
+
"column-fill" : "auto | balance",
3452
+
"column-gap" : "<length> | normal",
3453
+
"column-rule" : "<border-width> || <border-style> || <color>",
3454
+
"column-rule-color" : "<color>",
3455
+
"column-rule-style" : "<border-style>",
3456
+
"column-rule-width" : "<border-width>",
3457
+
"column-span" : "none | all",
3458
+
"column-width" : "<length> | auto",
3459
+
"columns" : 1,
3460
+
"content" : 1,
3461
+
"counter-increment" : 1,
3462
+
"counter-reset" : 1,
3463
+
"crop" : "<shape> | auto",
3464
+
"cue" : "cue-after | cue-before",
3465
+
"cue-after" : 1,
3466
+
"cue-before" : 1,
3467
+
"cursor" : 1,
3468
+
3469
+
//D
3470
+
"direction" : "ltr | rtl",
3471
+
"display" : "inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | grid | inline-grid | run-in | ruby | ruby-base | ruby-text | ruby-base-container | ruby-text-container | contents | none | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker | -webkit-box | -webkit-inline-box | -ms-flexbox | -ms-inline-flexbox | flex | -webkit-flex | inline-flex | -webkit-inline-flex",
3472
+
"dominant-baseline" : "auto | use-script | no-change | reset-size | ideographic | alphabetic | hanging | mathematical | central | middle | text-after-edge | text-before-edge",
3473
+
"drop-initial-after-adjust" : "central | middle | after-edge | text-after-edge | ideographic | alphabetic | mathematical | <percentage> | <length>",
3474
+
"drop-initial-after-align" : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
3475
+
"drop-initial-before-adjust" : "before-edge | text-before-edge | central | middle | hanging | mathematical | <percentage> | <length>",
3476
+
"drop-initial-before-align" : "caps-height | baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
3477
+
"drop-initial-size" : "auto | line | <length> | <percentage>",
3478
+
"drop-initial-value" : "<integer>",
3479
+
3480
+
//E
3481
+
"elevation" : "<angle> | below | level | above | higher | lower",
3482
+
"empty-cells" : "show | hide",
3483
+
"enable-background" : 1,
3484
+
3485
+
//F
3486
+
"fill" : "<paint>",
3487
+
"fill-opacity" : "<opacity-value>",
3488
+
"fill-rule" : "nonzero | evenodd",
3489
+
"filter" : "<filter-function-list> | none",
3490
+
"fit" : "fill | hidden | meet | slice",
3491
+
"fit-position" : 1,
3492
+
"flex" : "<flex>",
3493
+
"flex-basis" : "<width>",
3494
+
"flex-direction" : "row | row-reverse | column | column-reverse",
3495
+
"flex-flow" : "<flex-direction> || <flex-wrap>",
3496
+
"flex-grow" : "<number>",
3497
+
"flex-shrink" : "<number>",
3498
+
"flex-wrap" : "nowrap | wrap | wrap-reverse",
3499
+
"-webkit-flex" : "<flex>",
3500
+
"-webkit-flex-basis" : "<width>",
3501
+
"-webkit-flex-direction" : "row | row-reverse | column | column-reverse",
3502
+
"-webkit-flex-flow" : "<flex-direction> || <flex-wrap>",
3503
+
"-webkit-flex-grow" : "<number>",
3504
+
"-webkit-flex-shrink" : "<number>",
3505
+
"-webkit-flex-wrap" : "nowrap | wrap | wrap-reverse",
3506
+
"-ms-flex" : "<flex>",
3507
+
"-ms-flex-align" : "start | end | center | stretch | baseline",
3508
+
"-ms-flex-direction" : "row | row-reverse | column | column-reverse",
3509
+
"-ms-flex-order" : "<number>",
3510
+
"-ms-flex-pack" : "start | end | center | justify",
3511
+
"-ms-flex-wrap" : "nowrap | wrap | wrap-reverse",
3512
+
"float" : "left | right | none",
3513
+
"float-offset" : 1,
3514
+
"flood-color" : 1,
3515
+
"flood-opacity" : "<opacity-value>",
3516
+
"font" : "<font-shorthand> | caption | icon | menu | message-box | small-caption | status-bar",
3517
+
"font-family" : "<font-family>",
3518
+
"font-feature-settings" : "<feature-tag-value> | normal",
3519
+
"font-kerning" : "auto | normal | none",
3520
+
"font-size" : "<font-size>",
3521
+
"font-size-adjust" : "<number> | none",
3522
+
"font-stretch" : "<font-stretch>",
3523
+
"font-style" : "<font-style>",
3524
+
"font-variant" : "<font-variant> | normal | none",
3525
+
"font-variant-alternates" : "<font-variant-alternates> | normal",
3526
+
"font-variant-caps" : "<font-variant-caps> | normal",
3527
+
"font-variant-east-asian" : "<font-variant-east-asian> | normal",
3528
+
"font-variant-ligatures" : "<font-variant-ligatures> | normal | none",
3529
+
"font-variant-numeric" : "<font-variant-numeric> | normal",
3530
+
"font-variant-position" : "normal | sub | super",
3531
+
"font-weight" : "<font-weight>",
3532
+
3533
+
//G
3534
+
"glyph-orientation-horizontal" : "<glyph-angle>",
3535
+
"glyph-orientation-vertical" : "auto | <glyph-angle>",
3536
+
"grid" : 1,
3537
+
"grid-area" : 1,
3538
+
"grid-auto-columns" : 1,
3539
+
"grid-auto-flow" : 1,
3540
+
"grid-auto-position" : 1,
3541
+
"grid-auto-rows" : 1,
3542
+
"grid-cell-stacking" : "columns | rows | layer",
3543
+
"grid-column" : 1,
3544
+
"grid-columns" : 1,
3545
+
"grid-column-align" : "start | end | center | stretch",
3546
+
"grid-column-sizing" : 1,
3547
+
"grid-column-start" : 1,
3548
+
"grid-column-end" : 1,
3549
+
"grid-column-span" : "<integer>",
3550
+
"grid-flow" : "none | rows | columns",
3551
+
"grid-layer" : "<integer>",
3552
+
"grid-row" : 1,
3553
+
"grid-rows" : 1,
3554
+
"grid-row-align" : "start | end | center | stretch",
3555
+
"grid-row-start" : 1,
3556
+
"grid-row-end" : 1,
3557
+
"grid-row-span" : "<integer>",
3558
+
"grid-row-sizing" : 1,
3559
+
"grid-template" : 1,
3560
+
"grid-template-areas" : 1,
3561
+
"grid-template-columns" : 1,
3562
+
"grid-template-rows" : 1,
3563
+
3564
+
//H
3565
+
"hanging-punctuation" : 1,
3566
+
"height" : "<margin-width> | <content-sizing>",
3567
+
"hyphenate-after" : "<integer> | auto",
3568
+
"hyphenate-before" : "<integer> | auto",
3569
+
"hyphenate-character" : "<string> | auto",
3570
+
"hyphenate-lines" : "no-limit | <integer>",
3571
+
"hyphenate-resource" : 1,
3572
+
"hyphens" : "none | manual | auto",
3573
+
3574
+
//I
3575
+
"icon" : 1,
3576
+
"image-orientation" : "angle | auto",
3577
+
"image-rendering" : "auto | optimizeSpeed | optimizeQuality",
3578
+
"image-resolution" : 1,
3579
+
"ime-mode" : "auto | normal | active | inactive | disabled",
3580
+
"inline-box-align" : "last | <integer>",
3581
+
3582
+
//J
3583
+
"justify-content" : "flex-start | flex-end | center | space-between | space-around",
3584
+
"-webkit-justify-content" : "flex-start | flex-end | center | space-between | space-around",
3585
+
3586
+
//K
3587
+
"kerning" : "auto | <length>",
3588
+
3589
+
//L
3590
+
"left" : "<margin-width>",
3591
+
"letter-spacing" : "<length> | normal",
3592
+
"line-height" : "<line-height>",
3593
+
"line-break" : "auto | loose | normal | strict",
3594
+
"line-stacking" : 1,
3595
+
"line-stacking-ruby" : "exclude-ruby | include-ruby",
3596
+
"line-stacking-shift" : "consider-shifts | disregard-shifts",
3597
+
"line-stacking-strategy" : "inline-line-height | block-line-height | max-height | grid-height",
3598
+
"list-style" : 1,
3599
+
"list-style-image" : "<uri> | none",
3600
+
"list-style-position" : "inside | outside",
3601
+
"list-style-type" : "disc | circle | square | decimal | decimal-leading-zero | lower-roman | upper-roman | lower-greek | lower-latin | upper-latin | armenian | georgian | lower-alpha | upper-alpha | none",
3602
+
3603
+
//M
3604
+
"margin" : "<margin-width>{1,4}",
3605
+
"margin-bottom" : "<margin-width>",
3606
+
"margin-left" : "<margin-width>",
3607
+
"margin-right" : "<margin-width>",
3608
+
"margin-top" : "<margin-width>",
3609
+
"mark" : 1,
3610
+
"mark-after" : 1,
3611
+
"mark-before" : 1,
3612
+
"marker" : 1,
3613
+
"marker-end" : 1,
3614
+
"marker-mid" : 1,
3615
+
"marker-start" : 1,
3616
+
"marks" : 1,
3617
+
"marquee-direction" : 1,
3618
+
"marquee-play-count" : 1,
3619
+
"marquee-speed" : 1,
3620
+
"marquee-style" : 1,
3621
+
"mask" : 1,
3622
+
"max-height" : "<length> | <percentage> | <content-sizing> | none",
3623
+
"max-width" : "<length> | <percentage> | <content-sizing> | none",
3624
+
"min-height" : "<length> | <percentage> | <content-sizing> | contain-floats | -moz-contain-floats | -webkit-contain-floats",
3625
+
"min-width" : "<length> | <percentage> | <content-sizing> | contain-floats | -moz-contain-floats | -webkit-contain-floats",
3626
+
"move-to" : 1,
3627
+
3628
+
//N
3629
+
"nav-down" : 1,
3630
+
"nav-index" : 1,
3631
+
"nav-left" : 1,
3632
+
"nav-right" : 1,
3633
+
"nav-up" : 1,
3634
+
3635
+
//O
3636
+
"object-fit" : "fill | contain | cover | none | scale-down",
3637
+
"object-position" : "<position>",
3638
+
"opacity" : "<opacity-value>",
3639
+
"order" : "<integer>",
3640
+
"-webkit-order" : "<integer>",
3641
+
"orphans" : "<integer>",
3642
+
"outline" : 1,
3643
+
"outline-color" : "<color> | invert",
3644
+
"outline-offset" : 1,
3645
+
"outline-style" : "<border-style>",
3646
+
"outline-width" : "<border-width>",
3647
+
"overflow" : "visible | hidden | scroll | auto",
3648
+
"overflow-style" : 1,
3649
+
"overflow-wrap" : "normal | break-word",
3650
+
"overflow-x" : 1,
3651
+
"overflow-y" : 1,
3652
+
3653
+
//P
3654
+
"padding" : "<padding-width>{1,4}",
3655
+
"padding-bottom" : "<padding-width>",
3656
+
"padding-left" : "<padding-width>",
3657
+
"padding-right" : "<padding-width>",
3658
+
"padding-top" : "<padding-width>",
3659
+
"page" : 1,
3660
+
"page-break-after" : "auto | always | avoid | left | right",
3661
+
"page-break-before" : "auto | always | avoid | left | right",
3662
+
"page-break-inside" : "auto | avoid",
3663
+
"page-policy" : 1,
3664
+
"pause" : 1,
3665
+
"pause-after" : 1,
3666
+
"pause-before" : 1,
3667
+
"perspective" : 1,
3668
+
"perspective-origin" : 1,
3669
+
"phonemes" : 1,
3670
+
"pitch" : 1,
3671
+
"pitch-range" : 1,
3672
+
"play-during" : 1,
3673
+
"pointer-events" : "auto | none | visiblePainted | visibleFill | visibleStroke | visible | painted | fill | stroke | all",
3674
+
"position" : "static | relative | absolute | fixed",
3675
+
"presentation-level" : 1,
3676
+
"punctuation-trim" : 1,
3677
+
3678
+
//Q
3679
+
"quotes" : 1,
3680
+
3681
+
//R
3682
+
"rendering-intent" : 1,
3683
+
"resize" : 1,
3684
+
"rest" : 1,
3685
+
"rest-after" : 1,
3686
+
"rest-before" : 1,
3687
+
"richness" : 1,
3688
+
"right" : "<margin-width>",
3689
+
"rotation" : 1,
3690
+
"rotation-point" : 1,
3691
+
"ruby-align" : 1,
3692
+
"ruby-overhang" : 1,
3693
+
"ruby-position" : 1,
3694
+
"ruby-span" : 1,
3695
+
3696
+
//S
3697
+
"shape-rendering" : "auto | optimizeSpeed | crispEdges | geometricPrecision",
3698
+
"size" : 1,
3699
+
"speak" : "normal | none | spell-out",
3700
+
"speak-header" : "once | always",
3701
+
"speak-numeral" : "digits | continuous",
3702
+
"speak-punctuation" : "code | none",
3703
+
"speech-rate" : 1,
3704
+
"src" : 1,
3705
+
"stop-color" : 1,
3706
+
"stop-opacity" : "<opacity-value>",
3707
+
"stress" : 1,
3708
+
"string-set" : 1,
3709
+
"stroke" : "<paint>",
3710
+
"stroke-dasharray" : "none | <dasharray>",
3711
+
"stroke-dashoffset" : "<percentage> | <length>",
3712
+
"stroke-linecap" : "butt | round | square",
3713
+
"stroke-linejoin" : "miter | round | bevel",
3714
+
"stroke-miterlimit" : "<miterlimit>",
3715
+
"stroke-opacity" : "<opacity-value>",
3716
+
"stroke-width" : "<percentage> | <length>",
3717
+
3718
+
"table-layout" : "auto | fixed",
3719
+
"tab-size" : "<integer> | <length>",
3720
+
"target" : 1,
3721
+
"target-name" : 1,
3722
+
"target-new" : 1,
3723
+
"target-position" : 1,
3724
+
"text-align" : "left | right | center | justify | match-parent | start | end",
3725
+
"text-align-last" : 1,
3726
+
"text-anchor" : "start | middle | end",
3727
+
"text-decoration" : "<text-decoration-line> || <text-decoration-style> || <text-decoration-color>",
3728
+
"text-decoration-color" : "<text-decoration-color>",
3729
+
"text-decoration-line" : "<text-decoration-line>",
3730
+
"text-decoration-style" : "<text-decoration-style>",
3731
+
"text-emphasis" : 1,
3732
+
"text-height" : 1,
3733
+
"text-indent" : "<length> | <percentage>",
3734
+
"text-justify" : "auto | none | inter-word | inter-ideograph | inter-cluster | distribute | kashida",
3735
+
"text-outline" : 1,
3736
+
"text-overflow" : 1,
3737
+
"text-rendering" : "auto | optimizeSpeed | optimizeLegibility | geometricPrecision",
3738
+
"text-shadow" : 1,
3739
+
"text-transform" : "capitalize | uppercase | lowercase | none",
3740
+
"text-wrap" : "normal | none | avoid",
3741
+
"top" : "<margin-width>",
3742
+
"-ms-touch-action" : "auto | none | pan-x | pan-y | pan-left | pan-right | pan-up | pan-down | manipulation",
3743
+
"touch-action" : "auto | none | pan-x | pan-y | pan-left | pan-right | pan-up | pan-down | manipulation",
3744
+
"transform" : 1,
3745
+
"transform-origin" : 1,
3746
+
"transform-style" : 1,
3747
+
"transition" : 1,
3748
+
"transition-delay" : 1,
3749
+
"transition-duration" : 1,
3750
+
"transition-property" : 1,
3751
+
"transition-timing-function" : 1,
3752
+
3753
+
//U
3754
+
"unicode-bidi" : "normal | embed | isolate | bidi-override | isolate-override | plaintext",
3755
+
"user-modify" : "read-only | read-write | write-only",
3756
+
"user-select" : "none | text | toggle | element | elements | all",
3757
+
3758
+
//V
3759
+
"vertical-align" : "auto | use-script | baseline | sub | super | top | text-top | central | middle | bottom | text-bottom | <percentage> | <length>",
3760
+
"visibility" : "visible | hidden | collapse",
3761
+
"voice-balance" : 1,
3762
+
"voice-duration" : 1,
3763
+
"voice-family" : 1,
3764
+
"voice-pitch" : 1,
3765
+
"voice-pitch-range" : 1,
3766
+
"voice-rate" : 1,
3767
+
"voice-stress" : 1,
3768
+
"voice-volume" : 1,
3769
+
"volume" : 1,
3770
+
3771
+
//W
3772
+
"white-space" : "normal | pre | nowrap | pre-wrap | pre-line | -pre-wrap | -o-pre-wrap | -moz-pre-wrap | -hp-pre-wrap", // https://perishablepress.com/wrapping-content/
3773
+
"white-space-collapse" : 1,
3774
+
"widows" : "<integer>",
3775
+
"width" : "<length> | <percentage> | <content-sizing> | auto",
3776
+
"will-change" : "<will-change>",
3777
+
"word-break" : "normal | keep-all | break-all",
3778
+
"word-spacing" : "<length> | normal",
3779
+
"word-wrap" : "normal | break-word",
3780
+
"writing-mode" : "horizontal-tb | vertical-rl | vertical-lr | lr-tb | rl-tb | tb-rl | bt-rl | tb-lr | bt-lr | lr-bt | rl-bt | lr | rl | tb",
3781
+
3782
+
//Z
3783
+
"z-index" : "<integer> | auto",
3784
+
"zoom" : "<number> | <percentage> | normal"
3785
+
};
3786
+
3787
+
},{}],8:[function(require,module,exports){
3788
+
"use strict";
3789
+
3790
+
module.exports = PropertyName;
3791
+
3792
+
var SyntaxUnit = require("../util/SyntaxUnit");
3793
+
3794
+
var Parser = require("./Parser");
3795
+
3796
+
/**
3797
+
* Represents a selector combinator (whitespace, +, >).
3798
+
* @namespace parserlib.css
3799
+
* @class PropertyName
3800
+
* @extends parserlib.util.SyntaxUnit
3801
+
* @constructor
3802
+
* @param {String} text The text representation of the unit.
3803
+
* @param {String} hack The type of IE hack applied ("*", "_", or null).
3804
+
* @param {int} line The line of text on which the unit resides.
3805
+
* @param {int} col The column of text on which the unit resides.
3806
+
*/
3807
+
function PropertyName(text, hack, line, col) {
3808
+
3809
+
SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_NAME_TYPE);
3810
+
3811
+
/**
3812
+
* The type of IE hack applied ("*", "_", or null).
3813
+
* @type String
3814
+
* @property hack
3815
+
*/
3816
+
this.hack = hack;
3817
+
3818
+
}
3819
+
3820
+
PropertyName.prototype = new SyntaxUnit();
3821
+
PropertyName.prototype.constructor = PropertyName;
3822
+
PropertyName.prototype.toString = function() {
3823
+
return (this.hack ? this.hack : "") + this.text;
3824
+
};
3825
+
3826
+
},{"../util/SyntaxUnit":26,"./Parser":6}],9:[function(require,module,exports){
3827
+
"use strict";
3828
+
3829
+
module.exports = PropertyValue;
3830
+
3831
+
var SyntaxUnit = require("../util/SyntaxUnit");
3832
+
3833
+
var Parser = require("./Parser");
3834
+
3835
+
/**
3836
+
* Represents a single part of a CSS property value, meaning that it represents
3837
+
* just everything single part between ":" and ";". If there are multiple values
3838
+
* separated by commas, this type represents just one of the values.
3839
+
* @param {String[]} parts An array of value parts making up this value.
3840
+
* @param {int} line The line of text on which the unit resides.
3841
+
* @param {int} col The column of text on which the unit resides.
3842
+
* @namespace parserlib.css
3843
+
* @class PropertyValue
3844
+
* @extends parserlib.util.SyntaxUnit
3845
+
* @constructor
3846
+
*/
3847
+
function PropertyValue(parts, line, col) {
3848
+
3849
+
SyntaxUnit.call(this, parts.join(" "), line, col, Parser.PROPERTY_VALUE_TYPE);
3850
+
3851
+
/**
3852
+
* The parts that make up the selector.
3853
+
* @type Array
3854
+
* @property parts
3855
+
*/
3856
+
this.parts = parts;
3857
+
3858
+
}
3859
+
3860
+
PropertyValue.prototype = new SyntaxUnit();
3861
+
PropertyValue.prototype.constructor = PropertyValue;
3862
+
3863
+
3864
+
},{"../util/SyntaxUnit":26,"./Parser":6}],10:[function(require,module,exports){
3865
+
"use strict";
3866
+
3867
+
module.exports = PropertyValueIterator;
3868
+
3869
+
/**
3870
+
* A utility class that allows for easy iteration over the various parts of a
3871
+
* property value.
3872
+
* @param {parserlib.css.PropertyValue} value The property value to iterate over.
3873
+
* @namespace parserlib.css
3874
+
* @class PropertyValueIterator
3875
+
* @constructor
3876
+
*/
3877
+
function PropertyValueIterator(value) {
3878
+
3879
+
/**
3880
+
* Iterator value
3881
+
* @type int
3882
+
* @property _i
3883
+
* @private
3884
+
*/
3885
+
this._i = 0;
3886
+
3887
+
/**
3888
+
* The parts that make up the value.
3889
+
* @type Array
3890
+
* @property _parts
3891
+
* @private
3892
+
*/
3893
+
this._parts = value.parts;
3894
+
3895
+
/**
3896
+
* Keeps track of bookmarks along the way.
3897
+
* @type Array
3898
+
* @property _marks
3899
+
* @private
3900
+
*/
3901
+
this._marks = [];
3902
+
3903
+
/**
3904
+
* Holds the original property value.
3905
+
* @type parserlib.css.PropertyValue
3906
+
* @property value
3907
+
*/
3908
+
this.value = value;
3909
+
3910
+
}
3911
+
3912
+
/**
3913
+
* Returns the total number of parts in the value.
3914
+
* @return {int} The total number of parts in the value.
3915
+
* @method count
3916
+
*/
3917
+
PropertyValueIterator.prototype.count = function() {
3918
+
return this._parts.length;
3919
+
};
3920
+
3921
+
/**
3922
+
* Indicates if the iterator is positioned at the first item.
3923
+
* @return {Boolean} True if positioned at first item, false if not.
3924
+
* @method isFirst
3925
+
*/
3926
+
PropertyValueIterator.prototype.isFirst = function() {
3927
+
return this._i === 0;
3928
+
};
3929
+
3930
+
/**
3931
+
* Indicates if there are more parts of the property value.
3932
+
* @return {Boolean} True if there are more parts, false if not.
3933
+
* @method hasNext
3934
+
*/
3935
+
PropertyValueIterator.prototype.hasNext = function() {
3936
+
return this._i < this._parts.length;
3937
+
};
3938
+
3939
+
/**
3940
+
* Marks the current spot in the iteration so it can be restored to
3941
+
* later on.
3942
+
* @return {void}
3943
+
* @method mark
3944
+
*/
3945
+
PropertyValueIterator.prototype.mark = function() {
3946
+
this._marks.push(this._i);
3947
+
};
3948
+
3949
+
/**
3950
+
* Returns the next part of the property value or null if there is no next
3951
+
* part. Does not move the internal counter forward.
3952
+
* @return {parserlib.css.PropertyValuePart} The next part of the property value or null if there is no next
3953
+
* part.
3954
+
* @method peek
3955
+
*/
3956
+
PropertyValueIterator.prototype.peek = function(count) {
3957
+
return this.hasNext() ? this._parts[this._i + (count || 0)] : null;
3958
+
};
3959
+
3960
+
/**
3961
+
* Returns the next part of the property value or null if there is no next
3962
+
* part.
3963
+
* @return {parserlib.css.PropertyValuePart} The next part of the property value or null if there is no next
3964
+
* part.
3965
+
* @method next
3966
+
*/
3967
+
PropertyValueIterator.prototype.next = function() {
3968
+
return this.hasNext() ? this._parts[this._i++] : null;
3969
+
};
3970
+
3971
+
/**
3972
+
* Returns the previous part of the property value or null if there is no
3973
+
* previous part.
3974
+
* @return {parserlib.css.PropertyValuePart} The previous part of the
3975
+
* property value or null if there is no previous part.
3976
+
* @method previous
3977
+
*/
3978
+
PropertyValueIterator.prototype.previous = function() {
3979
+
return this._i > 0 ? this._parts[--this._i] : null;
3980
+
};
3981
+
3982
+
/**
3983
+
* Restores the last saved bookmark.
3984
+
* @return {void}
3985
+
* @method restore
3986
+
*/
3987
+
PropertyValueIterator.prototype.restore = function() {
3988
+
if (this._marks.length) {
3989
+
this._i = this._marks.pop();
3990
+
}
3991
+
};
3992
+
3993
+
/**
3994
+
* Drops the last saved bookmark.
3995
+
* @return {void}
3996
+
* @method drop
3997
+
*/
3998
+
PropertyValueIterator.prototype.drop = function() {
3999
+
this._marks.pop();
4000
+
};
4001
+
4002
+
},{}],11:[function(require,module,exports){
4003
+
"use strict";
4004
+
4005
+
module.exports = PropertyValuePart;
4006
+
4007
+
var SyntaxUnit = require("../util/SyntaxUnit");
4008
+
4009
+
var Colors = require("./Colors");
4010
+
var Parser = require("./Parser");
4011
+
var Tokens = require("./Tokens");
4012
+
4013
+
/**
4014
+
* Represents a single part of a CSS property value, meaning that it represents
4015
+
* just one part of the data between ":" and ";".
4016
+
* @param {String} text The text representation of the unit.
4017
+
* @param {int} line The line of text on which the unit resides.
4018
+
* @param {int} col The column of text on which the unit resides.
4019
+
* @namespace parserlib.css
4020
+
* @class PropertyValuePart
4021
+
* @extends parserlib.util.SyntaxUnit
4022
+
* @constructor
4023
+
*/
4024
+
function PropertyValuePart(text, line, col, optionalHint) {
4025
+
var hint = optionalHint || {};
4026
+
4027
+
SyntaxUnit.call(this, text, line, col, Parser.PROPERTY_VALUE_PART_TYPE);
4028
+
4029
+
/**
4030
+
* Indicates the type of value unit.
4031
+
* @type String
4032
+
* @property type
4033
+
*/
4034
+
this.type = "unknown";
4035
+
4036
+
//figure out what type of data it is
4037
+
4038
+
var temp;
4039
+
4040
+
//it is a measurement?
4041
+
if (/^([+\-]?[\d\.]+)([a-z]+)$/i.test(text)) { //dimension
4042
+
this.type = "dimension";
4043
+
this.value = +RegExp.$1;
4044
+
this.units = RegExp.$2;
4045
+
4046
+
//try to narrow down
4047
+
switch (this.units.toLowerCase()) {
4048
+
4049
+
case "em":
4050
+
case "rem":
4051
+
case "ex":
4052
+
case "px":
4053
+
case "cm":
4054
+
case "mm":
4055
+
case "in":
4056
+
case "pt":
4057
+
case "pc":
4058
+
case "ch":
4059
+
case "vh":
4060
+
case "vw":
4061
+
case "vmax":
4062
+
case "vmin":
4063
+
this.type = "length";
4064
+
break;
4065
+
4066
+
case "fr":
4067
+
this.type = "grid";
4068
+
break;
4069
+
4070
+
case "deg":
4071
+
case "rad":
4072
+
case "grad":
4073
+
case "turn":
4074
+
this.type = "angle";
4075
+
break;
4076
+
4077
+
case "ms":
4078
+
case "s":
4079
+
this.type = "time";
4080
+
break;
4081
+
4082
+
case "hz":
4083
+
case "khz":
4084
+
this.type = "frequency";
4085
+
break;
4086
+
4087
+
case "dpi":
4088
+
case "dpcm":
4089
+
this.type = "resolution";
4090
+
break;
4091
+
4092
+
//default
4093
+
4094
+
}
4095
+
4096
+
} else if (/^([+\-]?[\d\.]+)%$/i.test(text)) { //percentage
4097
+
this.type = "percentage";
4098
+
this.value = +RegExp.$1;
4099
+
} else if (/^([+\-]?\d+)$/i.test(text)) { //integer
4100
+
this.type = "integer";
4101
+
this.value = +RegExp.$1;
4102
+
} else if (/^([+\-]?[\d\.]+)$/i.test(text)) { //number
4103
+
this.type = "number";
4104
+
this.value = +RegExp.$1;
4105
+
4106
+
} else if (/^#([a-f0-9]{3,6})/i.test(text)) { //hexcolor
4107
+
this.type = "color";
4108
+
temp = RegExp.$1;
4109
+
if (temp.length === 3) {
4110
+
this.red = parseInt(temp.charAt(0)+temp.charAt(0), 16);
4111
+
this.green = parseInt(temp.charAt(1)+temp.charAt(1), 16);
4112
+
this.blue = parseInt(temp.charAt(2)+temp.charAt(2), 16);
4113
+
} else {
4114
+
this.red = parseInt(temp.substring(0, 2), 16);
4115
+
this.green = parseInt(temp.substring(2, 4), 16);
4116
+
this.blue = parseInt(temp.substring(4, 6), 16);
4117
+
}
4118
+
} else if (/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i.test(text)) { //rgb() color with absolute numbers
4119
+
this.type = "color";
4120
+
this.red = +RegExp.$1;
4121
+
this.green = +RegExp.$2;
4122
+
this.blue = +RegExp.$3;
4123
+
} else if (/^rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)) { //rgb() color with percentages
4124
+
this.type = "color";
4125
+
this.red = +RegExp.$1 * 255 / 100;
4126
+
this.green = +RegExp.$2 * 255 / 100;
4127
+
this.blue = +RegExp.$3 * 255 / 100;
4128
+
} else if (/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //rgba() color with absolute numbers
4129
+
this.type = "color";
4130
+
this.red = +RegExp.$1;
4131
+
this.green = +RegExp.$2;
4132
+
this.blue = +RegExp.$3;
4133
+
this.alpha = +RegExp.$4;
4134
+
} else if (/^rgba\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //rgba() color with percentages
4135
+
this.type = "color";
4136
+
this.red = +RegExp.$1 * 255 / 100;
4137
+
this.green = +RegExp.$2 * 255 / 100;
4138
+
this.blue = +RegExp.$3 * 255 / 100;
4139
+
this.alpha = +RegExp.$4;
4140
+
} else if (/^hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i.test(text)) { //hsl()
4141
+
this.type = "color";
4142
+
this.hue = +RegExp.$1;
4143
+
this.saturation = +RegExp.$2 / 100;
4144
+
this.lightness = +RegExp.$3 / 100;
4145
+
} else if (/^hsla\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*,\s*([\d\.]+)\s*\)/i.test(text)) { //hsla() color with percentages
4146
+
this.type = "color";
4147
+
this.hue = +RegExp.$1;
4148
+
this.saturation = +RegExp.$2 / 100;
4149
+
this.lightness = +RegExp.$3 / 100;
4150
+
this.alpha = +RegExp.$4;
4151
+
} else if (/^url\(("([^\\"]|\\.)*")\)/i.test(text)) { //URI
4152
+
// generated by TokenStream.readURI, so always double-quoted.
4153
+
this.type = "uri";
4154
+
this.uri = PropertyValuePart.parseString(RegExp.$1);
4155
+
} else if (/^([^\(]+)\(/i.test(text)) {
4156
+
this.type = "function";
4157
+
this.name = RegExp.$1;
4158
+
this.value = text;
4159
+
} else if (/^"([^\n\r\f\\"]|\\\r\n|\\[^\r0-9a-f]|\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)*"/i.test(text)) { //double-quoted string
4160
+
this.type = "string";
4161
+
this.value = PropertyValuePart.parseString(text);
4162
+
} else if (/^'([^\n\r\f\\']|\\\r\n|\\[^\r0-9a-f]|\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)*'/i.test(text)) { //single-quoted string
4163
+
this.type = "string";
4164
+
this.value = PropertyValuePart.parseString(text);
4165
+
} else if (Colors[text.toLowerCase()]) { //named color
4166
+
this.type = "color";
4167
+
temp = Colors[text.toLowerCase()].substring(1);
4168
+
this.red = parseInt(temp.substring(0, 2), 16);
4169
+
this.green = parseInt(temp.substring(2, 4), 16);
4170
+
this.blue = parseInt(temp.substring(4, 6), 16);
4171
+
} else if (/^[,\/]$/.test(text)) {
4172
+
this.type = "operator";
4173
+
this.value = text;
4174
+
} else if (/^-?[a-z_\u00A0-\uFFFF][a-z0-9\-_\u00A0-\uFFFF]*$/i.test(text)) {
4175
+
this.type = "identifier";
4176
+
this.value = text;
4177
+
}
4178
+
4179
+
// There can be ambiguity with escape sequences in identifiers, as
4180
+
// well as with "color" parts which are also "identifiers", so record
4181
+
// an explicit hint when the token generating this PropertyValuePart
4182
+
// was an identifier.
4183
+
this.wasIdent = Boolean(hint.ident);
4184
+
4185
+
}
4186
+
4187
+
PropertyValuePart.prototype = new SyntaxUnit();
4188
+
PropertyValuePart.prototype.constructor = PropertyValuePart;
4189
+
4190
+
/**
4191
+
* Helper method to parse a CSS string.
4192
+
*/
4193
+
PropertyValuePart.parseString = function(str) {
4194
+
str = str.slice(1, -1); // Strip surrounding single/double quotes
4195
+
var replacer = function(match, esc) {
4196
+
if (/^(\n|\r\n|\r|\f)$/.test(esc)) {
4197
+
return "";
4198
+
}
4199
+
var m = /^[0-9a-f]{1,6}/i.exec(esc);
4200
+
if (m) {
4201
+
var codePoint = parseInt(m[0], 16);
4202
+
if (String.fromCodePoint) {
4203
+
return String.fromCodePoint(codePoint);
4204
+
} else {
4205
+
// XXX No support for surrogates on old JavaScript engines.
4206
+
return String.fromCharCode(codePoint);
4207
+
}
4208
+
}
4209
+
return esc;
4210
+
};
4211
+
return str.replace(/\\(\r\n|[^\r0-9a-f]|[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?)/ig,
4212
+
replacer);
4213
+
};
4214
+
4215
+
/**
4216
+
* Helper method to serialize a CSS string.
4217
+
*/
4218
+
PropertyValuePart.serializeString = function(value) {
4219
+
var replacer = function(match, c) {
4220
+
if (c === "\"") {
4221
+
return "\\" + c;
4222
+
}
4223
+
var cp = String.codePointAt ? String.codePointAt(0) :
4224
+
// We only escape non-surrogate chars, so using charCodeAt
4225
+
// is harmless here.
4226
+
String.charCodeAt(0);
4227
+
return "\\" + cp.toString(16) + " ";
4228
+
};
4229
+
return "\"" + value.replace(/["\r\n\f]/g, replacer) + "\"";
4230
+
};
4231
+
4232
+
/**
4233
+
* Create a new syntax unit based solely on the given token.
4234
+
* Convenience method for creating a new syntax unit when
4235
+
* it represents a single token instead of multiple.
4236
+
* @param {Object} token The token object to represent.
4237
+
* @return {parserlib.css.PropertyValuePart} The object representing the token.
4238
+
* @static
4239
+
* @method fromToken
4240
+
*/
4241
+
PropertyValuePart.fromToken = function(token) {
4242
+
var part = new PropertyValuePart(token.value, token.startLine, token.startCol, {
4243
+
// Tokens can have escaped characters that would fool the type
4244
+
// identification in the PropertyValuePart constructor, so pass
4245
+
// in a hint if this was an identifier.
4246
+
ident: token.type === Tokens.IDENT
4247
+
});
4248
+
return part;
4249
+
};
4250
+
4251
+
},{"../util/SyntaxUnit":26,"./Colors":1,"./Parser":6,"./Tokens":18}],12:[function(require,module,exports){
4252
+
"use strict";
4253
+
4254
+
var Pseudos = module.exports = {
4255
+
__proto__: null,
4256
+
":first-letter": 1,
4257
+
":first-line": 1,
4258
+
":before": 1,
4259
+
":after": 1
4260
+
};
4261
+
4262
+
Pseudos.ELEMENT = 1;
4263
+
Pseudos.CLASS = 2;
4264
+
4265
+
Pseudos.isElement = function(pseudo) {
4266
+
return pseudo.indexOf("::") === 0 || Pseudos[pseudo.toLowerCase()] === Pseudos.ELEMENT;
4267
+
};
4268
+
4269
+
},{}],13:[function(require,module,exports){
4270
+
"use strict";
4271
+
4272
+
module.exports = Selector;
4273
+
4274
+
var SyntaxUnit = require("../util/SyntaxUnit");
4275
+
4276
+
var Parser = require("./Parser");
4277
+
var Specificity = require("./Specificity");
4278
+
4279
+
/**
4280
+
* Represents an entire single selector, including all parts but not
4281
+
* including multiple selectors (those separated by commas).
4282
+
* @namespace parserlib.css
4283
+
* @class Selector
4284
+
* @extends parserlib.util.SyntaxUnit
4285
+
* @constructor
4286
+
* @param {Array} parts Array of selectors parts making up this selector.
4287
+
* @param {int} line The line of text on which the unit resides.
4288
+
* @param {int} col The column of text on which the unit resides.
4289
+
*/
4290
+
function Selector(parts, line, col) {
4291
+
4292
+
SyntaxUnit.call(this, parts.join(" "), line, col, Parser.SELECTOR_TYPE);
4293
+
4294
+
/**
4295
+
* The parts that make up the selector.
4296
+
* @type Array
4297
+
* @property parts
4298
+
*/
4299
+
this.parts = parts;
4300
+
4301
+
/**
4302
+
* The specificity of the selector.
4303
+
* @type parserlib.css.Specificity
4304
+
* @property specificity
4305
+
*/
4306
+
this.specificity = Specificity.calculate(this);
4307
+
4308
+
}
4309
+
4310
+
Selector.prototype = new SyntaxUnit();
4311
+
Selector.prototype.constructor = Selector;
4312
+
4313
+
4314
+
},{"../util/SyntaxUnit":26,"./Parser":6,"./Specificity":16}],14:[function(require,module,exports){
4315
+
"use strict";
4316
+
4317
+
module.exports = SelectorPart;
4318
+
4319
+
var SyntaxUnit = require("../util/SyntaxUnit");
4320
+
4321
+
var Parser = require("./Parser");
4322
+
4323
+
/**
4324
+
* Represents a single part of a selector string, meaning a single set of
4325
+
* element name and modifiers. This does not include combinators such as
4326
+
* spaces, +, >, etc.
4327
+
* @namespace parserlib.css
4328
+
* @class SelectorPart
4329
+
* @extends parserlib.util.SyntaxUnit
4330
+
* @constructor
4331
+
* @param {String} elementName The element name in the selector or null
4332
+
* if there is no element name.
4333
+
* @param {Array} modifiers Array of individual modifiers for the element.
4334
+
* May be empty if there are none.
4335
+
* @param {String} text The text representation of the unit.
4336
+
* @param {int} line The line of text on which the unit resides.
4337
+
* @param {int} col The column of text on which the unit resides.
4338
+
*/
4339
+
function SelectorPart(elementName, modifiers, text, line, col) {
4340
+
4341
+
SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_PART_TYPE);
4342
+
4343
+
/**
4344
+
* The tag name of the element to which this part
4345
+
* of the selector affects.
4346
+
* @type String
4347
+
* @property elementName
4348
+
*/
4349
+
this.elementName = elementName;
4350
+
4351
+
/**
4352
+
* The parts that come after the element name, such as class names, IDs,
4353
+
* pseudo classes/elements, etc.
4354
+
* @type Array
4355
+
* @property modifiers
4356
+
*/
4357
+
this.modifiers = modifiers;
4358
+
4359
+
}
4360
+
4361
+
SelectorPart.prototype = new SyntaxUnit();
4362
+
SelectorPart.prototype.constructor = SelectorPart;
4363
+
4364
+
4365
+
},{"../util/SyntaxUnit":26,"./Parser":6}],15:[function(require,module,exports){
4366
+
"use strict";
4367
+
4368
+
module.exports = SelectorSubPart;
4369
+
4370
+
var SyntaxUnit = require("../util/SyntaxUnit");
4371
+
4372
+
var Parser = require("./Parser");
4373
+
4374
+
/**
4375
+
* Represents a selector modifier string, meaning a class name, element name,
4376
+
* element ID, pseudo rule, etc.
4377
+
* @namespace parserlib.css
4378
+
* @class SelectorSubPart
4379
+
* @extends parserlib.util.SyntaxUnit
4380
+
* @constructor
4381
+
* @param {String} text The text representation of the unit.
4382
+
* @param {String} type The type of selector modifier.
4383
+
* @param {int} line The line of text on which the unit resides.
4384
+
* @param {int} col The column of text on which the unit resides.
4385
+
*/
4386
+
function SelectorSubPart(text, type, line, col) {
4387
+
4388
+
SyntaxUnit.call(this, text, line, col, Parser.SELECTOR_SUB_PART_TYPE);
4389
+
4390
+
/**
4391
+
* The type of modifier.
4392
+
* @type String
4393
+
* @property type
4394
+
*/
4395
+
this.type = type;
4396
+
4397
+
/**
4398
+
* Some subparts have arguments, this represents them.
4399
+
* @type Array
4400
+
* @property args
4401
+
*/
4402
+
this.args = [];
4403
+
4404
+
}
4405
+
4406
+
SelectorSubPart.prototype = new SyntaxUnit();
4407
+
SelectorSubPart.prototype.constructor = SelectorSubPart;
4408
+
4409
+
4410
+
},{"../util/SyntaxUnit":26,"./Parser":6}],16:[function(require,module,exports){
4411
+
"use strict";
4412
+
4413
+
module.exports = Specificity;
4414
+
4415
+
var Pseudos = require("./Pseudos");
4416
+
var SelectorPart = require("./SelectorPart");
4417
+
4418
+
/**
4419
+
* Represents a selector's specificity.
4420
+
* @namespace parserlib.css
4421
+
* @class Specificity
4422
+
* @constructor
4423
+
* @param {int} a Should be 1 for inline styles, zero for stylesheet styles
4424
+
* @param {int} b Number of ID selectors
4425
+
* @param {int} c Number of classes and pseudo classes
4426
+
* @param {int} d Number of element names and pseudo elements
4427
+
*/
4428
+
function Specificity(a, b, c, d) {
4429
+
this.a = a;
4430
+
this.b = b;
4431
+
this.c = c;
4432
+
this.d = d;
4433
+
}
4434
+
4435
+
Specificity.prototype = {
4436
+
constructor: Specificity,
4437
+
4438
+
/**
4439
+
* Compare this specificity to another.
4440
+
* @param {Specificity} other The other specificity to compare to.
4441
+
* @return {int} -1 if the other specificity is larger, 1 if smaller, 0 if equal.
4442
+
* @method compare
4443
+
*/
4444
+
compare: function(other) {
4445
+
var comps = ["a", "b", "c", "d"],
4446
+
i, len;
4447
+
4448
+
for (i=0, len=comps.length; i < len; i++) {
4449
+
if (this[comps[i]] < other[comps[i]]) {
4450
+
return -1;
4451
+
} else if (this[comps[i]] > other[comps[i]]) {
4452
+
return 1;
4453
+
}
4454
+
}
4455
+
4456
+
return 0;
4457
+
},
4458
+
4459
+
/**
4460
+
* Creates a numeric value for the specificity.
4461
+
* @return {int} The numeric value for the specificity.
4462
+
* @method valueOf
4463
+
*/
4464
+
valueOf: function() {
4465
+
return (this.a * 1000) + (this.b * 100) + (this.c * 10) + this.d;
4466
+
},
4467
+
4468
+
/**
4469
+
* Returns a string representation for specificity.
4470
+
* @return {String} The string representation of specificity.
4471
+
* @method toString
4472
+
*/
4473
+
toString: function() {
4474
+
return this.a + "," + this.b + "," + this.c + "," + this.d;
4475
+
}
4476
+
4477
+
};
4478
+
4479
+
/**
4480
+
* Calculates the specificity of the given selector.
4481
+
* @param {parserlib.css.Selector} The selector to calculate specificity for.
4482
+
* @return {parserlib.css.Specificity} The specificity of the selector.
4483
+
* @static
4484
+
* @method calculate
4485
+
*/
4486
+
Specificity.calculate = function(selector) {
4487
+
4488
+
var i, len,
4489
+
part,
4490
+
b=0, c=0, d=0;
4491
+
4492
+
function updateValues(part) {
4493
+
4494
+
var i, j, len, num,
4495
+
elementName = part.elementName ? part.elementName.text : "",
4496
+
modifier;
4497
+
4498
+
if (elementName && elementName.charAt(elementName.length-1) !== "*") {
4499
+
d++;
4500
+
}
4501
+
4502
+
for (i=0, len=part.modifiers.length; i < len; i++) {
4503
+
modifier = part.modifiers[i];
4504
+
switch (modifier.type) {
4505
+
case "class":
4506
+
case "attribute":
4507
+
c++;
4508
+
break;
4509
+
4510
+
case "id":
4511
+
b++;
4512
+
break;
4513
+
4514
+
case "pseudo":
4515
+
if (Pseudos.isElement(modifier.text)) {
4516
+
d++;
4517
+
} else {
4518
+
c++;
4519
+
}
4520
+
break;
4521
+
4522
+
case "not":
4523
+
for (j=0, num=modifier.args.length; j < num; j++) {
4524
+
updateValues(modifier.args[j]);
4525
+
}
4526
+
}
4527
+
}
4528
+
}
4529
+
4530
+
for (i=0, len=selector.parts.length; i < len; i++) {
4531
+
part = selector.parts[i];
4532
+
4533
+
if (part instanceof SelectorPart) {
4534
+
updateValues(part);
4535
+
}
4536
+
}
4537
+
4538
+
return new Specificity(0, b, c, d);
4539
+
};
4540
+
4541
+
},{"./Pseudos":12,"./SelectorPart":14}],17:[function(require,module,exports){
4542
+
"use strict";
4543
+
4544
+
module.exports = TokenStream;
4545
+
4546
+
var TokenStreamBase = require("../util/TokenStreamBase");
4547
+
4548
+
var PropertyValuePart = require("./PropertyValuePart");
4549
+
var Tokens = require("./Tokens");
4550
+
4551
+
var h = /^[0-9a-fA-F]$/,
4552
+
nonascii = /^[\u00A0-\uFFFF]$/,
4553
+
nl = /\n|\r\n|\r|\f/,
4554
+
whitespace = /\u0009|\u000a|\u000c|\u000d|\u0020/;
4555
+
4556
+
//-----------------------------------------------------------------------------
4557
+
// Helper functions
4558
+
//-----------------------------------------------------------------------------
4559
+
4560
+
4561
+
function isHexDigit(c) {
4562
+
return c !== null && h.test(c);
4563
+
}
4564
+
4565
+
function isDigit(c) {
4566
+
return c !== null && /\d/.test(c);
4567
+
}
4568
+
4569
+
function isWhitespace(c) {
4570
+
return c !== null && whitespace.test(c);
4571
+
}
4572
+
4573
+
function isNewLine(c) {
4574
+
return c !== null && nl.test(c);
4575
+
}
4576
+
4577
+
function isNameStart(c) {
4578
+
return c !== null && /[a-z_\u00A0-\uFFFF\\]/i.test(c);
4579
+
}
4580
+
4581
+
function isNameChar(c) {
4582
+
return c !== null && (isNameStart(c) || /[0-9\-\\]/.test(c));
4583
+
}
4584
+
4585
+
function isIdentStart(c) {
4586
+
return c !== null && (isNameStart(c) || /\-\\/.test(c));
4587
+
}
4588
+
4589
+
function mix(receiver, supplier) {
4590
+
for (var prop in supplier) {
4591
+
if (Object.prototype.hasOwnProperty.call(supplier, prop)) {
4592
+
receiver[prop] = supplier[prop];
4593
+
}
4594
+
}
4595
+
return receiver;
4596
+
}
4597
+
4598
+
//-----------------------------------------------------------------------------
4599
+
// CSS Token Stream
4600
+
//-----------------------------------------------------------------------------
4601
+
4602
+
4603
+
/**
4604
+
* A token stream that produces CSS tokens.
4605
+
* @param {String|Reader} input The source of text to tokenize.
4606
+
* @constructor
4607
+
* @class TokenStream
4608
+
* @namespace parserlib.css
4609
+
*/
4610
+
function TokenStream(input) {
4611
+
TokenStreamBase.call(this, input, Tokens);
4612
+
}
4613
+
4614
+
TokenStream.prototype = mix(new TokenStreamBase(), {
4615
+
4616
+
/**
4617
+
* Overrides the TokenStreamBase method of the same name
4618
+
* to produce CSS tokens.
4619
+
* @return {Object} A token object representing the next token.
4620
+
* @method _getToken
4621
+
* @private
4622
+
*/
4623
+
_getToken: function() {
4624
+
4625
+
var c,
4626
+
reader = this._reader,
4627
+
token = null,
4628
+
startLine = reader.getLine(),
4629
+
startCol = reader.getCol();
4630
+
4631
+
c = reader.read();
4632
+
4633
+
4634
+
while (c) {
4635
+
switch (c) {
4636
+
4637
+
/*
4638
+
* Potential tokens:
4639
+
* - COMMENT
4640
+
* - SLASH
4641
+
* - CHAR
4642
+
*/
4643
+
case "/":
4644
+
4645
+
if (reader.peek() === "*") {
4646
+
token = this.commentToken(c, startLine, startCol);
4647
+
} else {
4648
+
token = this.charToken(c, startLine, startCol);
4649
+
}
4650
+
break;
4651
+
4652
+
/*
4653
+
* Potential tokens:
4654
+
* - DASHMATCH
4655
+
* - INCLUDES
4656
+
* - PREFIXMATCH
4657
+
* - SUFFIXMATCH
4658
+
* - SUBSTRINGMATCH
4659
+
* - CHAR
4660
+
*/
4661
+
case "|":
4662
+
case "~":
4663
+
case "^":
4664
+
case "$":
4665
+
case "*":
4666
+
if (reader.peek() === "=") {
4667
+
token = this.comparisonToken(c, startLine, startCol);
4668
+
} else {
4669
+
token = this.charToken(c, startLine, startCol);
4670
+
}
4671
+
break;
4672
+
4673
+
/*
4674
+
* Potential tokens:
4675
+
* - STRING
4676
+
* - INVALID
4677
+
*/
4678
+
case "\"":
4679
+
case "'":
4680
+
token = this.stringToken(c, startLine, startCol);
4681
+
break;
4682
+
4683
+
/*
4684
+
* Potential tokens:
4685
+
* - HASH
4686
+
* - CHAR
4687
+
*/
4688
+
case "#":
4689
+
if (isNameChar(reader.peek())) {
4690
+
token = this.hashToken(c, startLine, startCol);
4691
+
} else {
4692
+
token = this.charToken(c, startLine, startCol);
4693
+
}
4694
+
break;
4695
+
4696
+
/*
4697
+
* Potential tokens:
4698
+
* - DOT
4699
+
* - NUMBER
4700
+
* - DIMENSION
4701
+
* - PERCENTAGE
4702
+
*/
4703
+
case ".":
4704
+
if (isDigit(reader.peek())) {
4705
+
token = this.numberToken(c, startLine, startCol);
4706
+
} else {
4707
+
token = this.charToken(c, startLine, startCol);
4708
+
}
4709
+
break;
4710
+
4711
+
/*
4712
+
* Potential tokens:
4713
+
* - CDC
4714
+
* - MINUS
4715
+
* - NUMBER
4716
+
* - DIMENSION
4717
+
* - PERCENTAGE
4718
+
*/
4719
+
case "-":
4720
+
if (reader.peek() === "-") { //could be closing HTML-style comment
4721
+
token = this.htmlCommentEndToken(c, startLine, startCol);
4722
+
} else if (isNameStart(reader.peek())) {
4723
+
token = this.identOrFunctionToken(c, startLine, startCol);
4724
+
} else {
4725
+
token = this.charToken(c, startLine, startCol);
4726
+
}
4727
+
break;
4728
+
4729
+
/*
4730
+
* Potential tokens:
4731
+
* - IMPORTANT_SYM
4732
+
* - CHAR
4733
+
*/
4734
+
case "!":
4735
+
token = this.importantToken(c, startLine, startCol);
4736
+
break;
4737
+
4738
+
/*
4739
+
* Any at-keyword or CHAR
4740
+
*/
4741
+
case "@":
4742
+
token = this.atRuleToken(c, startLine, startCol);
4743
+
break;
4744
+
4745
+
/*
4746
+
* Potential tokens:
4747
+
* - NOT
4748
+
* - CHAR
4749
+
*/
4750
+
case ":":
4751
+
token = this.notToken(c, startLine, startCol);
4752
+
break;
4753
+
4754
+
/*
4755
+
* Potential tokens:
4756
+
* - CDO
4757
+
* - CHAR
4758
+
*/
4759
+
case "<":
4760
+
token = this.htmlCommentStartToken(c, startLine, startCol);
4761
+
break;
4762
+
4763
+
/*
4764
+
* Potential tokens:
4765
+
* - IDENT
4766
+
* - CHAR
4767
+
*/
4768
+
case "\\":
4769
+
if (/[^\r\n\f]/.test(reader.peek())) {
4770
+
token = this.identOrFunctionToken(this.readEscape(c, true), startLine, startCol);
4771
+
} else {
4772
+
token = this.charToken(c, startLine, startCol);
4773
+
}
4774
+
break;
4775
+
4776
+
/*
4777
+
* Potential tokens:
4778
+
* - UNICODE_RANGE
4779
+
* - URL
4780
+
* - CHAR
4781
+
*/
4782
+
case "U":
4783
+
case "u":
4784
+
if (reader.peek() === "+") {
4785
+
token = this.unicodeRangeToken(c, startLine, startCol);
4786
+
break;
4787
+
}
4788
+
/* falls through */
4789
+
default:
4790
+
4791
+
/*
4792
+
* Potential tokens:
4793
+
* - NUMBER
4794
+
* - DIMENSION
4795
+
* - LENGTH
4796
+
* - FREQ
4797
+
* - TIME
4798
+
* - EMS
4799
+
* - EXS
4800
+
* - ANGLE
4801
+
*/
4802
+
if (isDigit(c)) {
4803
+
token = this.numberToken(c, startLine, startCol);
4804
+
} else
4805
+
4806
+
/*
4807
+
* Potential tokens:
4808
+
* - S
4809
+
*/
4810
+
if (isWhitespace(c)) {
4811
+
token = this.whitespaceToken(c, startLine, startCol);
4812
+
} else
4813
+
4814
+
/*
4815
+
* Potential tokens:
4816
+
* - IDENT
4817
+
*/
4818
+
if (isIdentStart(c)) {
4819
+
token = this.identOrFunctionToken(c, startLine, startCol);
4820
+
} else {
4821
+
/*
4822
+
* Potential tokens:
4823
+
* - CHAR
4824
+
* - PLUS
4825
+
*/
4826
+
token = this.charToken(c, startLine, startCol);
4827
+
}
4828
+
4829
+
}
4830
+
4831
+
//make sure this token is wanted
4832
+
//TODO: check channel
4833
+
break;
4834
+
}
4835
+
4836
+
if (!token && c === null) {
4837
+
token = this.createToken(Tokens.EOF, null, startLine, startCol);
4838
+
}
4839
+
4840
+
return token;
4841
+
},
4842
+
4843
+
//-------------------------------------------------------------------------
4844
+
// Methods to create tokens
4845
+
//-------------------------------------------------------------------------
4846
+
4847
+
/**
4848
+
* Produces a token based on available data and the current
4849
+
* reader position information. This method is called by other
4850
+
* private methods to create tokens and is never called directly.
4851
+
* @param {int} tt The token type.
4852
+
* @param {String} value The text value of the token.
4853
+
* @param {int} startLine The beginning line for the character.
4854
+
* @param {int} startCol The beginning column for the character.
4855
+
* @param {Object} options (Optional) Specifies a channel property
4856
+
* to indicate that a different channel should be scanned
4857
+
* and/or a hide property indicating that the token should
4858
+
* be hidden.
4859
+
* @return {Object} A token object.
4860
+
* @method createToken
4861
+
*/
4862
+
createToken: function(tt, value, startLine, startCol, options) {
4863
+
var reader = this._reader;
4864
+
options = options || {};
4865
+
4866
+
return {
4867
+
value: value,
4868
+
type: tt,
4869
+
channel: options.channel,
4870
+
endChar: options.endChar,
4871
+
hide: options.hide || false,
4872
+
startLine: startLine,
4873
+
startCol: startCol,
4874
+
endLine: reader.getLine(),
4875
+
endCol: reader.getCol()
4876
+
};
4877
+
},
4878
+
4879
+
//-------------------------------------------------------------------------
4880
+
// Methods to create specific tokens
4881
+
//-------------------------------------------------------------------------
4882
+
4883
+
/**
4884
+
* Produces a token for any at-rule. If the at-rule is unknown, then
4885
+
* the token is for a single "@" character.
4886
+
* @param {String} first The first character for the token.
4887
+
* @param {int} startLine The beginning line for the character.
4888
+
* @param {int} startCol The beginning column for the character.
4889
+
* @return {Object} A token object.
4890
+
* @method atRuleToken
4891
+
*/
4892
+
atRuleToken: function(first, startLine, startCol) {
4893
+
var rule = first,
4894
+
reader = this._reader,
4895
+
tt = Tokens.CHAR,
4896
+
ident;
4897
+
4898
+
/*
4899
+
* First, mark where we are. There are only four @ rules,
4900
+
* so anything else is really just an invalid token.
4901
+
* Basically, if this doesn't match one of the known @
4902
+
* rules, just return '@' as an unknown token and allow
4903
+
* parsing to continue after that point.
4904
+
*/
4905
+
reader.mark();
4906
+
4907
+
//try to find the at-keyword
4908
+
ident = this.readName();
4909
+
rule = first + ident;
4910
+
tt = Tokens.type(rule.toLowerCase());
4911
+
4912
+
//if it's not valid, use the first character only and reset the reader
4913
+
if (tt === Tokens.CHAR || tt === Tokens.UNKNOWN) {
4914
+
if (rule.length > 1) {
4915
+
tt = Tokens.UNKNOWN_SYM;
4916
+
} else {
4917
+
tt = Tokens.CHAR;
4918
+
rule = first;
4919
+
reader.reset();
4920
+
}
4921
+
}
4922
+
4923
+
return this.createToken(tt, rule, startLine, startCol);
4924
+
},
4925
+
4926
+
/**
4927
+
* Produces a character token based on the given character
4928
+
* and location in the stream. If there's a special (non-standard)
4929
+
* token name, this is used; otherwise CHAR is used.
4930
+
* @param {String} c The character for the token.
4931
+
* @param {int} startLine The beginning line for the character.
4932
+
* @param {int} startCol The beginning column for the character.
4933
+
* @return {Object} A token object.
4934
+
* @method charToken
4935
+
*/
4936
+
charToken: function(c, startLine, startCol) {
4937
+
var tt = Tokens.type(c);
4938
+
var opts = {};
4939
+
4940
+
if (tt === -1) {
4941
+
tt = Tokens.CHAR;
4942
+
} else {
4943
+
opts.endChar = Tokens[tt].endChar;
4944
+
}
4945
+
4946
+
return this.createToken(tt, c, startLine, startCol, opts);
4947
+
},
4948
+
4949
+
/**
4950
+
* Produces a character token based on the given character
4951
+
* and location in the stream. If there's a special (non-standard)
4952
+
* token name, this is used; otherwise CHAR is used.
4953
+
* @param {String} first The first character for the token.
4954
+
* @param {int} startLine The beginning line for the character.
4955
+
* @param {int} startCol The beginning column for the character.
4956
+
* @return {Object} A token object.
4957
+
* @method commentToken
4958
+
*/
4959
+
commentToken: function(first, startLine, startCol) {
4960
+
var comment = this.readComment(first);
4961
+
4962
+
return this.createToken(Tokens.COMMENT, comment, startLine, startCol);
4963
+
},
4964
+
4965
+
/**
4966
+
* Produces a comparison token based on the given character
4967
+
* and location in the stream. The next character must be
4968
+
* read and is already known to be an equals sign.
4969
+
* @param {String} c The character for the token.
4970
+
* @param {int} startLine The beginning line for the character.
4971
+
* @param {int} startCol The beginning column for the character.
4972
+
* @return {Object} A token object.
4973
+
* @method comparisonToken
4974
+
*/
4975
+
comparisonToken: function(c, startLine, startCol) {
4976
+
var reader = this._reader,
4977
+
comparison = c + reader.read(),
4978
+
tt = Tokens.type(comparison) || Tokens.CHAR;
4979
+
4980
+
return this.createToken(tt, comparison, startLine, startCol);
4981
+
},
4982
+
4983
+
/**
4984
+
* Produces a hash token based on the specified information. The
4985
+
* first character provided is the pound sign (#) and then this
4986
+
* method reads a name afterward.
4987
+
* @param {String} first The first character (#) in the hash name.
4988
+
* @param {int} startLine The beginning line for the character.
4989
+
* @param {int} startCol The beginning column for the character.
4990
+
* @return {Object} A token object.
4991
+
* @method hashToken
4992
+
*/
4993
+
hashToken: function(first, startLine, startCol) {
4994
+
var name = this.readName(first);
4995
+
4996
+
return this.createToken(Tokens.HASH, name, startLine, startCol);
4997
+
},
4998
+
4999
+
/**
5000
+
* Produces a CDO or CHAR token based on the specified information. The
5001
+
* first character is provided and the rest is read by the function to determine
5002
+
* the correct token to create.
5003
+
* @param {String} first The first character in the token.
5004
+
* @param {int} startLine The beginning line for the character.
5005
+
* @param {int} startCol The beginning column for the character.
5006
+
* @return {Object} A token object.
5007
+
* @method htmlCommentStartToken
5008
+
*/
5009
+
htmlCommentStartToken: function(first, startLine, startCol) {
5010
+
var reader = this._reader,
5011
+
text = first;
5012
+
5013
+
reader.mark();
5014
+
text += reader.readCount(3);
5015
+
5016
+
if (text === "<!--") {
5017
+
return this.createToken(Tokens.CDO, text, startLine, startCol);
5018
+
} else {
5019
+
reader.reset();
5020
+
return this.charToken(first, startLine, startCol);
5021
+
}
5022
+
},
5023
+
5024
+
/**
5025
+
* Produces a CDC or CHAR token based on the specified information. The
5026
+
* first character is provided and the rest is read by the function to determine
5027
+
* the correct token to create.
5028
+
* @param {String} first The first character in the token.
5029
+
* @param {int} startLine The beginning line for the character.
5030
+
* @param {int} startCol The beginning column for the character.
5031
+
* @return {Object} A token object.
5032
+
* @method htmlCommentEndToken
5033
+
*/
5034
+
htmlCommentEndToken: function(first, startLine, startCol) {
5035
+
var reader = this._reader,
5036
+
text = first;
5037
+
5038
+
reader.mark();
5039
+
text += reader.readCount(2);
5040
+
5041
+
if (text === "-->") {
5042
+
return this.createToken(Tokens.CDC, text, startLine, startCol);
5043
+
} else {
5044
+
reader.reset();
5045
+
return this.charToken(first, startLine, startCol);
5046
+
}
5047
+
},
5048
+
5049
+
/**
5050
+
* Produces an IDENT or FUNCTION token based on the specified information. The
5051
+
* first character is provided and the rest is read by the function to determine
5052
+
* the correct token to create.
5053
+
* @param {String} first The first character in the identifier.
5054
+
* @param {int} startLine The beginning line for the character.
5055
+
* @param {int} startCol The beginning column for the character.
5056
+
* @return {Object} A token object.
5057
+
* @method identOrFunctionToken
5058
+
*/
5059
+
identOrFunctionToken: function(first, startLine, startCol) {
5060
+
var reader = this._reader,
5061
+
ident = this.readName(first),
5062
+
tt = Tokens.IDENT,
5063
+
uriFns = ["url(", "url-prefix(", "domain("],
5064
+
uri;
5065
+
5066
+
//if there's a left paren immediately after, it's a URI or function
5067
+
if (reader.peek() === "(") {
5068
+
ident += reader.read();
5069
+
if (uriFns.indexOf(ident.toLowerCase()) > -1) {
5070
+
reader.mark();
5071
+
uri = this.readURI(ident);
5072
+
if (uri === null) {
5073
+
//didn't find a valid URL or there's no closing paren
5074
+
reader.reset();
5075
+
tt = Tokens.FUNCTION;
5076
+
} else {
5077
+
tt = Tokens.URI;
5078
+
ident = uri;
5079
+
}
5080
+
} else {
5081
+
tt = Tokens.FUNCTION;
5082
+
}
5083
+
} else if (reader.peek() === ":") { //might be an IE function
5084
+
5085
+
//IE-specific functions always being with progid:
5086
+
if (ident.toLowerCase() === "progid") {
5087
+
ident += reader.readTo("(");
5088
+
tt = Tokens.IE_FUNCTION;
5089
+
}
5090
+
}
5091
+
5092
+
return this.createToken(tt, ident, startLine, startCol);
5093
+
},
5094
+
5095
+
/**
5096
+
* Produces an IMPORTANT_SYM or CHAR token based on the specified information. The
5097
+
* first character is provided and the rest is read by the function to determine
5098
+
* the correct token to create.
5099
+
* @param {String} first The first character in the token.
5100
+
* @param {int} startLine The beginning line for the character.
5101
+
* @param {int} startCol The beginning column for the character.
5102
+
* @return {Object} A token object.
5103
+
* @method importantToken
5104
+
*/
5105
+
importantToken: function(first, startLine, startCol) {
5106
+
var reader = this._reader,
5107
+
important = first,
5108
+
tt = Tokens.CHAR,
5109
+
temp,
5110
+
c;
5111
+
5112
+
reader.mark();
5113
+
c = reader.read();
5114
+
5115
+
while (c) {
5116
+
5117
+
//there can be a comment in here
5118
+
if (c === "/") {
5119
+
5120
+
//if the next character isn't a star, then this isn't a valid !important token
5121
+
if (reader.peek() !== "*") {
5122
+
break;
5123
+
} else {
5124
+
temp = this.readComment(c);
5125
+
if (temp === "") { //broken!
5126
+
break;
5127
+
}
5128
+
}
5129
+
} else if (isWhitespace(c)) {
5130
+
important += c + this.readWhitespace();
5131
+
} else if (/i/i.test(c)) {
5132
+
temp = reader.readCount(8);
5133
+
if (/mportant/i.test(temp)) {
5134
+
important += c + temp;
5135
+
tt = Tokens.IMPORTANT_SYM;
5136
+
5137
+
}
5138
+
break; //we're done
5139
+
} else {
5140
+
break;
5141
+
}
5142
+
5143
+
c = reader.read();
5144
+
}
5145
+
5146
+
if (tt === Tokens.CHAR) {
5147
+
reader.reset();
5148
+
return this.charToken(first, startLine, startCol);
5149
+
} else {
5150
+
return this.createToken(tt, important, startLine, startCol);
5151
+
}
5152
+
5153
+
5154
+
},
5155
+
5156
+
/**
5157
+
* Produces a NOT or CHAR token based on the specified information. The
5158
+
* first character is provided and the rest is read by the function to determine
5159
+
* the correct token to create.
5160
+
* @param {String} first The first character in the token.
5161
+
* @param {int} startLine The beginning line for the character.
5162
+
* @param {int} startCol The beginning column for the character.
5163
+
* @return {Object} A token object.
5164
+
* @method notToken
5165
+
*/
5166
+
notToken: function(first, startLine, startCol) {
5167
+
var reader = this._reader,
5168
+
text = first;
5169
+
5170
+
reader.mark();
5171
+
text += reader.readCount(4);
5172
+
5173
+
if (text.toLowerCase() === ":not(") {
5174
+
return this.createToken(Tokens.NOT, text, startLine, startCol);
5175
+
} else {
5176
+
reader.reset();
5177
+
return this.charToken(first, startLine, startCol);
5178
+
}
5179
+
},
5180
+
5181
+
/**
5182
+
* Produces a number token based on the given character
5183
+
* and location in the stream. This may return a token of
5184
+
* NUMBER, EMS, EXS, LENGTH, ANGLE, TIME, FREQ, DIMENSION,
5185
+
* or PERCENTAGE.
5186
+
* @param {String} first The first character for the token.
5187
+
* @param {int} startLine The beginning line for the character.
5188
+
* @param {int} startCol The beginning column for the character.
5189
+
* @return {Object} A token object.
5190
+
* @method numberToken
5191
+
*/
5192
+
numberToken: function(first, startLine, startCol) {
5193
+
var reader = this._reader,
5194
+
value = this.readNumber(first),
5195
+
ident,
5196
+
tt = Tokens.NUMBER,
5197
+
c = reader.peek();
5198
+
5199
+
if (isIdentStart(c)) {
5200
+
ident = this.readName(reader.read());
5201
+
value += ident;
5202
+
5203
+
if (/^em$|^ex$|^px$|^gd$|^rem$|^vw$|^vh$|^vmax$|^vmin$|^ch$|^cm$|^mm$|^in$|^pt$|^pc$/i.test(ident)) {
5204
+
tt = Tokens.LENGTH;
5205
+
} else if (/^deg|^rad$|^grad$|^turn$/i.test(ident)) {
5206
+
tt = Tokens.ANGLE;
5207
+
} else if (/^ms$|^s$/i.test(ident)) {
5208
+
tt = Tokens.TIME;
5209
+
} else if (/^hz$|^khz$/i.test(ident)) {
5210
+
tt = Tokens.FREQ;
5211
+
} else if (/^dpi$|^dpcm$/i.test(ident)) {
5212
+
tt = Tokens.RESOLUTION;
5213
+
} else {
5214
+
tt = Tokens.DIMENSION;
5215
+
}
5216
+
5217
+
} else if (c === "%") {
5218
+
value += reader.read();
5219
+
tt = Tokens.PERCENTAGE;
5220
+
}
5221
+
5222
+
return this.createToken(tt, value, startLine, startCol);
5223
+
},
5224
+
5225
+
/**
5226
+
* Produces a string token based on the given character
5227
+
* and location in the stream. Since strings may be indicated
5228
+
* by single or double quotes, a failure to match starting
5229
+
* and ending quotes results in an INVALID token being generated.
5230
+
* The first character in the string is passed in and then
5231
+
* the rest are read up to and including the final quotation mark.
5232
+
* @param {String} first The first character in the string.
5233
+
* @param {int} startLine The beginning line for the character.
5234
+
* @param {int} startCol The beginning column for the character.
5235
+
* @return {Object} A token object.
5236
+
* @method stringToken
5237
+
*/
5238
+
stringToken: function(first, startLine, startCol) {
5239
+
var delim = first,
5240
+
string = first,
5241
+
reader = this._reader,
5242
+
tt = Tokens.STRING,
5243
+
c = reader.read(),
5244
+
i;
5245
+
5246
+
while (c) {
5247
+
string += c;
5248
+
5249
+
if (c === "\\") {
5250
+
c = reader.read();
5251
+
if (c === null) {
5252
+
break; // premature EOF after backslash
5253
+
} else if (/[^\r\n\f0-9a-f]/i.test(c)) {
5254
+
// single-character escape
5255
+
string += c;
5256
+
} else {
5257
+
// read up to six hex digits
5258
+
for (i=0; isHexDigit(c) && i<6; i++) {
5259
+
string += c;
5260
+
c = reader.read();
5261
+
}
5262
+
// swallow trailing newline or space
5263
+
if (c === "\r" && reader.peek() === "\n") {
5264
+
string += c;
5265
+
c = reader.read();
5266
+
}
5267
+
if (isWhitespace(c)) {
5268
+
string += c;
5269
+
} else {
5270
+
// This character is null or not part of the escape;
5271
+
// jump back to the top to process it.
5272
+
continue;
5273
+
}
5274
+
}
5275
+
} else if (c === delim) {
5276
+
break; // delimiter found.
5277
+
} else if (isNewLine(reader.peek())) {
5278
+
// newline without an escapement: it's an invalid string
5279
+
tt = Tokens.INVALID;
5280
+
break;
5281
+
}
5282
+
c = reader.read();
5283
+
}
5284
+
5285
+
//if c is null, that means we're out of input and the string was never closed
5286
+
if (c === null) {
5287
+
tt = Tokens.INVALID;
5288
+
}
5289
+
5290
+
return this.createToken(tt, string, startLine, startCol);
5291
+
},
5292
+
5293
+
unicodeRangeToken: function(first, startLine, startCol) {
5294
+
var reader = this._reader,
5295
+
value = first,
5296
+
temp,
5297
+
tt = Tokens.CHAR;
5298
+
5299
+
//then it should be a unicode range
5300
+
if (reader.peek() === "+") {
5301
+
reader.mark();
5302
+
value += reader.read();
5303
+
value += this.readUnicodeRangePart(true);
5304
+
5305
+
//ensure there's an actual unicode range here
5306
+
if (value.length === 2) {
5307
+
reader.reset();
5308
+
} else {
5309
+
5310
+
tt = Tokens.UNICODE_RANGE;
5311
+
5312
+
//if there's a ? in the first part, there can't be a second part
5313
+
if (value.indexOf("?") === -1) {
5314
+
5315
+
if (reader.peek() === "-") {
5316
+
reader.mark();
5317
+
temp = reader.read();
5318
+
temp += this.readUnicodeRangePart(false);
5319
+
5320
+
//if there's not another value, back up and just take the first
5321
+
if (temp.length === 1) {
5322
+
reader.reset();
5323
+
} else {
5324
+
value += temp;
5325
+
}
5326
+
}
5327
+
5328
+
}
5329
+
}
5330
+
}
5331
+
5332
+
return this.createToken(tt, value, startLine, startCol);
5333
+
},
5334
+
5335
+
/**
5336
+
* Produces a S token based on the specified information. Since whitespace
5337
+
* may have multiple characters, this consumes all whitespace characters
5338
+
* into a single token.
5339
+
* @param {String} first The first character in the token.
5340
+
* @param {int} startLine The beginning line for the character.
5341
+
* @param {int} startCol The beginning column for the character.
5342
+
* @return {Object} A token object.
5343
+
* @method whitespaceToken
5344
+
*/
5345
+
whitespaceToken: function(first, startLine, startCol) {
5346
+
var value = first + this.readWhitespace();
5347
+
return this.createToken(Tokens.S, value, startLine, startCol);
5348
+
},
5349
+
5350
+
5351
+
//-------------------------------------------------------------------------
5352
+
// Methods to read values from the string stream
5353
+
//-------------------------------------------------------------------------
5354
+
5355
+
readUnicodeRangePart: function(allowQuestionMark) {
5356
+
var reader = this._reader,
5357
+
part = "",
5358
+
c = reader.peek();
5359
+
5360
+
//first read hex digits
5361
+
while (isHexDigit(c) && part.length < 6) {
5362
+
reader.read();
5363
+
part += c;
5364
+
c = reader.peek();
5365
+
}
5366
+
5367
+
//then read question marks if allowed
5368
+
if (allowQuestionMark) {
5369
+
while (c === "?" && part.length < 6) {
5370
+
reader.read();
5371
+
part += c;
5372
+
c = reader.peek();
5373
+
}
5374
+
}
5375
+
5376
+
//there can't be any other characters after this point
5377
+
5378
+
return part;
5379
+
},
5380
+
5381
+
readWhitespace: function() {
5382
+
var reader = this._reader,
5383
+
whitespace = "",
5384
+
c = reader.peek();
5385
+
5386
+
while (isWhitespace(c)) {
5387
+
reader.read();
5388
+
whitespace += c;
5389
+
c = reader.peek();
5390
+
}
5391
+
5392
+
return whitespace;
5393
+
},
5394
+
readNumber: function(first) {
5395
+
var reader = this._reader,
5396
+
number = first,
5397
+
hasDot = (first === "."),
5398
+
c = reader.peek();
5399
+
5400
+
5401
+
while (c) {
5402
+
if (isDigit(c)) {
5403
+
number += reader.read();
5404
+
} else if (c === ".") {
5405
+
if (hasDot) {
5406
+
break;
5407
+
} else {
5408
+
hasDot = true;
5409
+
number += reader.read();
5410
+
}
5411
+
} else {
5412
+
break;
5413
+
}
5414
+
5415
+
c = reader.peek();
5416
+
}
5417
+
5418
+
return number;
5419
+
},
5420
+
5421
+
// returns null w/o resetting reader if string is invalid.
5422
+
readString: function() {
5423
+
var token = this.stringToken(this._reader.read(), 0, 0);
5424
+
return token.type === Tokens.INVALID ? null : token.value;
5425
+
},
5426
+
5427
+
// returns null w/o resetting reader if URI is invalid.
5428
+
readURI: function(first) {
5429
+
var reader = this._reader,
5430
+
uri = first,
5431
+
inner = "",
5432
+
c = reader.peek();
5433
+
5434
+
//skip whitespace before
5435
+
while (c && isWhitespace(c)) {
5436
+
reader.read();
5437
+
c = reader.peek();
5438
+
}
5439
+
5440
+
//it's a string
5441
+
if (c === "'" || c === "\"") {
5442
+
inner = this.readString();
5443
+
if (inner !== null) {
5444
+
inner = PropertyValuePart.parseString(inner);
5445
+
}
5446
+
} else {
5447
+
inner = this.readUnquotedURL();
5448
+
}
5449
+
5450
+
c = reader.peek();
5451
+
5452
+
//skip whitespace after
5453
+
while (c && isWhitespace(c)) {
5454
+
reader.read();
5455
+
c = reader.peek();
5456
+
}
5457
+
5458
+
//if there was no inner value or the next character isn't closing paren, it's not a URI
5459
+
if (inner === null || c !== ")") {
5460
+
uri = null;
5461
+
} else {
5462
+
// Ensure argument to URL is always double-quoted
5463
+
// (This simplifies later processing in PropertyValuePart.)
5464
+
uri += PropertyValuePart.serializeString(inner) + reader.read();
5465
+
}
5466
+
5467
+
return uri;
5468
+
},
5469
+
// This method never fails, although it may return an empty string.
5470
+
readUnquotedURL: function(first) {
5471
+
var reader = this._reader,
5472
+
url = first || "",
5473
+
c;
5474
+
5475
+
for (c = reader.peek(); c; c = reader.peek()) {
5476
+
// Note that the grammar at
5477
+
// https://www.w3.org/TR/CSS2/grammar.html#scanner
5478
+
// incorrectly includes the backslash character in the
5479
+
// `url` production, although it is correctly omitted in
5480
+
// the `baduri1` production.
5481
+
if (nonascii.test(c) || /^[\-!#$%&*-\[\]-~]$/.test(c)) {
5482
+
url += c;
5483
+
reader.read();
5484
+
} else if (c === "\\") {
5485
+
if (/^[^\r\n\f]$/.test(reader.peek(2))) {
5486
+
url += this.readEscape(reader.read(), true);
5487
+
} else {
5488
+
break; // bad escape sequence.
5489
+
}
5490
+
} else {
5491
+
break; // bad character
5492
+
}
5493
+
}
5494
+
5495
+
return url;
5496
+
},
5497
+
5498
+
readName: function(first) {
5499
+
var reader = this._reader,
5500
+
ident = first || "",
5501
+
c;
5502
+
5503
+
for (c = reader.peek(); c; c = reader.peek()) {
5504
+
if (c === "\\") {
5505
+
if (/^[^\r\n\f]$/.test(reader.peek(2))) {
5506
+
ident += this.readEscape(reader.read(), true);
5507
+
} else {
5508
+
// Bad escape sequence.
5509
+
break;
5510
+
}
5511
+
} else if (isNameChar(c)) {
5512
+
ident += reader.read();
5513
+
} else {
5514
+
break;
5515
+
}
5516
+
}
5517
+
5518
+
return ident;
5519
+
},
5520
+
5521
+
readEscape: function(first, unescape) {
5522
+
var reader = this._reader,
5523
+
cssEscape = first || "",
5524
+
i = 0,
5525
+
c = reader.peek();
5526
+
5527
+
if (isHexDigit(c)) {
5528
+
do {
5529
+
cssEscape += reader.read();
5530
+
c = reader.peek();
5531
+
} while (c && isHexDigit(c) && ++i < 6);
5532
+
}
5533
+
5534
+
if (cssEscape.length === 1) {
5535
+
if (/^[^\r\n\f0-9a-f]$/.test(c)) {
5536
+
reader.read();
5537
+
if (unescape) {
5538
+
return c;
5539
+
}
5540
+
} else {
5541
+
// We should never get here (readName won't call readEscape
5542
+
// if the escape sequence is bad).
5543
+
throw new Error("Bad escape sequence.");
5544
+
}
5545
+
} else if (c === "\r") {
5546
+
reader.read();
5547
+
if (reader.peek() === "\n") {
5548
+
c += reader.read();
5549
+
}
5550
+
} else if (/^[ \t\n\f]$/.test(c)) {
5551
+
reader.read();
5552
+
} else {
5553
+
c = "";
5554
+
}
5555
+
5556
+
if (unescape) {
5557
+
var cp = parseInt(cssEscape.slice(first.length), 16);
5558
+
return String.fromCodePoint ? String.fromCodePoint(cp) :
5559
+
String.fromCharCode(cp);
5560
+
}
5561
+
return cssEscape + c;
5562
+
},
5563
+
5564
+
readComment: function(first) {
5565
+
var reader = this._reader,
5566
+
comment = first || "",
5567
+
c = reader.read();
5568
+
5569
+
if (c === "*") {
5570
+
while (c) {
5571
+
comment += c;
5572
+
5573
+
//look for end of comment
5574
+
if (comment.length > 2 && c === "*" && reader.peek() === "/") {
5575
+
comment += reader.read();
5576
+
break;
5577
+
}
5578
+
5579
+
c = reader.read();
5580
+
}
5581
+
5582
+
return comment;
5583
+
} else {
5584
+
return "";
5585
+
}
5586
+
5587
+
}
5588
+
});
5589
+
5590
+
},{"../util/TokenStreamBase":27,"./PropertyValuePart":11,"./Tokens":18}],18:[function(require,module,exports){
5591
+
"use strict";
5592
+
5593
+
var Tokens = module.exports = [
5594
+
5595
+
/*
5596
+
* The following token names are defined in CSS3 Grammar: https://www.w3.org/TR/css3-syntax/#lexical
5597
+
*/
5598
+
5599
+
// HTML-style comments
5600
+
{ name: "CDO" },
5601
+
{ name: "CDC" },
5602
+
5603
+
// ignorables
5604
+
{ name: "S", whitespace: true/*, channel: "ws"*/ },
5605
+
{ name: "COMMENT", comment: true, hide: true, channel: "comment" },
5606
+
5607
+
// attribute equality
5608
+
{ name: "INCLUDES", text: "~=" },
5609
+
{ name: "DASHMATCH", text: "|=" },
5610
+
{ name: "PREFIXMATCH", text: "^=" },
5611
+
{ name: "SUFFIXMATCH", text: "$=" },
5612
+
{ name: "SUBSTRINGMATCH", text: "*=" },
5613
+
5614
+
// identifier types
5615
+
{ name: "STRING" },
5616
+
{ name: "IDENT" },
5617
+
{ name: "HASH" },
5618
+
5619
+
// at-keywords
5620
+
{ name: "IMPORT_SYM", text: "@import" },
5621
+
{ name: "PAGE_SYM", text: "@page" },
5622
+
{ name: "MEDIA_SYM", text: "@media" },
5623
+
{ name: "FONT_FACE_SYM", text: "@font-face" },
5624
+
{ name: "CHARSET_SYM", text: "@charset" },
5625
+
{ name: "NAMESPACE_SYM", text: "@namespace" },
5626
+
{ name: "SUPPORTS_SYM", text: "@supports" },
5627
+
{ name: "VIEWPORT_SYM", text: ["@viewport", "@-ms-viewport", "@-o-viewport"] },
5628
+
{ name: "DOCUMENT_SYM", text: ["@document", "@-moz-document"] },
5629
+
{ name: "UNKNOWN_SYM" },
5630
+
//{ name: "ATKEYWORD"},
5631
+
5632
+
// CSS3 animations
5633
+
{ name: "KEYFRAMES_SYM", text: [ "@keyframes", "@-webkit-keyframes", "@-moz-keyframes", "@-o-keyframes" ] },
5634
+
5635
+
// important symbol
5636
+
{ name: "IMPORTANT_SYM" },
5637
+
5638
+
// measurements
5639
+
{ name: "LENGTH" },
5640
+
{ name: "ANGLE" },
5641
+
{ name: "TIME" },
5642
+
{ name: "FREQ" },
5643
+
{ name: "DIMENSION" },
5644
+
{ name: "PERCENTAGE" },
5645
+
{ name: "NUMBER" },
5646
+
5647
+
// functions
5648
+
{ name: "URI" },
5649
+
{ name: "FUNCTION" },
5650
+
5651
+
// Unicode ranges
5652
+
{ name: "UNICODE_RANGE" },
5653
+
5654
+
/*
5655
+
* The following token names are defined in CSS3 Selectors: https://www.w3.org/TR/css3-selectors/#selector-syntax
5656
+
*/
5657
+
5658
+
// invalid string
5659
+
{ name: "INVALID" },
5660
+
5661
+
// combinators
5662
+
{ name: "PLUS", text: "+" },
5663
+
{ name: "GREATER", text: ">" },
5664
+
{ name: "COMMA", text: "," },
5665
+
{ name: "TILDE", text: "~" },
5666
+
5667
+
// modifier
5668
+
{ name: "NOT" },
5669
+
5670
+
/*
5671
+
* Defined in CSS3 Paged Media
5672
+
*/
5673
+
{ name: "TOPLEFTCORNER_SYM", text: "@top-left-corner" },
5674
+
{ name: "TOPLEFT_SYM", text: "@top-left" },
5675
+
{ name: "TOPCENTER_SYM", text: "@top-center" },
5676
+
{ name: "TOPRIGHT_SYM", text: "@top-right" },
5677
+
{ name: "TOPRIGHTCORNER_SYM", text: "@top-right-corner" },
5678
+
{ name: "BOTTOMLEFTCORNER_SYM", text: "@bottom-left-corner" },
5679
+
{ name: "BOTTOMLEFT_SYM", text: "@bottom-left" },
5680
+
{ name: "BOTTOMCENTER_SYM", text: "@bottom-center" },
5681
+
{ name: "BOTTOMRIGHT_SYM", text: "@bottom-right" },
5682
+
{ name: "BOTTOMRIGHTCORNER_SYM", text: "@bottom-right-corner" },
5683
+
{ name: "LEFTTOP_SYM", text: "@left-top" },
5684
+
{ name: "LEFTMIDDLE_SYM", text: "@left-middle" },
5685
+
{ name: "LEFTBOTTOM_SYM", text: "@left-bottom" },
5686
+
{ name: "RIGHTTOP_SYM", text: "@right-top" },
5687
+
{ name: "RIGHTMIDDLE_SYM", text: "@right-middle" },
5688
+
{ name: "RIGHTBOTTOM_SYM", text: "@right-bottom" },
5689
+
5690
+
/*
5691
+
* The following token names are defined in CSS3 Media Queries: https://www.w3.org/TR/css3-mediaqueries/#syntax
5692
+
*/
5693
+
/*{ name: "MEDIA_ONLY", state: "media"},
5694
+
{ name: "MEDIA_NOT", state: "media"},
5695
+
{ name: "MEDIA_AND", state: "media"},*/
5696
+
{ name: "RESOLUTION", state: "media" },
5697
+
5698
+
/*
5699
+
* The following token names are not defined in any CSS specification but are used by the lexer.
5700
+
*/
5701
+
5702
+
// not a real token, but useful for stupid IE filters
5703
+
{ name: "IE_FUNCTION" },
5704
+
5705
+
// part of CSS3 grammar but not the Flex code
5706
+
{ name: "CHAR" },
5707
+
5708
+
// TODO: Needed?
5709
+
// Not defined as tokens, but might as well be
5710
+
{
5711
+
name: "PIPE",
5712
+
text: "|"
5713
+
},
5714
+
{
5715
+
name: "SLASH",
5716
+
text: "/"
5717
+
},
5718
+
{
5719
+
name: "MINUS",
5720
+
text: "-"
5721
+
},
5722
+
{
5723
+
name: "STAR",
5724
+
text: "*"
5725
+
},
5726
+
5727
+
{
5728
+
name: "LBRACE",
5729
+
endChar: "}",
5730
+
text: "{"
5731
+
},
5732
+
{
5733
+
name: "RBRACE",
5734
+
text: "}"
5735
+
},
5736
+
{
5737
+
name: "LBRACKET",
5738
+
endChar: "]",
5739
+
text: "["
5740
+
},
5741
+
{
5742
+
name: "RBRACKET",
5743
+
text: "]"
5744
+
},
5745
+
{
5746
+
name: "EQUALS",
5747
+
text: "="
5748
+
},
5749
+
{
5750
+
name: "COLON",
5751
+
text: ":"
5752
+
},
5753
+
{
5754
+
name: "SEMICOLON",
5755
+
text: ";"
5756
+
},
5757
+
{
5758
+
name: "LPAREN",
5759
+
endChar: ")",
5760
+
text: "("
5761
+
},
5762
+
{
5763
+
name: "RPAREN",
5764
+
text: ")"
5765
+
},
5766
+
{
5767
+
name: "DOT",
5768
+
text: "."
5769
+
}
5770
+
];
5771
+
5772
+
(function() {
5773
+
var nameMap = [],
5774
+
typeMap = Object.create(null);
5775
+
5776
+
Tokens.UNKNOWN = -1;
5777
+
Tokens.unshift({ name:"EOF" });
5778
+
for (var i=0, len = Tokens.length; i < len; i++) {
5779
+
nameMap.push(Tokens[i].name);
5780
+
Tokens[Tokens[i].name] = i;
5781
+
if (Tokens[i].text) {
5782
+
if (Tokens[i].text instanceof Array) {
5783
+
for (var j=0; j < Tokens[i].text.length; j++) {
5784
+
typeMap[Tokens[i].text[j]] = i;
5785
+
}
5786
+
} else {
5787
+
typeMap[Tokens[i].text] = i;
5788
+
}
5789
+
}
5790
+
}
5791
+
5792
+
Tokens.name = function(tt) {
5793
+
return nameMap[tt];
5794
+
};
5795
+
5796
+
Tokens.type = function(c) {
5797
+
return typeMap[c] || -1;
5798
+
};
5799
+
})();
5800
+
5801
+
},{}],19:[function(require,module,exports){
5802
+
"use strict";
5803
+
5804
+
/* exported Validation */
5805
+
5806
+
var Matcher = require("./Matcher");
5807
+
var Properties = require("./Properties");
5808
+
var ValidationTypes = require("./ValidationTypes");
5809
+
var ValidationError = require("./ValidationError");
5810
+
var PropertyValueIterator = require("./PropertyValueIterator");
5811
+
5812
+
var Validation = module.exports = {
5813
+
5814
+
validate: function(property, value) {
5815
+
5816
+
//normalize name
5817
+
var name = property.toString().toLowerCase(),
5818
+
expression = new PropertyValueIterator(value),
5819
+
spec = Properties[name],
5820
+
part;
5821
+
5822
+
if (!spec) {
5823
+
if (name.indexOf("-") !== 0) { //vendor prefixed are ok
5824
+
throw new ValidationError("Unknown property '" + property + "'.", property.line, property.col);
5825
+
}
5826
+
} else if (typeof spec !== "number") {
5827
+
5828
+
// All properties accept some CSS-wide values.
5829
+
// https://drafts.csswg.org/css-values-3/#common-keywords
5830
+
if (ValidationTypes.isAny(expression, "inherit | initial | unset")) {
5831
+
if (expression.hasNext()) {
5832
+
part = expression.next();
5833
+
throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5834
+
}
5835
+
return;
5836
+
}
5837
+
5838
+
// Property-specific validation.
5839
+
this.singleProperty(spec, expression);
5840
+
5841
+
}
5842
+
5843
+
},
5844
+
5845
+
singleProperty: function(types, expression) {
5846
+
5847
+
var result = false,
5848
+
value = expression.value,
5849
+
part;
5850
+
5851
+
result = Matcher.parse(types).match(expression);
5852
+
5853
+
if (!result) {
5854
+
if (expression.hasNext() && !expression.isFirst()) {
5855
+
part = expression.peek();
5856
+
throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5857
+
} else {
5858
+
throw new ValidationError("Expected (" + ValidationTypes.describe(types) + ") but found '" + value + "'.", value.line, value.col);
5859
+
}
5860
+
} else if (expression.hasNext()) {
5861
+
part = expression.next();
5862
+
throw new ValidationError("Expected end of value but found '" + part + "'.", part.line, part.col);
5863
+
}
5864
+
5865
+
}
5866
+
5867
+
};
5868
+
5869
+
},{"./Matcher":3,"./Properties":7,"./PropertyValueIterator":10,"./ValidationError":20,"./ValidationTypes":21}],20:[function(require,module,exports){
5870
+
"use strict";
5871
+
5872
+
module.exports = ValidationError;
5873
+
5874
+
/**
5875
+
* Type to use when a validation error occurs.
5876
+
* @class ValidationError
5877
+
* @namespace parserlib.util
5878
+
* @constructor
5879
+
* @param {String} message The error message.
5880
+
* @param {int} line The line at which the error occurred.
5881
+
* @param {int} col The column at which the error occurred.
5882
+
*/
5883
+
function ValidationError(message, line, col) {
5884
+
5885
+
/**
5886
+
* The column at which the error occurred.
5887
+
* @type int
5888
+
* @property col
5889
+
*/
5890
+
this.col = col;
5891
+
5892
+
/**
5893
+
* The line at which the error occurred.
5894
+
* @type int
5895
+
* @property line
5896
+
*/
5897
+
this.line = line;
5898
+
5899
+
/**
5900
+
* The text representation of the unit.
5901
+
* @type String
5902
+
* @property text
5903
+
*/
5904
+
this.message = message;
5905
+
5906
+
}
5907
+
5908
+
//inherit from Error
5909
+
ValidationError.prototype = new Error();
5910
+
5911
+
},{}],21:[function(require,module,exports){
5912
+
"use strict";
5913
+
5914
+
var ValidationTypes = module.exports;
5915
+
5916
+
var Matcher = require("./Matcher");
5917
+
5918
+
function copy(to, from) {
5919
+
Object.keys(from).forEach(function(prop) {
5920
+
to[prop] = from[prop];
5921
+
});
5922
+
}
5923
+
copy(ValidationTypes, {
5924
+
5925
+
isLiteral: function (part, literals) {
5926
+
var text = part.text.toString().toLowerCase(),
5927
+
args = literals.split(" | "),
5928
+
i, len, found = false;
5929
+
5930
+
for (i=0, len=args.length; i < len && !found; i++) {
5931
+
if (args[i].charAt(0) === "<") {
5932
+
found = this.simple[args[i]](part);
5933
+
} else if (args[i].slice(-2) === "()") {
5934
+
found = (part.type === "function" &&
5935
+
part.name === args[i].slice(0, -2));
5936
+
} else if (text === args[i].toLowerCase()) {
5937
+
found = true;
5938
+
}
5939
+
}
5940
+
5941
+
return found;
5942
+
},
5943
+
5944
+
isSimple: function(type) {
5945
+
return Boolean(this.simple[type]);
5946
+
},
5947
+
5948
+
isComplex: function(type) {
5949
+
return Boolean(this.complex[type]);
5950
+
},
5951
+
5952
+
describe: function(type) {
5953
+
if (this.complex[type] instanceof Matcher) {
5954
+
return this.complex[type].toString(0);
5955
+
}
5956
+
return type;
5957
+
},
5958
+
5959
+
/**
5960
+
* Determines if the next part(s) of the given expression
5961
+
* are any of the given types.
5962
+
*/
5963
+
isAny: function (expression, types) {
5964
+
var args = types.split(" | "),
5965
+
i, len, found = false;
5966
+
5967
+
for (i=0, len=args.length; i < len && !found && expression.hasNext(); i++) {
5968
+
found = this.isType(expression, args[i]);
5969
+
}
5970
+
5971
+
return found;
5972
+
},
5973
+
5974
+
/**
5975
+
* Determines if the next part(s) of the given expression
5976
+
* are one of a group.
5977
+
*/
5978
+
isAnyOfGroup: function(expression, types) {
5979
+
var args = types.split(" || "),
5980
+
i, len, found = false;
5981
+
5982
+
for (i=0, len=args.length; i < len && !found; i++) {
5983
+
found = this.isType(expression, args[i]);
5984
+
}
5985
+
5986
+
return found ? args[i-1] : false;
5987
+
},
5988
+
5989
+
/**
5990
+
* Determines if the next part(s) of the given expression
5991
+
* are of a given type.
5992
+
*/
5993
+
isType: function (expression, type) {
5994
+
var part = expression.peek(),
5995
+
result = false;
5996
+
5997
+
if (type.charAt(0) !== "<") {
5998
+
result = this.isLiteral(part, type);
5999
+
if (result) {
6000
+
expression.next();
6001
+
}
6002
+
} else if (this.simple[type]) {
6003
+
result = this.simple[type](part);
6004
+
if (result) {
6005
+
expression.next();
6006
+
}
6007
+
} else if (this.complex[type] instanceof Matcher) {
6008
+
result = this.complex[type].match(expression);
6009
+
} else {
6010
+
result = this.complex[type](expression);
6011
+
}
6012
+
6013
+
return result;
6014
+
},
6015
+
6016
+
6017
+
simple: {
6018
+
__proto__: null,
6019
+
6020
+
"<absolute-size>":
6021
+
"xx-small | x-small | small | medium | large | x-large | xx-large",
6022
+
6023
+
"<animateable-feature>":
6024
+
"scroll-position | contents | <animateable-feature-name>",
6025
+
6026
+
"<animateable-feature-name>": function(part) {
6027
+
return this["<ident>"](part) &&
6028
+
!/^(unset|initial|inherit|will-change|auto|scroll-position|contents)$/i.test(part);
6029
+
},
6030
+
6031
+
"<angle>": function(part) {
6032
+
return part.type === "angle";
6033
+
},
6034
+
6035
+
"<attachment>": "scroll | fixed | local",
6036
+
6037
+
"<attr>": "attr()",
6038
+
6039
+
// inset() = inset( <shape-arg>{1,4} [round <border-radius>]? )
6040
+
// circle() = circle( [<shape-radius>]? [at <position>]? )
6041
+
// ellipse() = ellipse( [<shape-radius>{2}]? [at <position>]? )
6042
+
// polygon() = polygon( [<fill-rule>,]? [<shape-arg> <shape-arg>]# )
6043
+
"<basic-shape>": "inset() | circle() | ellipse() | polygon()",
6044
+
6045
+
"<bg-image>": "<image> | <gradient> | none",
6046
+
6047
+
"<border-style>":
6048
+
"none | hidden | dotted | dashed | solid | double | groove | " +
6049
+
"ridge | inset | outset",
6050
+
6051
+
"<border-width>": "<length> | thin | medium | thick",
6052
+
6053
+
"<box>": "padding-box | border-box | content-box",
6054
+
6055
+
"<clip-source>": "<uri>",
6056
+
6057
+
"<color>": function(part) {
6058
+
return part.type === "color" || String(part) === "transparent" || String(part) === "currentColor";
6059
+
},
6060
+
6061
+
// The SVG <color> spec doesn't include "currentColor" or "transparent" as a color.
6062
+
"<color-svg>": function(part) {
6063
+
return part.type === "color";
6064
+
},
6065
+
6066
+
"<content>": "content()",
6067
+
6068
+
// https://www.w3.org/TR/css3-sizing/#width-height-keywords
6069
+
"<content-sizing>":
6070
+
"fill-available | -moz-available | -webkit-fill-available | " +
6071
+
"max-content | -moz-max-content | -webkit-max-content | " +
6072
+
"min-content | -moz-min-content | -webkit-min-content | " +
6073
+
"fit-content | -moz-fit-content | -webkit-fit-content",
6074
+
6075
+
"<feature-tag-value>": function(part) {
6076
+
return part.type === "function" && /^[A-Z0-9]{4}$/i.test(part);
6077
+
},
6078
+
6079
+
// custom() isn't actually in the spec
6080
+
"<filter-function>":
6081
+
"blur() | brightness() | contrast() | custom() | " +
6082
+
"drop-shadow() | grayscale() | hue-rotate() | invert() | " +
6083
+
"opacity() | saturate() | sepia()",
6084
+
6085
+
"<flex-basis>": "<width>",
6086
+
6087
+
"<flex-direction>": "row | row-reverse | column | column-reverse",
6088
+
6089
+
"<flex-grow>": "<number>",
6090
+
6091
+
"<flex-shrink>": "<number>",
6092
+
6093
+
"<flex-wrap>": "nowrap | wrap | wrap-reverse",
6094
+
6095
+
"<font-size>":
6096
+
"<absolute-size> | <relative-size> | <length> | <percentage>",
6097
+
6098
+
"<font-stretch>":
6099
+
"normal | ultra-condensed | extra-condensed | condensed | " +
6100
+
"semi-condensed | semi-expanded | expanded | extra-expanded | " +
6101
+
"ultra-expanded",
6102
+
6103
+
"<font-style>": "normal | italic | oblique",
6104
+
6105
+
"<font-variant-caps>":
6106
+
"small-caps | all-small-caps | petite-caps | all-petite-caps | " +
6107
+
"unicase | titling-caps",
6108
+
6109
+
"<font-variant-css21>": "normal | small-caps",
6110
+
6111
+
"<font-weight>":
6112
+
"normal | bold | bolder | lighter | " +
6113
+
"100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900",
6114
+
6115
+
"<generic-family>":
6116
+
"serif | sans-serif | cursive | fantasy | monospace",
6117
+
6118
+
"<geometry-box>": "<shape-box> | fill-box | stroke-box | view-box",
6119
+
6120
+
"<glyph-angle>": function(part) {
6121
+
return part.type === "angle" && part.units === "deg";
6122
+
},
6123
+
6124
+
"<gradient>": function(part) {
6125
+
return part.type === "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?(?:repeating\-)?(?:radial\-|linear\-)?gradient/i.test(part);
6126
+
},
6127
+
6128
+
"<icccolor>":
6129
+
"cielab() | cielch() | cielchab() | " +
6130
+
"icc-color() | icc-named-color()",
6131
+
6132
+
//any identifier
6133
+
"<ident>": function(part) {
6134
+
return part.type === "identifier" || part.wasIdent;
6135
+
},
6136
+
6137
+
"<ident-not-generic-family>": function(part) {
6138
+
return this["<ident>"](part) && !this["<generic-family>"](part);
6139
+
},
6140
+
6141
+
"<image>": "<uri>",
6142
+
6143
+
"<integer>": function(part) {
6144
+
return part.type === "integer";
6145
+
},
6146
+
6147
+
"<length>": function(part) {
6148
+
if (part.type === "function" && /^(?:\-(?:ms|moz|o|webkit)\-)?calc/i.test(part)) {
6149
+
return true;
6150
+
} else {
6151
+
return part.type === "length" || part.type === "number" || part.type === "integer" || String(part) === "0";
6152
+
}
6153
+
},
6154
+
6155
+
"<line>": function(part) {
6156
+
return part.type === "integer";
6157
+
},
6158
+
6159
+
"<line-height>": "<number> | <length> | <percentage> | normal",
6160
+
6161
+
"<margin-width>": "<length> | <percentage> | auto",
6162
+
6163
+
"<miterlimit>": function(part) {
6164
+
return this["<number>"](part) && part.value >= 1;
6165
+
},
6166
+
6167
+
"<nonnegative-length-or-percentage>": function(part) {
6168
+
return (this["<length>"](part) || this["<percentage>"](part)) &&
6169
+
(String(part) === "0" || part.type === "function" || (part.value) >= 0);
6170
+
},
6171
+
6172
+
"<nonnegative-number-or-percentage>": function(part) {
6173
+
return (this["<number>"](part) || this["<percentage>"](part)) &&
6174
+
(String(part) === "0" || part.type === "function" || (part.value) >= 0);
6175
+
},
6176
+
6177
+
"<number>": function(part) {
6178
+
return part.type === "number" || this["<integer>"](part);
6179
+
},
6180
+
6181
+
"<opacity-value>": function(part) {
6182
+
return this["<number>"](part) && part.value >= 0 && part.value <= 1;
6183
+
},
6184
+
6185
+
"<padding-width>": "<nonnegative-length-or-percentage>",
6186
+
6187
+
"<percentage>": function(part) {
6188
+
return part.type === "percentage" || String(part) === "0";
6189
+
},
6190
+
6191
+
"<relative-size>": "smaller | larger",
6192
+
6193
+
"<shape>": "rect() | inset-rect()",
6194
+
6195
+
"<shape-box>": "<box> | margin-box",
6196
+
6197
+
"<single-animation-direction>":
6198
+
"normal | reverse | alternate | alternate-reverse",
6199
+
6200
+
"<single-animation-name>": function(part) {
6201
+
return this["<ident>"](part) &&
6202
+
/^-?[a-z_][-a-z0-9_]+$/i.test(part) &&
6203
+
!/^(none|unset|initial|inherit)$/i.test(part);
6204
+
},
6205
+
6206
+
"<string>": function(part) {
6207
+
return part.type === "string";
6208
+
},
6209
+
6210
+
"<time>": function(part) {
6211
+
return part.type === "time";
6212
+
},
6213
+
6214
+
"<uri>": function(part) {
6215
+
return part.type === "uri";
6216
+
},
6217
+
6218
+
"<width>": "<margin-width>"
6219
+
},
6220
+
6221
+
complex: {
6222
+
__proto__: null,
6223
+
6224
+
"<azimuth>":
6225
+
"<angle>" +
6226
+
" | " +
6227
+
"[ [ left-side | far-left | left | center-left | center | " +
6228
+
"center-right | right | far-right | right-side ] || behind ]" +
6229
+
" | "+
6230
+
"leftwards | rightwards",
6231
+
6232
+
"<bg-position>": "<position>#",
6233
+
6234
+
"<bg-size>":
6235
+
"[ <length> | <percentage> | auto ]{1,2} | cover | contain",
6236
+
6237
+
"<border-image-slice>":
6238
+
// [<number> | <percentage>]{1,4} && fill?
6239
+
// *but* fill can appear between any of the numbers
6240
+
Matcher.many([true /* first element is required */],
6241
+
Matcher.cast("<nonnegative-number-or-percentage>"),
6242
+
Matcher.cast("<nonnegative-number-or-percentage>"),
6243
+
Matcher.cast("<nonnegative-number-or-percentage>"),
6244
+
Matcher.cast("<nonnegative-number-or-percentage>"),
6245
+
"fill"),
6246
+
6247
+
"<border-radius>":
6248
+
"<nonnegative-length-or-percentage>{1,4} " +
6249
+
"[ / <nonnegative-length-or-percentage>{1,4} ]?",
6250
+
6251
+
"<box-shadow>": "none | <shadow>#",
6252
+
6253
+
"<clip-path>": "<basic-shape> || <geometry-box>",
6254
+
6255
+
"<dasharray>":
6256
+
// "list of comma and/or white space separated <length>s and
6257
+
// <percentage>s". There is a non-negative constraint.
6258
+
Matcher.cast("<nonnegative-length-or-percentage>")
6259
+
.braces(1, Infinity, "#", Matcher.cast(",").question()),
6260
+
6261
+
"<family-name>":
6262
+
// <string> | <IDENT>+
6263
+
"<string> | <ident-not-generic-family> <ident>*",
6264
+
6265
+
"<filter-function-list>": "[ <filter-function> | <uri> ]+",
6266
+
6267
+
// https://www.w3.org/TR/2014/WD-css-flexbox-1-20140325/#flex-property
6268
+
"<flex>":
6269
+
"none | [ <flex-grow> <flex-shrink>? || <flex-basis> ]",
6270
+
6271
+
"<font-family>": "[ <generic-family> | <family-name> ]#",
6272
+
6273
+
"<font-shorthand>":
6274
+
"[ <font-style> || <font-variant-css21> || " +
6275
+
"<font-weight> || <font-stretch> ]? <font-size> " +
6276
+
"[ / <line-height> ]? <font-family>",
6277
+
6278
+
"<font-variant-alternates>":
6279
+
// stylistic(<feature-value-name>)
6280
+
"stylistic() || " +
6281
+
"historical-forms || " +
6282
+
// styleset(<feature-value-name> #)
6283
+
"styleset() || " +
6284
+
// character-variant(<feature-value-name> #)
6285
+
"character-variant() || " +
6286
+
// swash(<feature-value-name>)
6287
+
"swash() || " +
6288
+
// ornaments(<feature-value-name>)
6289
+
"ornaments() || " +
6290
+
// annotation(<feature-value-name>)
6291
+
"annotation()",
6292
+
6293
+
"<font-variant-ligatures>":
6294
+
// <common-lig-values>
6295
+
"[ common-ligatures | no-common-ligatures ] || " +
6296
+
// <discretionary-lig-values>
6297
+
"[ discretionary-ligatures | no-discretionary-ligatures ] || " +
6298
+
// <historical-lig-values>
6299
+
"[ historical-ligatures | no-historical-ligatures ] || " +
6300
+
// <contextual-alt-values>
6301
+
"[ contextual | no-contextual ]",
6302
+
6303
+
"<font-variant-numeric>":
6304
+
// <numeric-figure-values>
6305
+
"[ lining-nums | oldstyle-nums ] || " +
6306
+
// <numeric-spacing-values>
6307
+
"[ proportional-nums | tabular-nums ] || " +
6308
+
// <numeric-fraction-values>
6309
+
"[ diagonal-fractions | stacked-fractions ] || " +
6310
+
"ordinal || slashed-zero",
6311
+
6312
+
"<font-variant-east-asian>":
6313
+
// <east-asian-variant-values>
6314
+
"[ jis78 | jis83 | jis90 | jis04 | simplified | traditional ] || " +
6315
+
// <east-asian-width-values>
6316
+
"[ full-width | proportional-width ] || " +
6317
+
"ruby",
6318
+
6319
+
// Note that <color> here is "as defined in the SVG spec", which
6320
+
// is more restrictive that the <color> defined in the CSS spec.
6321
+
// none | currentColor | <color> [<icccolor>]? |
6322
+
// <funciri> [ none | currentColor | <color> [<icccolor>]? ]?
6323
+
"<paint>": "<paint-basic> | <uri> <paint-basic>?",
6324
+
6325
+
// Helper definition for <paint> above.
6326
+
"<paint-basic>": "none | currentColor | <color-svg> <icccolor>?",
6327
+
6328
+
"<position>":
6329
+
// Because our `alt` combinator is ordered, we need to test these
6330
+
// in order from longest possible match to shortest.
6331
+
"[ center | [ left | right ] [ <percentage> | <length> ]? ] && " +
6332
+
"[ center | [ top | bottom ] [ <percentage> | <length> ]? ]" +
6333
+
" | " +
6334
+
"[ left | center | right | <percentage> | <length> ] " +
6335
+
"[ top | center | bottom | <percentage> | <length> ]" +
6336
+
" | " +
6337
+
"[ left | center | right | top | bottom | <percentage> | <length> ]",
6338
+
6339
+
"<repeat-style>":
6340
+
"repeat-x | repeat-y | [ repeat | space | round | no-repeat ]{1,2}",
6341
+
6342
+
"<shadow>":
6343
+
//inset? && [ <length>{2,4} && <color>? ]
6344
+
Matcher.many([true /* length is required */],
6345
+
Matcher.cast("<length>").braces(2, 4), "inset", "<color>"),
6346
+
6347
+
"<text-decoration-color>":
6348
+
"<color>",
6349
+
6350
+
"<text-decoration-line>":
6351
+
"none | [ underline || overline || line-through || blink ]",
6352
+
6353
+
"<text-decoration-style>":
6354
+
"solid | double | dotted | dashed | wavy",
6355
+
6356
+
"<will-change>":
6357
+
"auto | <animateable-feature>#",
6358
+
6359
+
"<x-one-radius>":
6360
+
//[ <length> | <percentage> ] [ <length> | <percentage> ]?
6361
+
"[ <length> | <percentage> ]{1,2}"
6362
+
}
6363
+
});
6364
+
6365
+
Object.keys(ValidationTypes.simple).forEach(function(nt) {
6366
+
var rule = ValidationTypes.simple[nt];
6367
+
if (typeof rule === "string") {
6368
+
ValidationTypes.simple[nt] = function(part) {
6369
+
return ValidationTypes.isLiteral(part, rule);
6370
+
};
6371
+
}
6372
+
});
6373
+
6374
+
Object.keys(ValidationTypes.complex).forEach(function(nt) {
6375
+
var rule = ValidationTypes.complex[nt];
6376
+
if (typeof rule === "string") {
6377
+
ValidationTypes.complex[nt] = Matcher.parse(rule);
6378
+
}
6379
+
});
6380
+
6381
+
// Because this is defined relative to other complex validation types,
6382
+
// we need to define it *after* the rest of the types are initialized.
6383
+
ValidationTypes.complex["<font-variant>"] =
6384
+
Matcher.oror({ expand: "<font-variant-ligatures>" },
6385
+
{ expand: "<font-variant-alternates>" },
6386
+
"<font-variant-caps>",
6387
+
{ expand: "<font-variant-numeric>" },
6388
+
{ expand: "<font-variant-east-asian>" });
6389
+
6390
+
},{"./Matcher":3}],22:[function(require,module,exports){
6391
+
"use strict";
6392
+
6393
+
module.exports = {
6394
+
Colors : require("./Colors"),
6395
+
Combinator : require("./Combinator"),
6396
+
Parser : require("./Parser"),
6397
+
PropertyName : require("./PropertyName"),
6398
+
PropertyValue : require("./PropertyValue"),
6399
+
PropertyValuePart : require("./PropertyValuePart"),
6400
+
Matcher : require("./Matcher"),
6401
+
MediaFeature : require("./MediaFeature"),
6402
+
MediaQuery : require("./MediaQuery"),
6403
+
Selector : require("./Selector"),
6404
+
SelectorPart : require("./SelectorPart"),
6405
+
SelectorSubPart : require("./SelectorSubPart"),
6406
+
Specificity : require("./Specificity"),
6407
+
TokenStream : require("./TokenStream"),
6408
+
Tokens : require("./Tokens"),
6409
+
ValidationError : require("./ValidationError")
6410
+
};
6411
+
6412
+
},{"./Colors":1,"./Combinator":2,"./Matcher":3,"./MediaFeature":4,"./MediaQuery":5,"./Parser":6,"./PropertyName":8,"./PropertyValue":9,"./PropertyValuePart":11,"./Selector":13,"./SelectorPart":14,"./SelectorSubPart":15,"./Specificity":16,"./TokenStream":17,"./Tokens":18,"./ValidationError":20}],23:[function(require,module,exports){
6413
+
"use strict";
6414
+
6415
+
module.exports = EventTarget;
6416
+
6417
+
/**
6418
+
* A generic base to inherit from for any object
6419
+
* that needs event handling.
6420
+
* @class EventTarget
6421
+
* @constructor
6422
+
*/
6423
+
function EventTarget() {
6424
+
6425
+
/**
6426
+
* The array of listeners for various events.
6427
+
* @type Object
6428
+
* @property _listeners
6429
+
* @private
6430
+
*/
6431
+
this._listeners = Object.create(null);
6432
+
}
6433
+
6434
+
EventTarget.prototype = {
6435
+
6436
+
//restore constructor
6437
+
constructor: EventTarget,
6438
+
6439
+
/**
6440
+
* Adds a listener for a given event type.
6441
+
* @param {String} type The type of event to add a listener for.
6442
+
* @param {Function} listener The function to call when the event occurs.
6443
+
* @return {void}
6444
+
* @method addListener
6445
+
*/
6446
+
addListener: function(type, listener) {
6447
+
if (!this._listeners[type]) {
6448
+
this._listeners[type] = [];
6449
+
}
6450
+
6451
+
this._listeners[type].push(listener);
6452
+
},
6453
+
6454
+
/**
6455
+
* Fires an event based on the passed-in object.
6456
+
* @param {Object|String} event An object with at least a 'type' attribute
6457
+
* or a string indicating the event name.
6458
+
* @return {void}
6459
+
* @method fire
6460
+
*/
6461
+
fire: function(event) {
6462
+
if (typeof event === "string") {
6463
+
event = { type: event };
6464
+
}
6465
+
if (typeof event.target !== "undefined") {
6466
+
event.target = this;
6467
+
}
6468
+
6469
+
if (typeof event.type === "undefined") {
6470
+
throw new Error("Event object missing 'type' property.");
6471
+
}
6472
+
6473
+
if (this._listeners[event.type]) {
6474
+
6475
+
//create a copy of the array and use that so listeners can't chane
6476
+
var listeners = this._listeners[event.type].concat();
6477
+
for (var i=0, len=listeners.length; i < len; i++) {
6478
+
listeners[i].call(this, event);
6479
+
}
6480
+
}
6481
+
},
6482
+
6483
+
/**
6484
+
* Removes a listener for a given event type.
6485
+
* @param {String} type The type of event to remove a listener from.
6486
+
* @param {Function} listener The function to remove from the event.
6487
+
* @return {void}
6488
+
* @method removeListener
6489
+
*/
6490
+
removeListener: function(type, listener) {
6491
+
if (this._listeners[type]) {
6492
+
var listeners = this._listeners[type];
6493
+
for (var i=0, len=listeners.length; i < len; i++) {
6494
+
if (listeners[i] === listener) {
6495
+
listeners.splice(i, 1);
6496
+
break;
6497
+
}
6498
+
}
6499
+
6500
+
6501
+
}
6502
+
}
6503
+
};
6504
+
6505
+
},{}],24:[function(require,module,exports){
6506
+
"use strict";
6507
+
6508
+
module.exports = StringReader;
6509
+
6510
+
/**
6511
+
* Convenient way to read through strings.
6512
+
* @namespace parserlib.util
6513
+
* @class StringReader
6514
+
* @constructor
6515
+
* @param {String} text The text to read.
6516
+
*/
6517
+
function StringReader(text) {
6518
+
6519
+
/**
6520
+
* The input text with line endings normalized.
6521
+
* @property _input
6522
+
* @type String
6523
+
* @private
6524
+
*/
6525
+
this._input = text.replace(/(\r\n?|\n)/g, "\n");
6526
+
6527
+
6528
+
/**
6529
+
* The row for the character to be read next.
6530
+
* @property _line
6531
+
* @type int
6532
+
* @private
6533
+
*/
6534
+
this._line = 1;
6535
+
6536
+
6537
+
/**
6538
+
* The column for the character to be read next.
6539
+
* @property _col
6540
+
* @type int
6541
+
* @private
6542
+
*/
6543
+
this._col = 1;
6544
+
6545
+
/**
6546
+
* The index of the character in the input to be read next.
6547
+
* @property _cursor
6548
+
* @type int
6549
+
* @private
6550
+
*/
6551
+
this._cursor = 0;
6552
+
}
6553
+
6554
+
StringReader.prototype = {
6555
+
6556
+
// restore constructor
6557
+
constructor: StringReader,
6558
+
6559
+
//-------------------------------------------------------------------------
6560
+
// Position info
6561
+
//-------------------------------------------------------------------------
6562
+
6563
+
/**
6564
+
* Returns the column of the character to be read next.
6565
+
* @return {int} The column of the character to be read next.
6566
+
* @method getCol
6567
+
*/
6568
+
getCol: function() {
6569
+
return this._col;
6570
+
},
6571
+
6572
+
/**
6573
+
* Returns the row of the character to be read next.
6574
+
* @return {int} The row of the character to be read next.
6575
+
* @method getLine
6576
+
*/
6577
+
getLine: function() {
6578
+
return this._line;
6579
+
},
6580
+
6581
+
/**
6582
+
* Determines if you're at the end of the input.
6583
+
* @return {Boolean} True if there's no more input, false otherwise.
6584
+
* @method eof
6585
+
*/
6586
+
eof: function() {
6587
+
return this._cursor === this._input.length;
6588
+
},
6589
+
6590
+
//-------------------------------------------------------------------------
6591
+
// Basic reading
6592
+
//-------------------------------------------------------------------------
6593
+
6594
+
/**
6595
+
* Reads the next character without advancing the cursor.
6596
+
* @param {int} count How many characters to look ahead (default is 1).
6597
+
* @return {String} The next character or null if there is no next character.
6598
+
* @method peek
6599
+
*/
6600
+
peek: function(count) {
6601
+
var c = null;
6602
+
count = typeof count === "undefined" ? 1 : count;
6603
+
6604
+
// if we're not at the end of the input...
6605
+
if (this._cursor < this._input.length) {
6606
+
6607
+
// get character and increment cursor and column
6608
+
c = this._input.charAt(this._cursor + count - 1);
6609
+
}
6610
+
6611
+
return c;
6612
+
},
6613
+
6614
+
/**
6615
+
* Reads the next character from the input and adjusts the row and column
6616
+
* accordingly.
6617
+
* @return {String} The next character or null if there is no next character.
6618
+
* @method read
6619
+
*/
6620
+
read: function() {
6621
+
var c = null;
6622
+
6623
+
// if we're not at the end of the input...
6624
+
if (this._cursor < this._input.length) {
6625
+
6626
+
// if the last character was a newline, increment row count
6627
+
// and reset column count
6628
+
if (this._input.charAt(this._cursor) === "\n") {
6629
+
this._line++;
6630
+
this._col=1;
6631
+
} else {
6632
+
this._col++;
6633
+
}
6634
+
6635
+
// get character and increment cursor and column
6636
+
c = this._input.charAt(this._cursor++);
6637
+
}
6638
+
6639
+
return c;
6640
+
},
6641
+
6642
+
//-------------------------------------------------------------------------
6643
+
// Misc
6644
+
//-------------------------------------------------------------------------
6645
+
6646
+
/**
6647
+
* Saves the current location so it can be returned to later.
6648
+
* @method mark
6649
+
* @return {void}
6650
+
*/
6651
+
mark: function() {
6652
+
this._bookmark = {
6653
+
cursor: this._cursor,
6654
+
line: this._line,
6655
+
col: this._col
6656
+
};
6657
+
},
6658
+
6659
+
reset: function() {
6660
+
if (this._bookmark) {
6661
+
this._cursor = this._bookmark.cursor;
6662
+
this._line = this._bookmark.line;
6663
+
this._col = this._bookmark.col;
6664
+
delete this._bookmark;
6665
+
}
6666
+
},
6667
+
6668
+
//-------------------------------------------------------------------------
6669
+
// Advanced reading
6670
+
//-------------------------------------------------------------------------
6671
+
6672
+
/**
6673
+
* Reads up to and including the given string. Throws an error if that
6674
+
* string is not found.
6675
+
* @param {String} pattern The string to read.
6676
+
* @return {String} The string when it is found.
6677
+
* @throws Error when the string pattern is not found.
6678
+
* @method readTo
6679
+
*/
6680
+
readTo: function(pattern) {
6681
+
6682
+
var buffer = "",
6683
+
c;
6684
+
6685
+
/*
6686
+
* First, buffer must be the same length as the pattern.
6687
+
* Then, buffer must end with the pattern or else reach the
6688
+
* end of the input.
6689
+
*/
6690
+
while (buffer.length < pattern.length || buffer.lastIndexOf(pattern) !== buffer.length - pattern.length) {
6691
+
c = this.read();
6692
+
if (c) {
6693
+
buffer += c;
6694
+
} else {
6695
+
throw new Error("Expected \"" + pattern + "\" at line " + this._line + ", col " + this._col + ".");
6696
+
}
6697
+
}
6698
+
6699
+
return buffer;
6700
+
6701
+
},
6702
+
6703
+
/**
6704
+
* Reads characters while each character causes the given
6705
+
* filter function to return true. The function is passed
6706
+
* in each character and either returns true to continue
6707
+
* reading or false to stop.
6708
+
* @param {Function} filter The function to read on each character.
6709
+
* @return {String} The string made up of all characters that passed the
6710
+
* filter check.
6711
+
* @method readWhile
6712
+
*/
6713
+
readWhile: function(filter) {
6714
+
6715
+
var buffer = "",
6716
+
c = this.peek();
6717
+
6718
+
while (c !== null && filter(c)) {
6719
+
buffer += this.read();
6720
+
c = this.peek();
6721
+
}
6722
+
6723
+
return buffer;
6724
+
6725
+
},
6726
+
6727
+
/**
6728
+
* Reads characters that match either text or a regular expression and
6729
+
* returns those characters. If a match is found, the row and column
6730
+
* are adjusted; if no match is found, the reader's state is unchanged.
6731
+
* reading or false to stop.
6732
+
* @param {String|RegExp} matcher If a string, then the literal string
6733
+
* value is searched for. If a regular expression, then any string
6734
+
* matching the pattern is search for.
6735
+
* @return {String} The string made up of all characters that matched or
6736
+
* null if there was no match.
6737
+
* @method readMatch
6738
+
*/
6739
+
readMatch: function(matcher) {
6740
+
6741
+
var source = this._input.substring(this._cursor),
6742
+
value = null;
6743
+
6744
+
// if it's a string, just do a straight match
6745
+
if (typeof matcher === "string") {
6746
+
if (source.slice(0, matcher.length) === matcher) {
6747
+
value = this.readCount(matcher.length);
6748
+
}
6749
+
} else if (matcher instanceof RegExp) {
6750
+
if (matcher.test(source)) {
6751
+
value = this.readCount(RegExp.lastMatch.length);
6752
+
}
6753
+
}
6754
+
6755
+
return value;
6756
+
},
6757
+
6758
+
6759
+
/**
6760
+
* Reads a given number of characters. If the end of the input is reached,
6761
+
* it reads only the remaining characters and does not throw an error.
6762
+
* @param {int} count The number of characters to read.
6763
+
* @return {String} The string made up the read characters.
6764
+
* @method readCount
6765
+
*/
6766
+
readCount: function(count) {
6767
+
var buffer = "";
6768
+
6769
+
while (count--) {
6770
+
buffer += this.read();
6771
+
}
6772
+
6773
+
return buffer;
6774
+
}
6775
+
6776
+
};
6777
+
6778
+
},{}],25:[function(require,module,exports){
6779
+
"use strict";
6780
+
6781
+
module.exports = SyntaxError;
6782
+
6783
+
/**
6784
+
* Type to use when a syntax error occurs.
6785
+
* @class SyntaxError
6786
+
* @namespace parserlib.util
6787
+
* @constructor
6788
+
* @param {String} message The error message.
6789
+
* @param {int} line The line at which the error occurred.
6790
+
* @param {int} col The column at which the error occurred.
6791
+
*/
6792
+
function SyntaxError(message, line, col) {
6793
+
Error.call(this);
6794
+
this.name = this.constructor.name;
6795
+
6796
+
/**
6797
+
* The column at which the error occurred.
6798
+
* @type int
6799
+
* @property col
6800
+
*/
6801
+
this.col = col;
6802
+
6803
+
/**
6804
+
* The line at which the error occurred.
6805
+
* @type int
6806
+
* @property line
6807
+
*/
6808
+
this.line = line;
6809
+
6810
+
/**
6811
+
* The text representation of the unit.
6812
+
* @type String
6813
+
* @property text
6814
+
*/
6815
+
this.message = message;
6816
+
6817
+
}
6818
+
6819
+
//inherit from Error
6820
+
SyntaxError.prototype = Object.create(Error.prototype); // jshint ignore:line
6821
+
SyntaxError.prototype.constructor = SyntaxError; // jshint ignore:line
6822
+
6823
+
},{}],26:[function(require,module,exports){
6824
+
"use strict";
6825
+
6826
+
module.exports = SyntaxUnit;
6827
+
6828
+
/**
6829
+
* Base type to represent a single syntactic unit.
6830
+
* @class SyntaxUnit
6831
+
* @namespace parserlib.util
6832
+
* @constructor
6833
+
* @param {String} text The text of the unit.
6834
+
* @param {int} line The line of text on which the unit resides.
6835
+
* @param {int} col The column of text on which the unit resides.
6836
+
*/
6837
+
function SyntaxUnit(text, line, col, type) {
6838
+
6839
+
6840
+
/**
6841
+
* The column of text on which the unit resides.
6842
+
* @type int
6843
+
* @property col
6844
+
*/
6845
+
this.col = col;
6846
+
6847
+
/**
6848
+
* The line of text on which the unit resides.
6849
+
* @type int
6850
+
* @property line
6851
+
*/
6852
+
this.line = line;
6853
+
6854
+
/**
6855
+
* The text representation of the unit.
6856
+
* @type String
6857
+
* @property text
6858
+
*/
6859
+
this.text = text;
6860
+
6861
+
/**
6862
+
* The type of syntax unit.
6863
+
* @type int
6864
+
* @property type
6865
+
*/
6866
+
this.type = type;
6867
+
}
6868
+
6869
+
/**
6870
+
* Create a new syntax unit based solely on the given token.
6871
+
* Convenience method for creating a new syntax unit when
6872
+
* it represents a single token instead of multiple.
6873
+
* @param {Object} token The token object to represent.
6874
+
* @return {parserlib.util.SyntaxUnit} The object representing the token.
6875
+
* @static
6876
+
* @method fromToken
6877
+
*/
6878
+
SyntaxUnit.fromToken = function(token) {
6879
+
return new SyntaxUnit(token.value, token.startLine, token.startCol);
6880
+
};
6881
+
6882
+
SyntaxUnit.prototype = {
6883
+
6884
+
//restore constructor
6885
+
constructor: SyntaxUnit,
6886
+
6887
+
/**
6888
+
* Returns the text representation of the unit.
6889
+
* @return {String} The text representation of the unit.
6890
+
* @method valueOf
6891
+
*/
6892
+
valueOf: function() {
6893
+
return this.toString();
6894
+
},
6895
+
6896
+
/**
6897
+
* Returns the text representation of the unit.
6898
+
* @return {String} The text representation of the unit.
6899
+
* @method toString
6900
+
*/
6901
+
toString: function() {
6902
+
return this.text;
6903
+
}
6904
+
6905
+
};
6906
+
6907
+
},{}],27:[function(require,module,exports){
6908
+
"use strict";
6909
+
6910
+
module.exports = TokenStreamBase;
6911
+
6912
+
var StringReader = require("./StringReader");
6913
+
var SyntaxError = require("./SyntaxError");
6914
+
6915
+
/**
6916
+
* Generic TokenStream providing base functionality.
6917
+
* @class TokenStreamBase
6918
+
* @namespace parserlib.util
6919
+
* @constructor
6920
+
* @param {String|StringReader} input The text to tokenize or a reader from
6921
+
* which to read the input.
6922
+
*/
6923
+
function TokenStreamBase(input, tokenData) {
6924
+
6925
+
/**
6926
+
* The string reader for easy access to the text.
6927
+
* @type StringReader
6928
+
* @property _reader
6929
+
* @private
6930
+
*/
6931
+
this._reader = new StringReader(input ? input.toString() : "");
6932
+
6933
+
/**
6934
+
* Token object for the last consumed token.
6935
+
* @type Token
6936
+
* @property _token
6937
+
* @private
6938
+
*/
6939
+
this._token = null;
6940
+
6941
+
/**
6942
+
* The array of token information.
6943
+
* @type Array
6944
+
* @property _tokenData
6945
+
* @private
6946
+
*/
6947
+
this._tokenData = tokenData;
6948
+
6949
+
/**
6950
+
* Lookahead token buffer.
6951
+
* @type Array
6952
+
* @property _lt
6953
+
* @private
6954
+
*/
6955
+
this._lt = [];
6956
+
6957
+
/**
6958
+
* Lookahead token buffer index.
6959
+
* @type int
6960
+
* @property _ltIndex
6961
+
* @private
6962
+
*/
6963
+
this._ltIndex = 0;
6964
+
6965
+
this._ltIndexCache = [];
6966
+
}
6967
+
6968
+
/**
6969
+
* Accepts an array of token information and outputs
6970
+
* an array of token data containing key-value mappings
6971
+
* and matching functions that the TokenStream needs.
6972
+
* @param {Array} tokens An array of token descriptors.
6973
+
* @return {Array} An array of processed token data.
6974
+
* @method createTokenData
6975
+
* @static
6976
+
*/
6977
+
TokenStreamBase.createTokenData = function(tokens) {
6978
+
6979
+
var nameMap = [],
6980
+
typeMap = Object.create(null),
6981
+
tokenData = tokens.concat([]),
6982
+
i = 0,
6983
+
len = tokenData.length+1;
6984
+
6985
+
tokenData.UNKNOWN = -1;
6986
+
tokenData.unshift({ name:"EOF" });
6987
+
6988
+
for (; i < len; i++) {
6989
+
nameMap.push(tokenData[i].name);
6990
+
tokenData[tokenData[i].name] = i;
6991
+
if (tokenData[i].text) {
6992
+
typeMap[tokenData[i].text] = i;
6993
+
}
6994
+
}
6995
+
6996
+
tokenData.name = function(tt) {
6997
+
return nameMap[tt];
6998
+
};
6999
+
7000
+
tokenData.type = function(c) {
7001
+
return typeMap[c];
7002
+
};
7003
+
7004
+
return tokenData;
7005
+
};
7006
+
7007
+
TokenStreamBase.prototype = {
7008
+
7009
+
//restore constructor
7010
+
constructor: TokenStreamBase,
7011
+
7012
+
//-------------------------------------------------------------------------
7013
+
// Matching methods
7014
+
//-------------------------------------------------------------------------
7015
+
7016
+
/**
7017
+
* Determines if the next token matches the given token type.
7018
+
* If so, that token is consumed; if not, the token is placed
7019
+
* back onto the token stream. You can pass in any number of
7020
+
* token types and this will return true if any of the token
7021
+
* types is found.
7022
+
* @param {int|int[]} tokenTypes Either a single token type or an array of
7023
+
* token types that the next token might be. If an array is passed,
7024
+
* it's assumed that the token can be any of these.
7025
+
* @param {variant} channel (Optional) The channel to read from. If not
7026
+
* provided, reads from the default (unnamed) channel.
7027
+
* @return {Boolean} True if the token type matches, false if not.
7028
+
* @method match
7029
+
*/
7030
+
match: function(tokenTypes, channel) {
7031
+
7032
+
//always convert to an array, makes things easier
7033
+
if (!(tokenTypes instanceof Array)) {
7034
+
tokenTypes = [tokenTypes];
7035
+
}
7036
+
7037
+
var tt = this.get(channel),
7038
+
i = 0,
7039
+
len = tokenTypes.length;
7040
+
7041
+
while (i < len) {
7042
+
if (tt === tokenTypes[i++]) {
7043
+
return true;
7044
+
}
7045
+
}
7046
+
7047
+
//no match found, put the token back
7048
+
this.unget();
7049
+
return false;
7050
+
},
7051
+
7052
+
/**
7053
+
* Determines if the next token matches the given token type.
7054
+
* If so, that token is consumed; if not, an error is thrown.
7055
+
* @param {int|int[]} tokenTypes Either a single token type or an array of
7056
+
* token types that the next token should be. If an array is passed,
7057
+
* it's assumed that the token must be one of these.
7058
+
* @return {void}
7059
+
* @method mustMatch
7060
+
*/
7061
+
mustMatch: function(tokenTypes) {
7062
+
7063
+
var token;
7064
+
7065
+
//always convert to an array, makes things easier
7066
+
if (!(tokenTypes instanceof Array)) {
7067
+
tokenTypes = [tokenTypes];
7068
+
}
7069
+
7070
+
if (!this.match.apply(this, arguments)) {
7071
+
token = this.LT(1);
7072
+
throw new SyntaxError("Expected " + this._tokenData[tokenTypes[0]].name +
7073
+
" at line " + token.startLine + ", col " + token.startCol + ".", token.startLine, token.startCol);
7074
+
}
7075
+
},
7076
+
7077
+
//-------------------------------------------------------------------------
7078
+
// Consuming methods
7079
+
//-------------------------------------------------------------------------
7080
+
7081
+
/**
7082
+
* Keeps reading from the token stream until either one of the specified
7083
+
* token types is found or until the end of the input is reached.
7084
+
* @param {int|int[]} tokenTypes Either a single token type or an array of
7085
+
* token types that the next token should be. If an array is passed,
7086
+
* it's assumed that the token must be one of these.
7087
+
* @param {variant} channel (Optional) The channel to read from. If not
7088
+
* provided, reads from the default (unnamed) channel.
7089
+
* @return {void}
7090
+
* @method advance
7091
+
*/
7092
+
advance: function(tokenTypes, channel) {
7093
+
7094
+
while (this.LA(0) !== 0 && !this.match(tokenTypes, channel)) {
7095
+
this.get();
7096
+
}
7097
+
7098
+
return this.LA(0);
7099
+
},
7100
+
7101
+
/**
7102
+
* Consumes the next token from the token stream.
7103
+
* @return {int} The token type of the token that was just consumed.
7104
+
* @method get
7105
+
*/
7106
+
get: function(channel) {
7107
+
7108
+
var tokenInfo = this._tokenData,
7109
+
i =0,
7110
+
token,
7111
+
info;
7112
+
7113
+
//check the lookahead buffer first
7114
+
if (this._lt.length && this._ltIndex >= 0 && this._ltIndex < this._lt.length) {
7115
+
7116
+
i++;
7117
+
this._token = this._lt[this._ltIndex++];
7118
+
info = tokenInfo[this._token.type];
7119
+
7120
+
//obey channels logic
7121
+
while ((info.channel !== undefined && channel !== info.channel) &&
7122
+
this._ltIndex < this._lt.length) {
7123
+
this._token = this._lt[this._ltIndex++];
7124
+
info = tokenInfo[this._token.type];
7125
+
i++;
7126
+
}
7127
+
7128
+
//here be dragons
7129
+
if ((info.channel === undefined || channel === info.channel) &&
7130
+
this._ltIndex <= this._lt.length) {
7131
+
this._ltIndexCache.push(i);
7132
+
return this._token.type;
7133
+
}
7134
+
}
7135
+
7136
+
//call token retriever method
7137
+
token = this._getToken();
7138
+
7139
+
//if it should be hidden, don't save a token
7140
+
if (token.type > -1 && !tokenInfo[token.type].hide) {
7141
+
7142
+
//apply token channel
7143
+
token.channel = tokenInfo[token.type].channel;
7144
+
7145
+
//save for later
7146
+
this._token = token;
7147
+
this._lt.push(token);
7148
+
7149
+
//save space that will be moved (must be done before array is truncated)
7150
+
this._ltIndexCache.push(this._lt.length - this._ltIndex + i);
7151
+
7152
+
//keep the buffer under 5 items
7153
+
if (this._lt.length > 5) {
7154
+
this._lt.shift();
7155
+
}
7156
+
7157
+
//also keep the shift buffer under 5 items
7158
+
if (this._ltIndexCache.length > 5) {
7159
+
this._ltIndexCache.shift();
7160
+
}
7161
+
7162
+
//update lookahead index
7163
+
this._ltIndex = this._lt.length;
7164
+
}
7165
+
7166
+
/*
7167
+
* Skip to the next token if:
7168
+
* 1. The token type is marked as hidden.
7169
+
* 2. The token type has a channel specified and it isn't the current channel.
7170
+
*/
7171
+
info = tokenInfo[token.type];
7172
+
if (info &&
7173
+
(info.hide ||
7174
+
(info.channel !== undefined && channel !== info.channel))) {
7175
+
return this.get(channel);
7176
+
} else {
7177
+
//return just the type
7178
+
return token.type;
7179
+
}
7180
+
},
7181
+
7182
+
/**
7183
+
* Looks ahead a certain number of tokens and returns the token type at
7184
+
* that position. This will throw an error if you lookahead past the
7185
+
* end of input, past the size of the lookahead buffer, or back past
7186
+
* the first token in the lookahead buffer.
7187
+
* @param {int} The index of the token type to retrieve. 0 for the
7188
+
* current token, 1 for the next, -1 for the previous, etc.
7189
+
* @return {int} The token type of the token in the given position.
7190
+
* @method LA
7191
+
*/
7192
+
LA: function(index) {
7193
+
var total = index,
7194
+
tt;
7195
+
if (index > 0) {
7196
+
//TODO: Store 5 somewhere
7197
+
if (index > 5) {
7198
+
throw new Error("Too much lookahead.");
7199
+
}
7200
+
7201
+
//get all those tokens
7202
+
while (total) {
7203
+
tt = this.get();
7204
+
total--;
7205
+
}
7206
+
7207
+
//unget all those tokens
7208
+
while (total < index) {
7209
+
this.unget();
7210
+
total++;
7211
+
}
7212
+
} else if (index < 0) {
7213
+
7214
+
if (this._lt[this._ltIndex+index]) {
7215
+
tt = this._lt[this._ltIndex+index].type;
7216
+
} else {
7217
+
throw new Error("Too much lookbehind.");
7218
+
}
7219
+
7220
+
} else {
7221
+
tt = this._token.type;
7222
+
}
7223
+
7224
+
return tt;
7225
+
7226
+
},
7227
+
7228
+
/**
7229
+
* Looks ahead a certain number of tokens and returns the token at
7230
+
* that position. This will throw an error if you lookahead past the
7231
+
* end of input, past the size of the lookahead buffer, or back past
7232
+
* the first token in the lookahead buffer.
7233
+
* @param {int} The index of the token type to retrieve. 0 for the
7234
+
* current token, 1 for the next, -1 for the previous, etc.
7235
+
* @return {Object} The token of the token in the given position.
7236
+
* @method LA
7237
+
*/
7238
+
LT: function(index) {
7239
+
7240
+
//lookahead first to prime the token buffer
7241
+
this.LA(index);
7242
+
7243
+
//now find the token, subtract one because _ltIndex is already at the next index
7244
+
return this._lt[this._ltIndex+index-1];
7245
+
},
7246
+
7247
+
/**
7248
+
* Returns the token type for the next token in the stream without
7249
+
* consuming it.
7250
+
* @return {int} The token type of the next token in the stream.
7251
+
* @method peek
7252
+
*/
7253
+
peek: function() {
7254
+
return this.LA(1);
7255
+
},
7256
+
7257
+
/**
7258
+
* Returns the actual token object for the last consumed token.
7259
+
* @return {Token} The token object for the last consumed token.
7260
+
* @method token
7261
+
*/
7262
+
token: function() {
7263
+
return this._token;
7264
+
},
7265
+
7266
+
/**
7267
+
* Returns the name of the token for the given token type.
7268
+
* @param {int} tokenType The type of token to get the name of.
7269
+
* @return {String} The name of the token or "UNKNOWN_TOKEN" for any
7270
+
* invalid token type.
7271
+
* @method tokenName
7272
+
*/
7273
+
tokenName: function(tokenType) {
7274
+
if (tokenType < 0 || tokenType > this._tokenData.length) {
7275
+
return "UNKNOWN_TOKEN";
7276
+
} else {
7277
+
return this._tokenData[tokenType].name;
7278
+
}
7279
+
},
7280
+
7281
+
/**
7282
+
* Returns the token type value for the given token name.
7283
+
* @param {String} tokenName The name of the token whose value should be returned.
7284
+
* @return {int} The token type value for the given token name or -1
7285
+
* for an unknown token.
7286
+
* @method tokenName
7287
+
*/
7288
+
tokenType: function(tokenName) {
7289
+
return this._tokenData[tokenName] || -1;
7290
+
},
7291
+
7292
+
/**
7293
+
* Returns the last consumed token to the token stream.
7294
+
* @method unget
7295
+
*/
7296
+
unget: function() {
7297
+
//if (this._ltIndex > -1) {
7298
+
if (this._ltIndexCache.length) {
7299
+
this._ltIndex -= this._ltIndexCache.pop();//--;
7300
+
this._token = this._lt[this._ltIndex - 1];
7301
+
} else {
7302
+
throw new Error("Too much lookahead.");
7303
+
}
7304
+
}
7305
+
7306
+
};
7307
+
7308
+
7309
+
},{"./StringReader":24,"./SyntaxError":25}],28:[function(require,module,exports){
7310
+
"use strict";
7311
+
7312
+
module.exports = {
7313
+
StringReader : require("./StringReader"),
7314
+
SyntaxError : require("./SyntaxError"),
7315
+
SyntaxUnit : require("./SyntaxUnit"),
7316
+
EventTarget : require("./EventTarget"),
7317
+
TokenStreamBase : require("./TokenStreamBase")
7318
+
};
7319
+
7320
+
},{"./EventTarget":23,"./StringReader":24,"./SyntaxError":25,"./SyntaxUnit":26,"./TokenStreamBase":27}],"parserlib":[function(require,module,exports){
7321
+
"use strict";
7322
+
7323
+
module.exports = {
7324
+
css : require("./css"),
7325
+
util : require("./util")
7326
+
};
7327
+
7328
+
},{"./css":22,"./util":28}]},{},[]);
7329
+
7330
+
return require('parserlib');
7331
+
})();
7332
+
var clone = (function() {
7333
+
'use strict';
7334
+
7335
+
var nativeMap;
7336
+
try {
7337
+
nativeMap = Map;
7338
+
} catch(_) {
7339
+
// maybe a reference error because no `Map`. Give it a dummy value that no
7340
+
// value will ever be an instanceof.
7341
+
nativeMap = function() {};
7342
+
}
7343
+
7344
+
var nativeSet;
7345
+
try {
7346
+
nativeSet = Set;
7347
+
} catch(_) {
7348
+
nativeSet = function() {};
7349
+
}
7350
+
7351
+
var nativePromise;
7352
+
try {
7353
+
nativePromise = Promise;
7354
+
} catch(_) {
7355
+
nativePromise = function() {};
7356
+
}
7357
+
7358
+
/**
7359
+
* Clones (copies) an Object using deep copying.
7360
+
*
7361
+
* This function supports circular references by default, but if you are certain
7362
+
* there are no circular references in your object, you can save some CPU time
7363
+
* by calling clone(obj, false).
7364
+
*
7365
+
* Caution: if `circular` is false and `parent` contains circular references,
7366
+
* your program may enter an infinite loop and crash.
7367
+
*
7368
+
* @param `parent` - the object to be cloned
7369
+
* @param `circular` - set to true if the object to be cloned may contain
7370
+
* circular references. (optional - true by default)
7371
+
* @param `depth` - set to a number if the object is only to be cloned to
7372
+
* a particular depth. (optional - defaults to Infinity)
7373
+
* @param `prototype` - sets the prototype to be used when cloning an object.
7374
+
* (optional - defaults to parent prototype).
7375
+
* @param `includeNonEnumerable` - set to true if the non-enumerable properties
7376
+
* should be cloned as well. Non-enumerable properties on the prototype
7377
+
* chain will be ignored. (optional - false by default)
7378
+
*/
7379
+
function clone(parent, circular, depth, prototype, includeNonEnumerable) {
7380
+
if (typeof circular === 'object') {
7381
+
depth = circular.depth;
7382
+
prototype = circular.prototype;
7383
+
includeNonEnumerable = circular.includeNonEnumerable;
7384
+
circular = circular.circular;
7385
+
}
7386
+
// maintain two arrays for circular references, where corresponding parents
7387
+
// and children have the same index
7388
+
var allParents = [];
7389
+
var allChildren = [];
7390
+
7391
+
var useBuffer = typeof Buffer != 'undefined';
7392
+
7393
+
if (typeof circular == 'undefined')
7394
+
circular = true;
7395
+
7396
+
if (typeof depth == 'undefined')
7397
+
depth = Infinity;
7398
+
7399
+
// recurse this function so we don't reset allParents and allChildren
7400
+
function _clone(parent, depth) {
7401
+
// cloning null always returns null
7402
+
if (parent === null)
7403
+
return null;
7404
+
7405
+
if (depth === 0)
7406
+
return parent;
7407
+
7408
+
var child;
7409
+
var proto;
7410
+
if (typeof parent != 'object') {
7411
+
return parent;
7412
+
}
7413
+
7414
+
if (parent instanceof nativeMap) {
7415
+
child = new nativeMap();
7416
+
} else if (parent instanceof nativeSet) {
7417
+
child = new nativeSet();
7418
+
} else if (parent instanceof nativePromise) {
7419
+
child = new nativePromise(function (resolve, reject) {
7420
+
parent.then(function(value) {
7421
+
resolve(_clone(value, depth - 1));
7422
+
}, function(err) {
7423
+
reject(_clone(err, depth - 1));
7424
+
});
7425
+
});
7426
+
} else if (clone.__isArray(parent)) {
7427
+
child = [];
7428
+
} else if (clone.__isRegExp(parent)) {
7429
+
child = new RegExp(parent.source, __getRegExpFlags(parent));
7430
+
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
7431
+
} else if (clone.__isDate(parent)) {
7432
+
child = new Date(parent.getTime());
7433
+
} else if (useBuffer && Buffer.isBuffer(parent)) {
7434
+
child = new Buffer(parent.length);
7435
+
parent.copy(child);
7436
+
return child;
7437
+
} else if (parent instanceof Error) {
7438
+
child = Object.create(parent);
7439
+
} else {
7440
+
if (typeof prototype == 'undefined') {
7441
+
proto = Object.getPrototypeOf(parent);
7442
+
child = Object.create(proto);
7443
+
}
7444
+
else {
7445
+
child = Object.create(prototype);
7446
+
proto = prototype;
7447
+
}
7448
+
}
7449
+
7450
+
if (circular) {
7451
+
var index = allParents.indexOf(parent);
7452
+
7453
+
if (index != -1) {
7454
+
return allChildren[index];
7455
+
}
7456
+
allParents.push(parent);
7457
+
allChildren.push(child);
7458
+
}
7459
+
7460
+
if (parent instanceof nativeMap) {
7461
+
var keyIterator = parent.keys();
7462
+
while(true) {
7463
+
var next = keyIterator.next();
7464
+
if (next.done) {
7465
+
break;
7466
+
}
7467
+
var keyChild = _clone(next.value, depth - 1);
7468
+
var valueChild = _clone(parent.get(next.value), depth - 1);
7469
+
child.set(keyChild, valueChild);
7470
+
}
7471
+
}
7472
+
if (parent instanceof nativeSet) {
7473
+
var iterator = parent.keys();
7474
+
while(true) {
7475
+
var next = iterator.next();
7476
+
if (next.done) {
7477
+
break;
7478
+
}
7479
+
var entryChild = _clone(next.value, depth - 1);
7480
+
child.add(entryChild);
7481
+
}
7482
+
}
7483
+
7484
+
for (var i in parent) {
7485
+
var attrs;
7486
+
if (proto) {
7487
+
attrs = Object.getOwnPropertyDescriptor(proto, i);
7488
+
}
7489
+
7490
+
if (attrs && attrs.set == null) {
7491
+
continue;
7492
+
}
7493
+
child[i] = _clone(parent[i], depth - 1);
7494
+
}
7495
+
7496
+
if (Object.getOwnPropertySymbols) {
7497
+
var symbols = Object.getOwnPropertySymbols(parent);
7498
+
for (var i = 0; i < symbols.length; i++) {
7499
+
// Don't need to worry about cloning a symbol because it is a primitive,
7500
+
// like a number or string.
7501
+
var symbol = symbols[i];
7502
+
var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
7503
+
if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
7504
+
continue;
7505
+
}
7506
+
child[symbol] = _clone(parent[symbol], depth - 1);
7507
+
if (!descriptor.enumerable) {
7508
+
Object.defineProperty(child, symbol, {
7509
+
enumerable: false
7510
+
});
7511
+
}
7512
+
}
7513
+
}
7514
+
7515
+
if (includeNonEnumerable) {
7516
+
var allPropertyNames = Object.getOwnPropertyNames(parent);
7517
+
for (var i = 0; i < allPropertyNames.length; i++) {
7518
+
var propertyName = allPropertyNames[i];
7519
+
var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
7520
+
if (descriptor && descriptor.enumerable) {
7521
+
continue;
7522
+
}
7523
+
child[propertyName] = _clone(parent[propertyName], depth - 1);
7524
+
Object.defineProperty(child, propertyName, {
7525
+
enumerable: false
7526
+
});
7527
+
}
7528
+
}
7529
+
7530
+
return child;
7531
+
}
7532
+
7533
+
return _clone(parent, depth);
7534
+
}
7535
+
7536
+
/**
7537
+
* Simple flat clone using prototype, accepts only objects, usefull for property
7538
+
* override on FLAT configuration object (no nested props).
7539
+
*
7540
+
* USE WITH CAUTION! This may not behave as you wish if you do not know how this
7541
+
* works.
7542
+
*/
7543
+
clone.clonePrototype = function clonePrototype(parent) {
7544
+
if (parent === null)
7545
+
return null;
7546
+
7547
+
var c = function () {};
7548
+
c.prototype = parent;
7549
+
return new c();
7550
+
};
7551
+
7552
+
// private utility functions
7553
+
7554
+
function __objToStr(o) {
7555
+
return Object.prototype.toString.call(o);
7556
+
}
7557
+
clone.__objToStr = __objToStr;
7558
+
7559
+
function __isDate(o) {
7560
+
return typeof o === 'object' && __objToStr(o) === '[object Date]';
7561
+
}
7562
+
clone.__isDate = __isDate;
7563
+
7564
+
function __isArray(o) {
7565
+
return typeof o === 'object' && __objToStr(o) === '[object Array]';
7566
+
}
7567
+
clone.__isArray = __isArray;
7568
+
7569
+
function __isRegExp(o) {
7570
+
return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
7571
+
}
7572
+
clone.__isRegExp = __isRegExp;
7573
+
7574
+
function __getRegExpFlags(re) {
7575
+
var flags = '';
7576
+
if (re.global) flags += 'g';
7577
+
if (re.ignoreCase) flags += 'i';
7578
+
if (re.multiline) flags += 'm';
7579
+
return flags;
7580
+
}
7581
+
clone.__getRegExpFlags = __getRegExpFlags;
7582
+
7583
+
return clone;
7584
+
})();
7585
+
7586
+
if (typeof module === 'object' && module.exports) {
7587
+
module.exports = clone;
7588
+
}
7589
+
7590
+
/**
7591
+
* Main CSSLint object.
7592
+
* @class CSSLint
7593
+
* @static
7594
+
* @extends parserlib.util.EventTarget
7595
+
*/
7596
+
7597
+
/* global parserlib, clone, Reporter */
7598
+
/* exported CSSLint */
7599
+
7600
+
var CSSLint = (function() {
7601
+
"use strict";
7602
+
7603
+
var rules = [],
7604
+
formatters = [],
7605
+
embeddedRuleset = /\/\*\s*csslint([^\*]*)\*\//,
7606
+
api = new parserlib.util.EventTarget();
7607
+
7608
+
api.version = "1.0.4";
7609
+
7610
+
//-------------------------------------------------------------------------
7611
+
// Rule Management
7612
+
//-------------------------------------------------------------------------
7613
+
7614
+
/**
7615
+
* Adds a new rule to the engine.
7616
+
* @param {Object} rule The rule to add.
7617
+
* @method addRule
7618
+
*/
7619
+
api.addRule = function(rule) {
7620
+
rules.push(rule);
7621
+
rules[rule.id] = rule;
7622
+
};
7623
+
7624
+
/**
7625
+
* Clears all rule from the engine.
7626
+
* @method clearRules
7627
+
*/
7628
+
api.clearRules = function() {
7629
+
rules = [];
7630
+
};
7631
+
7632
+
/**
7633
+
* Returns the rule objects.
7634
+
* @return An array of rule objects.
7635
+
* @method getRules
7636
+
*/
7637
+
api.getRules = function() {
7638
+
return [].concat(rules).sort(function(a, b) {
7639
+
return a.id > b.id ? 1 : 0;
7640
+
});
7641
+
};
7642
+
7643
+
/**
7644
+
* Returns a ruleset configuration object with all current rules.
7645
+
* @return A ruleset object.
7646
+
* @method getRuleset
7647
+
*/
7648
+
api.getRuleset = function() {
7649
+
var ruleset = {},
7650
+
i = 0,
7651
+
len = rules.length;
7652
+
7653
+
while (i < len) {
7654
+
ruleset[rules[i++].id] = 1; // by default, everything is a warning
7655
+
}
7656
+
7657
+
return ruleset;
7658
+
};
7659
+
7660
+
/**
7661
+
* Returns a ruleset object based on embedded rules.
7662
+
* @param {String} text A string of css containing embedded rules.
7663
+
* @param {Object} ruleset A ruleset object to modify.
7664
+
* @return {Object} A ruleset object.
7665
+
* @method getEmbeddedRuleset
7666
+
*/
7667
+
function applyEmbeddedRuleset(text, ruleset) {
7668
+
var valueMap,
7669
+
embedded = text && text.match(embeddedRuleset),
7670
+
rules = embedded && embedded[1];
7671
+
7672
+
if (rules) {
7673
+
valueMap = {
7674
+
"true": 2, // true is error
7675
+
"": 1, // blank is warning
7676
+
"false": 0, // false is ignore
7677
+
7678
+
"2": 2, // explicit error
7679
+
"1": 1, // explicit warning
7680
+
"0": 0 // explicit ignore
7681
+
};
7682
+
7683
+
rules.toLowerCase().split(",").forEach(function(rule) {
7684
+
var pair = rule.split(":"),
7685
+
property = pair[0] || "",
7686
+
value = pair[1] || "";
7687
+
7688
+
ruleset[property.trim()] = valueMap[value.trim()];
7689
+
});
7690
+
}
7691
+
7692
+
return ruleset;
7693
+
}
7694
+
7695
+
//-------------------------------------------------------------------------
7696
+
// Formatters
7697
+
//-------------------------------------------------------------------------
7698
+
7699
+
/**
7700
+
* Adds a new formatter to the engine.
7701
+
* @param {Object} formatter The formatter to add.
7702
+
* @method addFormatter
7703
+
*/
7704
+
api.addFormatter = function(formatter) {
7705
+
// formatters.push(formatter);
7706
+
formatters[formatter.id] = formatter;
7707
+
};
7708
+
7709
+
/**
7710
+
* Retrieves a formatter for use.
7711
+
* @param {String} formatId The name of the format to retrieve.
7712
+
* @return {Object} The formatter or undefined.
7713
+
* @method getFormatter
7714
+
*/
7715
+
api.getFormatter = function(formatId) {
7716
+
return formatters[formatId];
7717
+
};
7718
+
7719
+
/**
7720
+
* Formats the results in a particular format for a single file.
7721
+
* @param {Object} result The results returned from CSSLint.verify().
7722
+
* @param {String} filename The filename for which the results apply.
7723
+
* @param {String} formatId The name of the formatter to use.
7724
+
* @param {Object} options (Optional) for special output handling.
7725
+
* @return {String} A formatted string for the results.
7726
+
* @method format
7727
+
*/
7728
+
api.format = function(results, filename, formatId, options) {
7729
+
var formatter = this.getFormatter(formatId),
7730
+
result = null;
7731
+
7732
+
if (formatter) {
7733
+
result = formatter.startFormat();
7734
+
result += formatter.formatResults(results, filename, options || {});
7735
+
result += formatter.endFormat();
7736
+
}
7737
+
7738
+
return result;
7739
+
};
7740
+
7741
+
/**
7742
+
* Indicates if the given format is supported.
7743
+
* @param {String} formatId The ID of the format to check.
7744
+
* @return {Boolean} True if the format exists, false if not.
7745
+
* @method hasFormat
7746
+
*/
7747
+
api.hasFormat = function(formatId) {
7748
+
return formatters.hasOwnProperty(formatId);
7749
+
};
7750
+
7751
+
//-------------------------------------------------------------------------
7752
+
// Verification
7753
+
//-------------------------------------------------------------------------
7754
+
7755
+
/**
7756
+
* Starts the verification process for the given CSS text.
7757
+
* @param {String} text The CSS text to verify.
7758
+
* @param {Object} ruleset (Optional) List of rules to apply. If null, then
7759
+
* all rules are used. If a rule has a value of 1 then it's a warning,
7760
+
* a value of 2 means it's an error.
7761
+
* @return {Object} Results of the verification.
7762
+
* @method verify
7763
+
*/
7764
+
api.verify = function(text, ruleset) {
7765
+
7766
+
var i = 0,
7767
+
reporter,
7768
+
lines,
7769
+
allow = {},
7770
+
ignore = [],
7771
+
report,
7772
+
parser = new parserlib.css.Parser({
7773
+
starHack: true,
7774
+
ieFilters: true,
7775
+
underscoreHack: true,
7776
+
strict: false
7777
+
});
7778
+
7779
+
// normalize line endings
7780
+
lines = text.replace(/\n\r?/g, "$split$").split("$split$");
7781
+
7782
+
// find 'allow' comments
7783
+
CSSLint.Util.forEach(lines, function (line, lineno) {
7784
+
var allowLine = line && line.match(/\/\*[ \t]*csslint[ \t]+allow:[ \t]*([^\*]*)\*\//i),
7785
+
allowRules = allowLine && allowLine[1],
7786
+
allowRuleset = {};
7787
+
7788
+
if (allowRules) {
7789
+
allowRules.toLowerCase().split(",").forEach(function(allowRule) {
7790
+
allowRuleset[allowRule.trim()] = true;
7791
+
});
7792
+
if (Object.keys(allowRuleset).length > 0) {
7793
+
allow[lineno + 1] = allowRuleset;
7794
+
}
7795
+
}
7796
+
});
7797
+
7798
+
var ignoreStart = null,
7799
+
ignoreEnd = null;
7800
+
CSSLint.Util.forEach(lines, function (line, lineno) {
7801
+
// Keep oldest, "unclosest" ignore:start
7802
+
if (ignoreStart === null && line.match(/\/\*[ \t]*csslint[ \t]+ignore:start[ \t]*\*\//i)) {
7803
+
ignoreStart = lineno;
7804
+
}
7805
+
7806
+
if (line.match(/\/\*[ \t]*csslint[ \t]+ignore:end[ \t]*\*\//i)) {
7807
+
ignoreEnd = lineno;
7808
+
}
7809
+
7810
+
if (ignoreStart !== null && ignoreEnd !== null) {
7811
+
ignore.push([ignoreStart, ignoreEnd]);
7812
+
ignoreStart = ignoreEnd = null;
7813
+
}
7814
+
});
7815
+
7816
+
// Close remaining ignore block, if any
7817
+
if (ignoreStart !== null) {
7818
+
ignore.push([ignoreStart, lines.length]);
7819
+
}
7820
+
7821
+
if (!ruleset) {
7822
+
ruleset = this.getRuleset();
7823
+
}
7824
+
7825
+
if (embeddedRuleset.test(text)) {
7826
+
// defensively copy so that caller's version does not get modified
7827
+
ruleset = clone(ruleset);
7828
+
ruleset = applyEmbeddedRuleset(text, ruleset);
7829
+
}
7830
+
7831
+
reporter = new Reporter(lines, ruleset, allow, ignore);
7832
+
7833
+
ruleset.errors = 2; // always report parsing errors as errors
7834
+
for (i in ruleset) {
7835
+
if (ruleset.hasOwnProperty(i) && ruleset[i]) {
7836
+
if (rules[i]) {
7837
+
rules[i].init(parser, reporter);
7838
+
}
7839
+
}
7840
+
}
7841
+
7842
+
7843
+
// capture most horrible error type
7844
+
try {
7845
+
parser.parse(text);
7846
+
} catch (ex) {
7847
+
reporter.error("Fatal error, cannot continue: " + ex.message, ex.line, ex.col, {});
7848
+
}
7849
+
7850
+
report = {
7851
+
messages : reporter.messages,
7852
+
stats : reporter.stats,
7853
+
ruleset : reporter.ruleset,
7854
+
allow : reporter.allow,
7855
+
ignore : reporter.ignore
7856
+
};
7857
+
7858
+
// sort by line numbers, rollups at the bottom
7859
+
report.messages.sort(function (a, b) {
7860
+
if (a.rollup && !b.rollup) {
7861
+
return 1;
7862
+
} else if (!a.rollup && b.rollup) {
7863
+
return -1;
7864
+
} else {
7865
+
return a.line - b.line;
7866
+
}
7867
+
});
7868
+
7869
+
return report;
7870
+
};
7871
+
7872
+
//-------------------------------------------------------------------------
7873
+
// Publish the API
7874
+
//-------------------------------------------------------------------------
7875
+
7876
+
return api;
7877
+
7878
+
})();
7879
+
7880
+
/**
7881
+
* An instance of Report is used to report results of the
7882
+
* verification back to the main API.
7883
+
* @class Reporter
7884
+
* @constructor
7885
+
* @param {String[]} lines The text lines of the source.
7886
+
* @param {Object} ruleset The set of rules to work with, including if
7887
+
* they are errors or warnings.
7888
+
* @param {Object} explicitly allowed lines
7889
+
* @param {[][]} ingore list of line ranges to be ignored
7890
+
*/
7891
+
function Reporter(lines, ruleset, allow, ignore) {
7892
+
"use strict";
7893
+
7894
+
/**
7895
+
* List of messages being reported.
7896
+
* @property messages
7897
+
* @type String[]
7898
+
*/
7899
+
this.messages = [];
7900
+
7901
+
/**
7902
+
* List of statistics being reported.
7903
+
* @property stats
7904
+
* @type String[]
7905
+
*/
7906
+
this.stats = [];
7907
+
7908
+
/**
7909
+
* Lines of code being reported on. Used to provide contextual information
7910
+
* for messages.
7911
+
* @property lines
7912
+
* @type String[]
7913
+
*/
7914
+
this.lines = lines;
7915
+
7916
+
/**
7917
+
* Information about the rules. Used to determine whether an issue is an
7918
+
* error or warning.
7919
+
* @property ruleset
7920
+
* @type Object
7921
+
*/
7922
+
this.ruleset = ruleset;
7923
+
7924
+
/**
7925
+
* Lines with specific rule messages to leave out of the report.
7926
+
* @property allow
7927
+
* @type Object
7928
+
*/
7929
+
this.allow = allow;
7930
+
if (!this.allow) {
7931
+
this.allow = {};
7932
+
}
7933
+
7934
+
/**
7935
+
* Linesets not to include in the report.
7936
+
* @property ignore
7937
+
* @type [][]
7938
+
*/
7939
+
this.ignore = ignore;
7940
+
if (!this.ignore) {
7941
+
this.ignore = [];
7942
+
}
7943
+
}
7944
+
7945
+
Reporter.prototype = {
7946
+
7947
+
// restore constructor
7948
+
constructor: Reporter,
7949
+
7950
+
/**
7951
+
* Report an error.
7952
+
* @param {String} message The message to store.
7953
+
* @param {int} line The line number.
7954
+
* @param {int} col The column number.
7955
+
* @param {Object} rule The rule this message relates to.
7956
+
* @method error
7957
+
*/
7958
+
error: function(message, line, col, rule) {
7959
+
"use strict";
7960
+
this.messages.push({
7961
+
type : "error",
7962
+
line : line,
7963
+
col : col,
7964
+
message : message,
7965
+
evidence: this.lines[line-1],
7966
+
rule : rule || {}
7967
+
});
7968
+
},
7969
+
7970
+
/**
7971
+
* Report an warning.
7972
+
* @param {String} message The message to store.
7973
+
* @param {int} line The line number.
7974
+
* @param {int} col The column number.
7975
+
* @param {Object} rule The rule this message relates to.
7976
+
* @method warn
7977
+
* @deprecated Use report instead.
7978
+
*/
7979
+
warn: function(message, line, col, rule) {
7980
+
"use strict";
7981
+
this.report(message, line, col, rule);
7982
+
},
7983
+
7984
+
/**
7985
+
* Report an issue.
7986
+
* @param {String} message The message to store.
7987
+
* @param {int} line The line number.
7988
+
* @param {int} col The column number.
7989
+
* @param {Object} rule The rule this message relates to.
7990
+
* @method report
7991
+
*/
7992
+
report: function(message, line, col, rule) {
7993
+
"use strict";
7994
+
7995
+
// Check if rule violation should be allowed
7996
+
if (this.allow.hasOwnProperty(line) && this.allow[line].hasOwnProperty(rule.id)) {
7997
+
return;
7998
+
}
7999
+
8000
+
var ignore = false;
8001
+
CSSLint.Util.forEach(this.ignore, function (range) {
8002
+
if (range[0] <= line && line <= range[1]) {
8003
+
ignore = true;
8004
+
}
8005
+
});
8006
+
if (ignore) {
8007
+
return;
8008
+
}
8009
+
8010
+
this.messages.push({
8011
+
type : this.ruleset[rule.id] === 2 ? "error" : "warning",
8012
+
line : line,
8013
+
col : col,
8014
+
message : message,
8015
+
evidence: this.lines[line-1],
8016
+
rule : rule
8017
+
});
8018
+
},
8019
+
8020
+
/**
8021
+
* Report some informational text.
8022
+
* @param {String} message The message to store.
8023
+
* @param {int} line The line number.
8024
+
* @param {int} col The column number.
8025
+
* @param {Object} rule The rule this message relates to.
8026
+
* @method info
8027
+
*/
8028
+
info: function(message, line, col, rule) {
8029
+
"use strict";
8030
+
this.messages.push({
8031
+
type : "info",
8032
+
line : line,
8033
+
col : col,
8034
+
message : message,
8035
+
evidence: this.lines[line-1],
8036
+
rule : rule
8037
+
});
8038
+
},
8039
+
8040
+
/**
8041
+
* Report some rollup error information.
8042
+
* @param {String} message The message to store.
8043
+
* @param {Object} rule The rule this message relates to.
8044
+
* @method rollupError
8045
+
*/
8046
+
rollupError: function(message, rule) {
8047
+
"use strict";
8048
+
this.messages.push({
8049
+
type : "error",
8050
+
rollup : true,
8051
+
message : message,
8052
+
rule : rule
8053
+
});
8054
+
},
8055
+
8056
+
/**
8057
+
* Report some rollup warning information.
8058
+
* @param {String} message The message to store.
8059
+
* @param {Object} rule The rule this message relates to.
8060
+
* @method rollupWarn
8061
+
*/
8062
+
rollupWarn: function(message, rule) {
8063
+
"use strict";
8064
+
this.messages.push({
8065
+
type : "warning",
8066
+
rollup : true,
8067
+
message : message,
8068
+
rule : rule
8069
+
});
8070
+
},
8071
+
8072
+
/**
8073
+
* Report a statistic.
8074
+
* @param {String} name The name of the stat to store.
8075
+
* @param {Variant} value The value of the stat.
8076
+
* @method stat
8077
+
*/
8078
+
stat: function(name, value) {
8079
+
"use strict";
8080
+
this.stats[name] = value;
8081
+
}
8082
+
};
8083
+
8084
+
// expose for testing purposes
8085
+
CSSLint._Reporter = Reporter;
8086
+
8087
+
/*
8088
+
* Utility functions that make life easier.
8089
+
*/
8090
+
CSSLint.Util = {
8091
+
/*
8092
+
* Adds all properties from supplier onto receiver,
8093
+
* overwriting if the same name already exists on
8094
+
* receiver.
8095
+
* @param {Object} The object to receive the properties.
8096
+
* @param {Object} The object to provide the properties.
8097
+
* @return {Object} The receiver
8098
+
*/
8099
+
mix: function(receiver, supplier) {
8100
+
"use strict";
8101
+
var prop;
8102
+
8103
+
for (prop in supplier) {
8104
+
if (supplier.hasOwnProperty(prop)) {
8105
+
receiver[prop] = supplier[prop];
8106
+
}
8107
+
}
8108
+
8109
+
return prop;
8110
+
},
8111
+
8112
+
/*
8113
+
* Polyfill for array indexOf() method.
8114
+
* @param {Array} values The array to search.
8115
+
* @param {Variant} value The value to search for.
8116
+
* @return {int} The index of the value if found, -1 if not.
8117
+
*/
8118
+
indexOf: function(values, value) {
8119
+
"use strict";
8120
+
if (values.indexOf) {
8121
+
return values.indexOf(value);
8122
+
} else {
8123
+
for (var i=0, len=values.length; i < len; i++) {
8124
+
if (values[i] === value) {
8125
+
return i;
8126
+
}
8127
+
}
8128
+
return -1;
8129
+
}
8130
+
},
8131
+
8132
+
/*
8133
+
* Polyfill for array forEach() method.
8134
+
* @param {Array} values The array to operate on.
8135
+
* @param {Function} func The function to call on each item.
8136
+
* @return {void}
8137
+
*/
8138
+
forEach: function(values, func) {
8139
+
"use strict";
8140
+
if (values.forEach) {
8141
+
return values.forEach(func);
8142
+
} else {
8143
+
for (var i=0, len=values.length; i < len; i++) {
8144
+
func(values[i], i, values);
8145
+
}
8146
+
}
8147
+
}
8148
+
};
8149
+
8150
+
/*
8151
+
* Rule: Don't use adjoining classes (.foo.bar).
8152
+
*/
8153
+
8154
+
CSSLint.addRule({
8155
+
8156
+
// rule information
8157
+
id: "adjoining-classes",
8158
+
name: "Disallow adjoining classes",
8159
+
desc: "Don't use adjoining classes.",
8160
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-adjoining-classes",
8161
+
browsers: "IE6",
8162
+
8163
+
// initialization
8164
+
init: function(parser, reporter) {
8165
+
"use strict";
8166
+
var rule = this;
8167
+
parser.addListener("startrule", function(event) {
8168
+
var selectors = event.selectors,
8169
+
selector,
8170
+
part,
8171
+
modifier,
8172
+
classCount,
8173
+
i, j, k;
8174
+
8175
+
for (i=0; i < selectors.length; i++) {
8176
+
selector = selectors[i];
8177
+
for (j=0; j < selector.parts.length; j++) {
8178
+
part = selector.parts[j];
8179
+
if (part.type === parser.SELECTOR_PART_TYPE) {
8180
+
classCount = 0;
8181
+
for (k=0; k < part.modifiers.length; k++) {
8182
+
modifier = part.modifiers[k];
8183
+
if (modifier.type === "class") {
8184
+
classCount++;
8185
+
}
8186
+
if (classCount > 1){
8187
+
reporter.report("Adjoining classes: "+selectors[i].text, part.line, part.col, rule);
8188
+
}
8189
+
}
8190
+
}
8191
+
}
8192
+
}
8193
+
});
8194
+
}
8195
+
8196
+
});
8197
+
8198
+
/*
8199
+
* Rule: Don't use width or height when using padding or border.
8200
+
*/
8201
+
CSSLint.addRule({
8202
+
8203
+
// rule information
8204
+
id: "box-model",
8205
+
name: "Beware of broken box size",
8206
+
desc: "Don't use width or height when using padding or border.",
8207
+
url: "https://github.com/CSSLint/csslint/wiki/Beware-of-box-model-size",
8208
+
browsers: "All",
8209
+
8210
+
// initialization
8211
+
init: function(parser, reporter) {
8212
+
"use strict";
8213
+
var rule = this,
8214
+
widthProperties = {
8215
+
border: 1,
8216
+
"border-left": 1,
8217
+
"border-right": 1,
8218
+
padding: 1,
8219
+
"padding-left": 1,
8220
+
"padding-right": 1
8221
+
},
8222
+
heightProperties = {
8223
+
border: 1,
8224
+
"border-bottom": 1,
8225
+
"border-top": 1,
8226
+
padding: 1,
8227
+
"padding-bottom": 1,
8228
+
"padding-top": 1
8229
+
},
8230
+
properties,
8231
+
boxSizing = false;
8232
+
8233
+
function startRule() {
8234
+
properties = {};
8235
+
boxSizing = false;
8236
+
}
8237
+
8238
+
function endRule() {
8239
+
var prop, value;
8240
+
8241
+
if (!boxSizing) {
8242
+
if (properties.height) {
8243
+
for (prop in heightProperties) {
8244
+
if (heightProperties.hasOwnProperty(prop) && properties[prop]) {
8245
+
value = properties[prop].value;
8246
+
// special case for padding
8247
+
if (!(prop === "padding" && value.parts.length === 2 && value.parts[0].value === 0)) {
8248
+
reporter.report("Using height with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
8249
+
}
8250
+
}
8251
+
}
8252
+
}
8253
+
8254
+
if (properties.width) {
8255
+
for (prop in widthProperties) {
8256
+
if (widthProperties.hasOwnProperty(prop) && properties[prop]) {
8257
+
value = properties[prop].value;
8258
+
8259
+
if (!(prop === "padding" && value.parts.length === 2 && value.parts[1].value === 0)) {
8260
+
reporter.report("Using width with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
8261
+
}
8262
+
}
8263
+
}
8264
+
}
8265
+
}
8266
+
}
8267
+
8268
+
parser.addListener("startrule", startRule);
8269
+
parser.addListener("startfontface", startRule);
8270
+
parser.addListener("startpage", startRule);
8271
+
parser.addListener("startpagemargin", startRule);
8272
+
parser.addListener("startkeyframerule", startRule);
8273
+
parser.addListener("startviewport", startRule);
8274
+
8275
+
parser.addListener("property", function(event) {
8276
+
var name = event.property.text.toLowerCase();
8277
+
8278
+
if (heightProperties[name] || widthProperties[name]) {
8279
+
if (!/^0\S*$/.test(event.value) && !(name === "border" && event.value.toString() === "none")) {
8280
+
properties[name] = {
8281
+
line: event.property.line,
8282
+
col: event.property.col,
8283
+
value: event.value
8284
+
};
8285
+
}
8286
+
} else {
8287
+
if (/^(width|height)/i.test(name) && /^(length|percentage)/.test(event.value.parts[0].type)) {
8288
+
properties[name] = 1;
8289
+
} else if (name === "box-sizing") {
8290
+
boxSizing = true;
8291
+
}
8292
+
}
8293
+
8294
+
});
8295
+
8296
+
parser.addListener("endrule", endRule);
8297
+
parser.addListener("endfontface", endRule);
8298
+
parser.addListener("endpage", endRule);
8299
+
parser.addListener("endpagemargin", endRule);
8300
+
parser.addListener("endkeyframerule", endRule);
8301
+
parser.addListener("endviewport", endRule);
8302
+
}
8303
+
8304
+
});
8305
+
8306
+
/*
8307
+
* Rule: box-sizing doesn't work in IE6 and IE7.
8308
+
*/
8309
+
8310
+
CSSLint.addRule({
8311
+
8312
+
// rule information
8313
+
id: "box-sizing",
8314
+
name: "Disallow use of box-sizing",
8315
+
desc: "The box-sizing properties isn't supported in IE6 and IE7.",
8316
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-box-sizing",
8317
+
browsers: "IE6, IE7",
8318
+
tags: ["Compatibility"],
8319
+
8320
+
// initialization
8321
+
init: function(parser, reporter) {
8322
+
"use strict";
8323
+
var rule = this;
8324
+
8325
+
parser.addListener("property", function(event) {
8326
+
var name = event.property.text.toLowerCase();
8327
+
8328
+
if (name === "box-sizing") {
8329
+
reporter.report("The box-sizing property isn't supported in IE6 and IE7.", event.line, event.col, rule);
8330
+
}
8331
+
});
8332
+
}
8333
+
8334
+
});
8335
+
8336
+
/*
8337
+
* Rule: Use the bulletproof @font-face syntax to avoid 404's in old IE
8338
+
* (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax)
8339
+
*/
8340
+
8341
+
CSSLint.addRule({
8342
+
8343
+
// rule information
8344
+
id: "bulletproof-font-face",
8345
+
name: "Use the bulletproof @font-face syntax",
8346
+
desc: "Use the bulletproof @font-face syntax to avoid 404's in old IE (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax).",
8347
+
url: "https://github.com/CSSLint/csslint/wiki/Bulletproof-font-face",
8348
+
browsers: "All",
8349
+
8350
+
// initialization
8351
+
init: function(parser, reporter) {
8352
+
"use strict";
8353
+
var rule = this,
8354
+
fontFaceRule = false,
8355
+
firstSrc = true,
8356
+
ruleFailed = false,
8357
+
line, col;
8358
+
8359
+
// Mark the start of a @font-face declaration so we only test properties inside it
8360
+
parser.addListener("startfontface", function() {
8361
+
fontFaceRule = true;
8362
+
});
8363
+
8364
+
parser.addListener("property", function(event) {
8365
+
// If we aren't inside an @font-face declaration then just return
8366
+
if (!fontFaceRule) {
8367
+
return;
8368
+
}
8369
+
8370
+
var propertyName = event.property.toString().toLowerCase(),
8371
+
value = event.value.toString();
8372
+
8373
+
// Set the line and col numbers for use in the endfontface listener
8374
+
line = event.line;
8375
+
col = event.col;
8376
+
8377
+
// This is the property that we care about, we can ignore the rest
8378
+
if (propertyName === "src") {
8379
+
var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i;
8380
+
8381
+
// We need to handle the advanced syntax with two src properties
8382
+
if (!value.match(regex) && firstSrc) {
8383
+
ruleFailed = true;
8384
+
firstSrc = false;
8385
+
} else if (value.match(regex) && !firstSrc) {
8386
+
ruleFailed = false;
8387
+
}
8388
+
}
8389
+
8390
+
8391
+
});
8392
+
8393
+
// Back to normal rules that we don't need to test
8394
+
parser.addListener("endfontface", function() {
8395
+
fontFaceRule = false;
8396
+
8397
+
if (ruleFailed) {
8398
+
reporter.report("@font-face declaration doesn't follow the fontspring bulletproof syntax.", line, col, rule);
8399
+
}
8400
+
});
8401
+
}
8402
+
});
8403
+
8404
+
/*
8405
+
* Rule: Include all compatible vendor prefixes to reach a wider
8406
+
* range of users.
8407
+
*/
8408
+
8409
+
CSSLint.addRule({
8410
+
8411
+
// rule information
8412
+
id: "compatible-vendor-prefixes",
8413
+
name: "Require compatible vendor prefixes",
8414
+
desc: "Include all compatible vendor prefixes to reach a wider range of users.",
8415
+
url: "https://github.com/CSSLint/csslint/wiki/Require-compatible-vendor-prefixes",
8416
+
browsers: "All",
8417
+
8418
+
// initialization
8419
+
init: function (parser, reporter) {
8420
+
"use strict";
8421
+
var rule = this,
8422
+
compatiblePrefixes,
8423
+
properties,
8424
+
prop,
8425
+
variations,
8426
+
prefixed,
8427
+
i,
8428
+
len,
8429
+
inKeyFrame = false,
8430
+
arrayPush = Array.prototype.push,
8431
+
applyTo = [];
8432
+
8433
+
// See http://peter.sh/experiments/vendor-prefixed-css-property-overview/ for details
8434
+
compatiblePrefixes = {
8435
+
"animation" : "webkit",
8436
+
"animation-delay" : "webkit",
8437
+
"animation-direction" : "webkit",
8438
+
"animation-duration" : "webkit",
8439
+
"animation-fill-mode" : "webkit",
8440
+
"animation-iteration-count" : "webkit",
8441
+
"animation-name" : "webkit",
8442
+
"animation-play-state" : "webkit",
8443
+
"animation-timing-function" : "webkit",
8444
+
"appearance" : "webkit moz",
8445
+
"border-end" : "webkit moz",
8446
+
"border-end-color" : "webkit moz",
8447
+
"border-end-style" : "webkit moz",
8448
+
"border-end-width" : "webkit moz",
8449
+
"border-image" : "webkit moz o",
8450
+
"border-radius" : "webkit",
8451
+
"border-start" : "webkit moz",
8452
+
"border-start-color" : "webkit moz",
8453
+
"border-start-style" : "webkit moz",
8454
+
"border-start-width" : "webkit moz",
8455
+
"box-align" : "webkit moz ms",
8456
+
"box-direction" : "webkit moz ms",
8457
+
"box-flex" : "webkit moz ms",
8458
+
"box-lines" : "webkit ms",
8459
+
"box-ordinal-group" : "webkit moz ms",
8460
+
"box-orient" : "webkit moz ms",
8461
+
"box-pack" : "webkit moz ms",
8462
+
"box-sizing" : "",
8463
+
"box-shadow" : "",
8464
+
"column-count" : "webkit moz ms",
8465
+
"column-gap" : "webkit moz ms",
8466
+
"column-rule" : "webkit moz ms",
8467
+
"column-rule-color" : "webkit moz ms",
8468
+
"column-rule-style" : "webkit moz ms",
8469
+
"column-rule-width" : "webkit moz ms",
8470
+
"column-width" : "webkit moz ms",
8471
+
"hyphens" : "epub moz",
8472
+
"line-break" : "webkit ms",
8473
+
"margin-end" : "webkit moz",
8474
+
"margin-start" : "webkit moz",
8475
+
"marquee-speed" : "webkit wap",
8476
+
"marquee-style" : "webkit wap",
8477
+
"padding-end" : "webkit moz",
8478
+
"padding-start" : "webkit moz",
8479
+
"tab-size" : "moz o",
8480
+
"text-size-adjust" : "webkit ms",
8481
+
"transform" : "webkit ms",
8482
+
"transform-origin" : "webkit ms",
8483
+
"transition" : "",
8484
+
"transition-delay" : "",
8485
+
"transition-duration" : "",
8486
+
"transition-property" : "",
8487
+
"transition-timing-function" : "",
8488
+
"user-modify" : "webkit moz",
8489
+
"user-select" : "webkit moz ms",
8490
+
"word-break" : "epub ms",
8491
+
"writing-mode" : "epub ms"
8492
+
};
8493
+
8494
+
8495
+
for (prop in compatiblePrefixes) {
8496
+
if (compatiblePrefixes.hasOwnProperty(prop)) {
8497
+
variations = [];
8498
+
prefixed = compatiblePrefixes[prop].split(" ");
8499
+
for (i = 0, len = prefixed.length; i < len; i++) {
8500
+
variations.push("-" + prefixed[i] + "-" + prop);
8501
+
}
8502
+
compatiblePrefixes[prop] = variations;
8503
+
arrayPush.apply(applyTo, variations);
8504
+
}
8505
+
}
8506
+
8507
+
parser.addListener("startrule", function () {
8508
+
properties = [];
8509
+
});
8510
+
8511
+
parser.addListener("startkeyframes", function (event) {
8512
+
inKeyFrame = event.prefix || true;
8513
+
});
8514
+
8515
+
parser.addListener("endkeyframes", function () {
8516
+
inKeyFrame = false;
8517
+
});
8518
+
8519
+
parser.addListener("property", function (event) {
8520
+
var name = event.property;
8521
+
if (CSSLint.Util.indexOf(applyTo, name.text) > -1) {
8522
+
8523
+
// e.g., -moz-transform is okay to be alone in @-moz-keyframes
8524
+
if (!inKeyFrame || typeof inKeyFrame !== "string" ||
8525
+
name.text.indexOf("-" + inKeyFrame + "-") !== 0) {
8526
+
properties.push(name);
8527
+
}
8528
+
}
8529
+
});
8530
+
8531
+
parser.addListener("endrule", function () {
8532
+
if (!properties.length) {
8533
+
return;
8534
+
}
8535
+
8536
+
var propertyGroups = {},
8537
+
i,
8538
+
len,
8539
+
name,
8540
+
prop,
8541
+
variations,
8542
+
value,
8543
+
full,
8544
+
actual,
8545
+
item,
8546
+
propertiesSpecified;
8547
+
8548
+
for (i = 0, len = properties.length; i < len; i++) {
8549
+
name = properties[i];
8550
+
8551
+
for (prop in compatiblePrefixes) {
8552
+
if (compatiblePrefixes.hasOwnProperty(prop)) {
8553
+
variations = compatiblePrefixes[prop];
8554
+
if (CSSLint.Util.indexOf(variations, name.text) > -1) {
8555
+
if (!propertyGroups[prop]) {
8556
+
propertyGroups[prop] = {
8557
+
full: variations.slice(0),
8558
+
actual: [],
8559
+
actualNodes: []
8560
+
};
8561
+
}
8562
+
if (CSSLint.Util.indexOf(propertyGroups[prop].actual, name.text) === -1) {
8563
+
propertyGroups[prop].actual.push(name.text);
8564
+
propertyGroups[prop].actualNodes.push(name);
8565
+
}
8566
+
}
8567
+
}
8568
+
}
8569
+
}
8570
+
8571
+
for (prop in propertyGroups) {
8572
+
if (propertyGroups.hasOwnProperty(prop)) {
8573
+
value = propertyGroups[prop];
8574
+
full = value.full;
8575
+
actual = value.actual;
8576
+
8577
+
if (full.length > actual.length) {
8578
+
for (i = 0, len = full.length; i < len; i++) {
8579
+
item = full[i];
8580
+
if (CSSLint.Util.indexOf(actual, item) === -1) {
8581
+
propertiesSpecified = (actual.length === 1) ? actual[0] : (actual.length === 2) ? actual.join(" and ") : actual.join(", ");
8582
+
reporter.report("The property " + item + " is compatible with " + propertiesSpecified + " and should be included as well.", value.actualNodes[0].line, value.actualNodes[0].col, rule);
8583
+
}
8584
+
}
8585
+
8586
+
}
8587
+
}
8588
+
}
8589
+
});
8590
+
}
8591
+
});
8592
+
8593
+
/*
8594
+
* Rule: Certain properties don't play well with certain display values.
8595
+
* - float should not be used with inline-block
8596
+
* - height, width, margin-top, margin-bottom, float should not be used with inline
8597
+
* - vertical-align should not be used with block
8598
+
* - margin, float should not be used with table-*
8599
+
*/
8600
+
8601
+
CSSLint.addRule({
8602
+
8603
+
// rule information
8604
+
id: "display-property-grouping",
8605
+
name: "Require properties appropriate for display",
8606
+
desc: "Certain properties shouldn't be used with certain display property values.",
8607
+
url: "https://github.com/CSSLint/csslint/wiki/Require-properties-appropriate-for-display",
8608
+
browsers: "All",
8609
+
8610
+
// initialization
8611
+
init: function(parser, reporter) {
8612
+
"use strict";
8613
+
var rule = this;
8614
+
8615
+
var propertiesToCheck = {
8616
+
display: 1,
8617
+
"float": "none",
8618
+
height: 1,
8619
+
width: 1,
8620
+
margin: 1,
8621
+
"margin-left": 1,
8622
+
"margin-right": 1,
8623
+
"margin-bottom": 1,
8624
+
"margin-top": 1,
8625
+
padding: 1,
8626
+
"padding-left": 1,
8627
+
"padding-right": 1,
8628
+
"padding-bottom": 1,
8629
+
"padding-top": 1,
8630
+
"vertical-align": 1
8631
+
},
8632
+
properties;
8633
+
8634
+
function reportProperty(name, display, msg) {
8635
+
if (properties[name]) {
8636
+
if (typeof propertiesToCheck[name] !== "string" || properties[name].value.toLowerCase() !== propertiesToCheck[name]) {
8637
+
reporter.report(msg || name + " can't be used with display: " + display + ".", properties[name].line, properties[name].col, rule);
8638
+
}
8639
+
}
8640
+
}
8641
+
8642
+
function startRule() {
8643
+
properties = {};
8644
+
}
8645
+
8646
+
function endRule() {
8647
+
8648
+
var display = properties.display ? properties.display.value : null;
8649
+
if (display) {
8650
+
switch (display) {
8651
+
8652
+
case "inline":
8653
+
// height, width, margin-top, margin-bottom, float should not be used with inline
8654
+
reportProperty("height", display);
8655
+
reportProperty("width", display);
8656
+
reportProperty("margin", display);
8657
+
reportProperty("margin-top", display);
8658
+
reportProperty("margin-bottom", display);
8659
+
reportProperty("float", display, "display:inline has no effect on floated elements (but may be used to fix the IE6 double-margin bug).");
8660
+
break;
8661
+
8662
+
case "block":
8663
+
// vertical-align should not be used with block
8664
+
reportProperty("vertical-align", display);
8665
+
break;
8666
+
8667
+
case "inline-block":
8668
+
// float should not be used with inline-block
8669
+
reportProperty("float", display);
8670
+
break;
8671
+
8672
+
default:
8673
+
// margin, float should not be used with table
8674
+
if (display.indexOf("table-") === 0) {
8675
+
reportProperty("margin", display);
8676
+
reportProperty("margin-left", display);
8677
+
reportProperty("margin-right", display);
8678
+
reportProperty("margin-top", display);
8679
+
reportProperty("margin-bottom", display);
8680
+
reportProperty("float", display);
8681
+
}
8682
+
8683
+
// otherwise do nothing
8684
+
}
8685
+
}
8686
+
8687
+
}
8688
+
8689
+
parser.addListener("startrule", startRule);
8690
+
parser.addListener("startfontface", startRule);
8691
+
parser.addListener("startkeyframerule", startRule);
8692
+
parser.addListener("startpagemargin", startRule);
8693
+
parser.addListener("startpage", startRule);
8694
+
parser.addListener("startviewport", startRule);
8695
+
8696
+
parser.addListener("property", function(event) {
8697
+
var name = event.property.text.toLowerCase();
8698
+
8699
+
if (propertiesToCheck[name]) {
8700
+
properties[name] = {
8701
+
value: event.value.text,
8702
+
line: event.property.line,
8703
+
col: event.property.col
8704
+
};
8705
+
}
8706
+
});
8707
+
8708
+
parser.addListener("endrule", endRule);
8709
+
parser.addListener("endfontface", endRule);
8710
+
parser.addListener("endkeyframerule", endRule);
8711
+
parser.addListener("endpagemargin", endRule);
8712
+
parser.addListener("endpage", endRule);
8713
+
parser.addListener("endviewport", endRule);
8714
+
8715
+
}
8716
+
8717
+
});
8718
+
8719
+
/*
8720
+
* Rule: Disallow duplicate background-images (using url).
8721
+
*/
8722
+
8723
+
CSSLint.addRule({
8724
+
8725
+
// rule information
8726
+
id: "duplicate-background-images",
8727
+
name: "Disallow duplicate background images",
8728
+
desc: "Every background-image should be unique. Use a common class for e.g. sprites.",
8729
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-background-images",
8730
+
browsers: "All",
8731
+
8732
+
// initialization
8733
+
init: function(parser, reporter) {
8734
+
"use strict";
8735
+
var rule = this,
8736
+
stack = {};
8737
+
8738
+
parser.addListener("property", function(event) {
8739
+
var name = event.property.text,
8740
+
value = event.value,
8741
+
i, len;
8742
+
8743
+
if (name.match(/background/i)) {
8744
+
for (i=0, len=value.parts.length; i < len; i++) {
8745
+
if (value.parts[i].type === "uri") {
8746
+
if (typeof stack[value.parts[i].uri] === "undefined") {
8747
+
stack[value.parts[i].uri] = event;
8748
+
} else {
8749
+
reporter.report("Background image '" + value.parts[i].uri + "' was used multiple times, first declared at line " + stack[value.parts[i].uri].line + ", col " + stack[value.parts[i].uri].col + ".", event.line, event.col, rule);
8750
+
}
8751
+
}
8752
+
}
8753
+
}
8754
+
});
8755
+
}
8756
+
});
8757
+
8758
+
/*
8759
+
* Rule: Duplicate properties must appear one after the other. If an already-defined
8760
+
* property appears somewhere else in the rule, then it's likely an error.
8761
+
*/
8762
+
8763
+
CSSLint.addRule({
8764
+
8765
+
// rule information
8766
+
id: "duplicate-properties",
8767
+
name: "Disallow duplicate properties",
8768
+
desc: "Duplicate properties must appear one after the other.",
8769
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-properties",
8770
+
browsers: "All",
8771
+
8772
+
// initialization
8773
+
init: function(parser, reporter) {
8774
+
"use strict";
8775
+
var rule = this,
8776
+
properties,
8777
+
lastProperty;
8778
+
8779
+
function startRule() {
8780
+
properties = {};
8781
+
}
8782
+
8783
+
parser.addListener("startrule", startRule);
8784
+
parser.addListener("startfontface", startRule);
8785
+
parser.addListener("startpage", startRule);
8786
+
parser.addListener("startpagemargin", startRule);
8787
+
parser.addListener("startkeyframerule", startRule);
8788
+
parser.addListener("startviewport", startRule);
8789
+
8790
+
parser.addListener("property", function(event) {
8791
+
var property = event.property,
8792
+
name = property.text.toLowerCase();
8793
+
8794
+
if (properties[name] && (lastProperty !== name || properties[name] === event.value.text)) {
8795
+
reporter.report("Duplicate property '" + event.property + "' found.", event.line, event.col, rule);
8796
+
}
8797
+
8798
+
properties[name] = event.value.text;
8799
+
lastProperty = name;
8800
+
8801
+
});
8802
+
8803
+
8804
+
}
8805
+
8806
+
});
8807
+
8808
+
/*
8809
+
* Rule: Style rules without any properties defined should be removed.
8810
+
*/
8811
+
8812
+
CSSLint.addRule({
8813
+
8814
+
// rule information
8815
+
id: "empty-rules",
8816
+
name: "Disallow empty rules",
8817
+
desc: "Rules without any properties specified should be removed.",
8818
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-empty-rules",
8819
+
browsers: "All",
8820
+
8821
+
// initialization
8822
+
init: function(parser, reporter) {
8823
+
"use strict";
8824
+
var rule = this,
8825
+
count = 0;
8826
+
8827
+
parser.addListener("startrule", function() {
8828
+
count=0;
8829
+
});
8830
+
8831
+
parser.addListener("property", function() {
8832
+
count++;
8833
+
});
8834
+
8835
+
parser.addListener("endrule", function(event) {
8836
+
var selectors = event.selectors;
8837
+
if (count === 0) {
8838
+
reporter.report("Rule is empty.", selectors[0].line, selectors[0].col, rule);
8839
+
}
8840
+
});
8841
+
}
8842
+
8843
+
});
8844
+
8845
+
/*
8846
+
* Rule: There should be no syntax errors. (Duh.)
8847
+
*/
8848
+
8849
+
CSSLint.addRule({
8850
+
8851
+
// rule information
8852
+
id: "errors",
8853
+
name: "Parsing Errors",
8854
+
desc: "This rule looks for recoverable syntax errors.",
8855
+
browsers: "All",
8856
+
8857
+
// initialization
8858
+
init: function(parser, reporter) {
8859
+
"use strict";
8860
+
var rule = this;
8861
+
8862
+
parser.addListener("error", function(event) {
8863
+
reporter.error(event.message, event.line, event.col, rule);
8864
+
});
8865
+
8866
+
}
8867
+
8868
+
});
8869
+
8870
+
CSSLint.addRule({
8871
+
8872
+
// rule information
8873
+
id: "fallback-colors",
8874
+
name: "Require fallback colors",
8875
+
desc: "For older browsers that don't support RGBA, HSL, or HSLA, provide a fallback color.",
8876
+
url: "https://github.com/CSSLint/csslint/wiki/Require-fallback-colors",
8877
+
browsers: "IE6,IE7,IE8",
8878
+
8879
+
// initialization
8880
+
init: function(parser, reporter) {
8881
+
"use strict";
8882
+
var rule = this,
8883
+
lastProperty,
8884
+
propertiesToCheck = {
8885
+
color: 1,
8886
+
background: 1,
8887
+
"border-color": 1,
8888
+
"border-top-color": 1,
8889
+
"border-right-color": 1,
8890
+
"border-bottom-color": 1,
8891
+
"border-left-color": 1,
8892
+
border: 1,
8893
+
"border-top": 1,
8894
+
"border-right": 1,
8895
+
"border-bottom": 1,
8896
+
"border-left": 1,
8897
+
"background-color": 1
8898
+
};
8899
+
8900
+
function startRule() {
8901
+
lastProperty = null;
8902
+
}
8903
+
8904
+
parser.addListener("startrule", startRule);
8905
+
parser.addListener("startfontface", startRule);
8906
+
parser.addListener("startpage", startRule);
8907
+
parser.addListener("startpagemargin", startRule);
8908
+
parser.addListener("startkeyframerule", startRule);
8909
+
parser.addListener("startviewport", startRule);
8910
+
8911
+
parser.addListener("property", function(event) {
8912
+
var property = event.property,
8913
+
name = property.text.toLowerCase(),
8914
+
parts = event.value.parts,
8915
+
i = 0,
8916
+
colorType = "",
8917
+
len = parts.length;
8918
+
8919
+
if (propertiesToCheck[name]) {
8920
+
while (i < len) {
8921
+
if (parts[i].type === "color") {
8922
+
if ("alpha" in parts[i] || "hue" in parts[i]) {
8923
+
8924
+
if (/([^\)]+)\(/.test(parts[i])) {
8925
+
colorType = RegExp.$1.toUpperCase();
8926
+
}
8927
+
8928
+
if (!lastProperty || (lastProperty.property.text.toLowerCase() !== name || lastProperty.colorType !== "compat")) {
8929
+
reporter.report("Fallback " + name + " (hex or RGB) should precede " + colorType + " " + name + ".", event.line, event.col, rule);
8930
+
}
8931
+
} else {
8932
+
event.colorType = "compat";
8933
+
}
8934
+
}
8935
+
8936
+
i++;
8937
+
}
8938
+
}
8939
+
8940
+
lastProperty = event;
8941
+
});
8942
+
8943
+
}
8944
+
8945
+
});
8946
+
8947
+
/*
8948
+
* Rule: You shouldn't use more than 10 floats. If you do, there's probably
8949
+
* room for some abstraction.
8950
+
*/
8951
+
8952
+
CSSLint.addRule({
8953
+
8954
+
// rule information
8955
+
id: "floats",
8956
+
name: "Disallow too many floats",
8957
+
desc: "This rule tests if the float property is used too many times",
8958
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-too-many-floats",
8959
+
browsers: "All",
8960
+
8961
+
// initialization
8962
+
init: function(parser, reporter) {
8963
+
"use strict";
8964
+
var rule = this;
8965
+
var count = 0;
8966
+
8967
+
// count how many times "float" is used
8968
+
parser.addListener("property", function(event) {
8969
+
if (event.property.text.toLowerCase() === "float" &&
8970
+
event.value.text.toLowerCase() !== "none") {
8971
+
count++;
8972
+
}
8973
+
});
8974
+
8975
+
// report the results
8976
+
parser.addListener("endstylesheet", function() {
8977
+
reporter.stat("floats", count);
8978
+
if (count >= 10) {
8979
+
reporter.rollupWarn("Too many floats (" + count + "), you're probably using them for layout. Consider using a grid system instead.", rule);
8980
+
}
8981
+
});
8982
+
}
8983
+
8984
+
});
8985
+
8986
+
/*
8987
+
* Rule: Avoid too many @font-face declarations in the same stylesheet.
8988
+
*/
8989
+
8990
+
CSSLint.addRule({
8991
+
8992
+
// rule information
8993
+
id: "font-faces",
8994
+
name: "Don't use too many web fonts",
8995
+
desc: "Too many different web fonts in the same stylesheet.",
8996
+
url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-web-fonts",
8997
+
browsers: "All",
8998
+
8999
+
// initialization
9000
+
init: function(parser, reporter) {
9001
+
"use strict";
9002
+
var rule = this,
9003
+
count = 0;
9004
+
9005
+
9006
+
parser.addListener("startfontface", function() {
9007
+
count++;
9008
+
});
9009
+
9010
+
parser.addListener("endstylesheet", function() {
9011
+
if (count > 5) {
9012
+
reporter.rollupWarn("Too many @font-face declarations (" + count + ").", rule);
9013
+
}
9014
+
});
9015
+
}
9016
+
9017
+
});
9018
+
9019
+
/*
9020
+
* Rule: You shouldn't need more than 9 font-size declarations.
9021
+
*/
9022
+
9023
+
CSSLint.addRule({
9024
+
9025
+
// rule information
9026
+
id: "font-sizes",
9027
+
name: "Disallow too many font sizes",
9028
+
desc: "Checks the number of font-size declarations.",
9029
+
url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-font-size-declarations",
9030
+
browsers: "All",
9031
+
9032
+
// initialization
9033
+
init: function(parser, reporter) {
9034
+
"use strict";
9035
+
var rule = this,
9036
+
count = 0;
9037
+
9038
+
// check for use of "font-size"
9039
+
parser.addListener("property", function(event) {
9040
+
if (event.property.toString() === "font-size") {
9041
+
count++;
9042
+
}
9043
+
});
9044
+
9045
+
// report the results
9046
+
parser.addListener("endstylesheet", function() {
9047
+
reporter.stat("font-sizes", count);
9048
+
if (count >= 10) {
9049
+
reporter.rollupWarn("Too many font-size declarations (" + count + "), abstraction needed.", rule);
9050
+
}
9051
+
});
9052
+
}
9053
+
9054
+
});
9055
+
9056
+
/*
9057
+
* Rule: When using a vendor-prefixed gradient, make sure to use them all.
9058
+
*/
9059
+
9060
+
CSSLint.addRule({
9061
+
9062
+
// rule information
9063
+
id: "gradients",
9064
+
name: "Require all gradient definitions",
9065
+
desc: "When using a vendor-prefixed gradient, make sure to use them all.",
9066
+
url: "https://github.com/CSSLint/csslint/wiki/Require-all-gradient-definitions",
9067
+
browsers: "All",
9068
+
9069
+
// initialization
9070
+
init: function(parser, reporter) {
9071
+
"use strict";
9072
+
var rule = this,
9073
+
gradients;
9074
+
9075
+
parser.addListener("startrule", function() {
9076
+
gradients = {
9077
+
moz: 0,
9078
+
webkit: 0,
9079
+
oldWebkit: 0,
9080
+
o: 0
9081
+
};
9082
+
});
9083
+
9084
+
parser.addListener("property", function(event) {
9085
+
9086
+
if (/\-(moz|o|webkit)(?:\-(?:linear|radial))\-gradient/i.test(event.value)) {
9087
+
gradients[RegExp.$1] = 1;
9088
+
} else if (/\-webkit\-gradient/i.test(event.value)) {
9089
+
gradients.oldWebkit = 1;
9090
+
}
9091
+
9092
+
});
9093
+
9094
+
parser.addListener("endrule", function(event) {
9095
+
var missing = [];
9096
+
9097
+
if (!gradients.moz) {
9098
+
missing.push("Firefox 3.6+");
9099
+
}
9100
+
9101
+
if (!gradients.webkit) {
9102
+
missing.push("Webkit (Safari 5+, Chrome)");
9103
+
}
9104
+
9105
+
if (!gradients.oldWebkit) {
9106
+
missing.push("Old Webkit (Safari 4+, Chrome)");
9107
+
}
9108
+
9109
+
if (!gradients.o) {
9110
+
missing.push("Opera 11.1+");
9111
+
}
9112
+
9113
+
if (missing.length && missing.length < 4) {
9114
+
reporter.report("Missing vendor-prefixed CSS gradients for " + missing.join(", ") + ".", event.selectors[0].line, event.selectors[0].col, rule);
9115
+
}
9116
+
9117
+
});
9118
+
9119
+
}
9120
+
9121
+
});
9122
+
9123
+
/*
9124
+
* Rule: Don't use IDs for selectors.
9125
+
*/
9126
+
9127
+
CSSLint.addRule({
9128
+
9129
+
// rule information
9130
+
id: "ids",
9131
+
name: "Disallow IDs in selectors",
9132
+
desc: "Selectors should not contain IDs.",
9133
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-IDs-in-selectors",
9134
+
browsers: "All",
9135
+
9136
+
// initialization
9137
+
init: function(parser, reporter) {
9138
+
"use strict";
9139
+
var rule = this;
9140
+
parser.addListener("startrule", function(event) {
9141
+
var selectors = event.selectors,
9142
+
selector,
9143
+
part,
9144
+
modifier,
9145
+
idCount,
9146
+
i, j, k;
9147
+
9148
+
for (i=0; i < selectors.length; i++) {
9149
+
selector = selectors[i];
9150
+
idCount = 0;
9151
+
9152
+
for (j=0; j < selector.parts.length; j++) {
9153
+
part = selector.parts[j];
9154
+
if (part.type === parser.SELECTOR_PART_TYPE) {
9155
+
for (k=0; k < part.modifiers.length; k++) {
9156
+
modifier = part.modifiers[k];
9157
+
if (modifier.type === "id") {
9158
+
idCount++;
9159
+
}
9160
+
}
9161
+
}
9162
+
}
9163
+
9164
+
if (idCount === 1) {
9165
+
reporter.report("Don't use IDs in selectors.", selector.line, selector.col, rule);
9166
+
} else if (idCount > 1) {
9167
+
reporter.report(idCount + " IDs in the selector, really?", selector.line, selector.col, rule);
9168
+
}
9169
+
}
9170
+
9171
+
});
9172
+
}
9173
+
9174
+
});
9175
+
9176
+
/*
9177
+
* Rule: IE6-9 supports up to 31 stylesheet import.
9178
+
* Reference:
9179
+
* http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/internet-explorer-stylesheet-rule-selector-import-sheet-limit-maximum.aspx
9180
+
*/
9181
+
9182
+
CSSLint.addRule({
9183
+
9184
+
// rule information
9185
+
id: "import-ie-limit",
9186
+
name: "@import limit on IE6-IE9",
9187
+
desc: "IE6-9 supports up to 31 @import per stylesheet",
9188
+
browsers: "IE6, IE7, IE8, IE9",
9189
+
9190
+
// initialization
9191
+
init: function(parser, reporter) {
9192
+
"use strict";
9193
+
var rule = this,
9194
+
MAX_IMPORT_COUNT = 31,
9195
+
count = 0;
9196
+
9197
+
function startPage() {
9198
+
count = 0;
9199
+
}
9200
+
9201
+
parser.addListener("startpage", startPage);
9202
+
9203
+
parser.addListener("import", function() {
9204
+
count++;
9205
+
});
9206
+
9207
+
parser.addListener("endstylesheet", function() {
9208
+
if (count > MAX_IMPORT_COUNT) {
9209
+
reporter.rollupError(
9210
+
"Too many @import rules (" + count + "). IE6-9 supports up to 31 import per stylesheet.",
9211
+
rule
9212
+
);
9213
+
}
9214
+
});
9215
+
}
9216
+
9217
+
});
9218
+
9219
+
/*
9220
+
* Rule: Don't use @import, use <link> instead.
9221
+
*/
9222
+
9223
+
CSSLint.addRule({
9224
+
9225
+
// rule information
9226
+
id: "import",
9227
+
name: "Disallow @import",
9228
+
desc: "Don't use @import, use <link> instead.",
9229
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-%40import",
9230
+
browsers: "All",
9231
+
9232
+
// initialization
9233
+
init: function(parser, reporter) {
9234
+
"use strict";
9235
+
var rule = this;
9236
+
9237
+
parser.addListener("import", function(event) {
9238
+
reporter.report("@import prevents parallel downloads, use <link> instead.", event.line, event.col, rule);
9239
+
});
9240
+
9241
+
}
9242
+
9243
+
});
9244
+
9245
+
/*
9246
+
* Rule: Make sure !important is not overused, this could lead to specificity
9247
+
* war. Display a warning on !important declarations, an error if it's
9248
+
* used more at least 10 times.
9249
+
*/
9250
+
9251
+
CSSLint.addRule({
9252
+
9253
+
// rule information
9254
+
id: "important",
9255
+
name: "Disallow !important",
9256
+
desc: "Be careful when using !important declaration",
9257
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-%21important",
9258
+
browsers: "All",
9259
+
9260
+
// initialization
9261
+
init: function(parser, reporter) {
9262
+
"use strict";
9263
+
var rule = this,
9264
+
count = 0;
9265
+
9266
+
// warn that important is used and increment the declaration counter
9267
+
parser.addListener("property", function(event) {
9268
+
if (event.important === true) {
9269
+
count++;
9270
+
reporter.report("Use of !important", event.line, event.col, rule);
9271
+
}
9272
+
});
9273
+
9274
+
// if there are more than 10, show an error
9275
+
parser.addListener("endstylesheet", function() {
9276
+
reporter.stat("important", count);
9277
+
if (count >= 10) {
9278
+
reporter.rollupWarn("Too many !important declarations (" + count + "), try to use less than 10 to avoid specificity issues.", rule);
9279
+
}
9280
+
});
9281
+
}
9282
+
9283
+
});
9284
+
9285
+
/*
9286
+
* Rule: Properties should be known (listed in CSS3 specification) or
9287
+
* be a vendor-prefixed property.
9288
+
*/
9289
+
9290
+
CSSLint.addRule({
9291
+
9292
+
// rule information
9293
+
id: "known-properties",
9294
+
name: "Require use of known properties",
9295
+
desc: "Properties should be known (listed in CSS3 specification) or be a vendor-prefixed property.",
9296
+
url: "https://github.com/CSSLint/csslint/wiki/Require-use-of-known-properties",
9297
+
browsers: "All",
9298
+
9299
+
// initialization
9300
+
init: function(parser, reporter) {
9301
+
"use strict";
9302
+
var rule = this;
9303
+
9304
+
parser.addListener("property", function(event) {
9305
+
9306
+
// the check is handled entirely by the parser-lib (https://github.com/nzakas/parser-lib)
9307
+
if (event.invalid) {
9308
+
reporter.report(event.invalid.message, event.line, event.col, rule);
9309
+
}
9310
+
9311
+
});
9312
+
}
9313
+
9314
+
});
9315
+
9316
+
/*
9317
+
* Rule: All properties should be in alphabetical order.
9318
+
*/
9319
+
9320
+
CSSLint.addRule({
9321
+
9322
+
// rule information
9323
+
id: "order-alphabetical",
9324
+
name: "Alphabetical order",
9325
+
desc: "Assure properties are in alphabetical order",
9326
+
browsers: "All",
9327
+
9328
+
// initialization
9329
+
init: function(parser, reporter) {
9330
+
"use strict";
9331
+
var rule = this,
9332
+
properties;
9333
+
9334
+
var startRule = function () {
9335
+
properties = [];
9336
+
};
9337
+
9338
+
var endRule = function(event) {
9339
+
var currentProperties = properties.join(","),
9340
+
expectedProperties = properties.sort().join(",");
9341
+
9342
+
if (currentProperties !== expectedProperties) {
9343
+
reporter.report("Rule doesn't have all its properties in alphabetical order.", event.line, event.col, rule);
9344
+
}
9345
+
};
9346
+
9347
+
parser.addListener("startrule", startRule);
9348
+
parser.addListener("startfontface", startRule);
9349
+
parser.addListener("startpage", startRule);
9350
+
parser.addListener("startpagemargin", startRule);
9351
+
parser.addListener("startkeyframerule", startRule);
9352
+
parser.addListener("startviewport", startRule);
9353
+
9354
+
parser.addListener("property", function(event) {
9355
+
var name = event.property.text,
9356
+
lowerCasePrefixLessName = name.toLowerCase().replace(/^-.*?-/, "");
9357
+
9358
+
properties.push(lowerCasePrefixLessName);
9359
+
});
9360
+
9361
+
parser.addListener("endrule", endRule);
9362
+
parser.addListener("endfontface", endRule);
9363
+
parser.addListener("endpage", endRule);
9364
+
parser.addListener("endpagemargin", endRule);
9365
+
parser.addListener("endkeyframerule", endRule);
9366
+
parser.addListener("endviewport", endRule);
9367
+
}
9368
+
9369
+
});
9370
+
9371
+
/*
9372
+
* Rule: outline: none or outline: 0 should only be used in a :focus rule
9373
+
* and only if there are other properties in the same rule.
9374
+
*/
9375
+
9376
+
CSSLint.addRule({
9377
+
9378
+
// rule information
9379
+
id: "outline-none",
9380
+
name: "Disallow outline: none",
9381
+
desc: "Use of outline: none or outline: 0 should be limited to :focus rules.",
9382
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-outline%3Anone",
9383
+
browsers: "All",
9384
+
tags: ["Accessibility"],
9385
+
9386
+
// initialization
9387
+
init: function(parser, reporter) {
9388
+
"use strict";
9389
+
var rule = this,
9390
+
lastRule;
9391
+
9392
+
function startRule(event) {
9393
+
if (event.selectors) {
9394
+
lastRule = {
9395
+
line: event.line,
9396
+
col: event.col,
9397
+
selectors: event.selectors,
9398
+
propCount: 0,
9399
+
outline: false
9400
+
};
9401
+
} else {
9402
+
lastRule = null;
9403
+
}
9404
+
}
9405
+
9406
+
function endRule() {
9407
+
if (lastRule) {
9408
+
if (lastRule.outline) {
9409
+
if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") === -1) {
9410
+
reporter.report("Outlines should only be modified using :focus.", lastRule.line, lastRule.col, rule);
9411
+
} else if (lastRule.propCount === 1) {
9412
+
reporter.report("Outlines shouldn't be hidden unless other visual changes are made.", lastRule.line, lastRule.col, rule);
9413
+
}
9414
+
}
9415
+
}
9416
+
}
9417
+
9418
+
parser.addListener("startrule", startRule);
9419
+
parser.addListener("startfontface", startRule);
9420
+
parser.addListener("startpage", startRule);
9421
+
parser.addListener("startpagemargin", startRule);
9422
+
parser.addListener("startkeyframerule", startRule);
9423
+
parser.addListener("startviewport", startRule);
9424
+
9425
+
parser.addListener("property", function(event) {
9426
+
var name = event.property.text.toLowerCase(),
9427
+
value = event.value;
9428
+
9429
+
if (lastRule) {
9430
+
lastRule.propCount++;
9431
+
if (name === "outline" && (value.toString() === "none" || value.toString() === "0")) {
9432
+
lastRule.outline = true;
9433
+
}
9434
+
}
9435
+
9436
+
});
9437
+
9438
+
parser.addListener("endrule", endRule);
9439
+
parser.addListener("endfontface", endRule);
9440
+
parser.addListener("endpage", endRule);
9441
+
parser.addListener("endpagemargin", endRule);
9442
+
parser.addListener("endkeyframerule", endRule);
9443
+
parser.addListener("endviewport", endRule);
9444
+
9445
+
}
9446
+
9447
+
});
9448
+
9449
+
/*
9450
+
* Rule: Don't use classes or IDs with elements (a.foo or a#foo).
9451
+
*/
9452
+
9453
+
CSSLint.addRule({
9454
+
9455
+
// rule information
9456
+
id: "overqualified-elements",
9457
+
name: "Disallow overqualified elements",
9458
+
desc: "Don't use classes or IDs with elements (a.foo or a#foo).",
9459
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-overqualified-elements",
9460
+
browsers: "All",
9461
+
9462
+
// initialization
9463
+
init: function(parser, reporter) {
9464
+
"use strict";
9465
+
var rule = this,
9466
+
classes = {};
9467
+
9468
+
parser.addListener("startrule", function(event) {
9469
+
var selectors = event.selectors,
9470
+
selector,
9471
+
part,
9472
+
modifier,
9473
+
i, j, k;
9474
+
9475
+
for (i=0; i < selectors.length; i++) {
9476
+
selector = selectors[i];
9477
+
9478
+
for (j=0; j < selector.parts.length; j++) {
9479
+
part = selector.parts[j];
9480
+
if (part.type === parser.SELECTOR_PART_TYPE) {
9481
+
for (k=0; k < part.modifiers.length; k++) {
9482
+
modifier = part.modifiers[k];
9483
+
if (part.elementName && modifier.type === "id") {
9484
+
reporter.report("Element (" + part + ") is overqualified, just use " + modifier + " without element name.", part.line, part.col, rule);
9485
+
} else if (modifier.type === "class") {
9486
+
9487
+
if (!classes[modifier]) {
9488
+
classes[modifier] = [];
9489
+
}
9490
+
classes[modifier].push({
9491
+
modifier: modifier,
9492
+
part: part
9493
+
});
9494
+
}
9495
+
}
9496
+
}
9497
+
}
9498
+
}
9499
+
});
9500
+
9501
+
parser.addListener("endstylesheet", function() {
9502
+
9503
+
var prop;
9504
+
for (prop in classes) {
9505
+
if (classes.hasOwnProperty(prop)) {
9506
+
9507
+
// one use means that this is overqualified
9508
+
if (classes[prop].length === 1 && classes[prop][0].part.elementName) {
9509
+
reporter.report("Element (" + classes[prop][0].part + ") is overqualified, just use " + classes[prop][0].modifier + " without element name.", classes[prop][0].part.line, classes[prop][0].part.col, rule);
9510
+
}
9511
+
}
9512
+
}
9513
+
});
9514
+
}
9515
+
9516
+
});
9517
+
9518
+
/*
9519
+
* Rule: Headings (h1-h6) should not be qualified (namespaced).
9520
+
*/
9521
+
9522
+
CSSLint.addRule({
9523
+
9524
+
// rule information
9525
+
id: "qualified-headings",
9526
+
name: "Disallow qualified headings",
9527
+
desc: "Headings should not be qualified (namespaced).",
9528
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-qualified-headings",
9529
+
browsers: "All",
9530
+
9531
+
// initialization
9532
+
init: function(parser, reporter) {
9533
+
"use strict";
9534
+
var rule = this;
9535
+
9536
+
parser.addListener("startrule", function(event) {
9537
+
var selectors = event.selectors,
9538
+
selector,
9539
+
part,
9540
+
i, j;
9541
+
9542
+
for (i=0; i < selectors.length; i++) {
9543
+
selector = selectors[i];
9544
+
9545
+
for (j=0; j < selector.parts.length; j++) {
9546
+
part = selector.parts[j];
9547
+
if (part.type === parser.SELECTOR_PART_TYPE) {
9548
+
if (part.elementName && /h[1-6]/.test(part.elementName.toString()) && j > 0) {
9549
+
reporter.report("Heading (" + part.elementName + ") should not be qualified.", part.line, part.col, rule);
9550
+
}
9551
+
}
9552
+
}
9553
+
}
9554
+
});
9555
+
}
9556
+
9557
+
});
9558
+
9559
+
/*
9560
+
* Rule: Selectors that look like regular expressions are slow and should be avoided.
9561
+
*/
9562
+
9563
+
CSSLint.addRule({
9564
+
9565
+
// rule information
9566
+
id: "regex-selectors",
9567
+
name: "Disallow selectors that look like regexs",
9568
+
desc: "Selectors that look like regular expressions are slow and should be avoided.",
9569
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-selectors-that-look-like-regular-expressions",
9570
+
browsers: "All",
9571
+
9572
+
// initialization
9573
+
init: function(parser, reporter) {
9574
+
"use strict";
9575
+
var rule = this;
9576
+
9577
+
parser.addListener("startrule", function(event) {
9578
+
var selectors = event.selectors,
9579
+
selector,
9580
+
part,
9581
+
modifier,
9582
+
i, j, k;
9583
+
9584
+
for (i=0; i < selectors.length; i++) {
9585
+
selector = selectors[i];
9586
+
for (j=0; j < selector.parts.length; j++) {
9587
+
part = selector.parts[j];
9588
+
if (part.type === parser.SELECTOR_PART_TYPE) {
9589
+
for (k=0; k < part.modifiers.length; k++) {
9590
+
modifier = part.modifiers[k];
9591
+
if (modifier.type === "attribute") {
9592
+
if (/([~\|\^\$\*]=)/.test(modifier)) {
9593
+
reporter.report("Attribute selectors with " + RegExp.$1 + " are slow!", modifier.line, modifier.col, rule);
9594
+
}
9595
+
}
9596
+
9597
+
}
9598
+
}
9599
+
}
9600
+
}
9601
+
});
9602
+
}
9603
+
9604
+
});
9605
+
9606
+
/*
9607
+
* Rule: Total number of rules should not exceed x.
9608
+
*/
9609
+
9610
+
CSSLint.addRule({
9611
+
9612
+
// rule information
9613
+
id: "rules-count",
9614
+
name: "Rules Count",
9615
+
desc: "Track how many rules there are.",
9616
+
browsers: "All",
9617
+
9618
+
// initialization
9619
+
init: function(parser, reporter) {
9620
+
"use strict";
9621
+
var count = 0;
9622
+
9623
+
// count each rule
9624
+
parser.addListener("startrule", function() {
9625
+
count++;
9626
+
});
9627
+
9628
+
parser.addListener("endstylesheet", function() {
9629
+
reporter.stat("rule-count", count);
9630
+
});
9631
+
}
9632
+
9633
+
});
9634
+
9635
+
/*
9636
+
* Rule: Warn people with approaching the IE 4095 limit
9637
+
*/
9638
+
9639
+
CSSLint.addRule({
9640
+
9641
+
// rule information
9642
+
id: "selector-max-approaching",
9643
+
name: "Warn when approaching the 4095 selector limit for IE",
9644
+
desc: "Will warn when selector count is >= 3800 selectors.",
9645
+
browsers: "IE",
9646
+
9647
+
// initialization
9648
+
init: function(parser, reporter) {
9649
+
"use strict";
9650
+
var rule = this, count = 0;
9651
+
9652
+
parser.addListener("startrule", function(event) {
9653
+
count += event.selectors.length;
9654
+
});
9655
+
9656
+
parser.addListener("endstylesheet", function() {
9657
+
if (count >= 3800) {
9658
+
reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule);
9659
+
}
9660
+
});
9661
+
}
9662
+
9663
+
});
9664
+
9665
+
/*
9666
+
* Rule: Warn people past the IE 4095 limit
9667
+
*/
9668
+
9669
+
CSSLint.addRule({
9670
+
9671
+
// rule information
9672
+
id: "selector-max",
9673
+
name: "Error when past the 4095 selector limit for IE",
9674
+
desc: "Will error when selector count is > 4095.",
9675
+
browsers: "IE",
9676
+
9677
+
// initialization
9678
+
init: function(parser, reporter) {
9679
+
"use strict";
9680
+
var rule = this, count = 0;
9681
+
9682
+
parser.addListener("startrule", function(event) {
9683
+
count += event.selectors.length;
9684
+
});
9685
+
9686
+
parser.addListener("endstylesheet", function() {
9687
+
if (count > 4095) {
9688
+
reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule);
9689
+
}
9690
+
});
9691
+
}
9692
+
9693
+
});
9694
+
9695
+
/*
9696
+
* Rule: Avoid new-line characters in selectors.
9697
+
*/
9698
+
9699
+
CSSLint.addRule({
9700
+
9701
+
// rule information
9702
+
id: "selector-newline",
9703
+
name: "Disallow new-line characters in selectors",
9704
+
desc: "New-line characters in selectors are usually a forgotten comma and not a descendant combinator.",
9705
+
browsers: "All",
9706
+
9707
+
// initialization
9708
+
init: function(parser, reporter) {
9709
+
"use strict";
9710
+
var rule = this;
9711
+
9712
+
function startRule(event) {
9713
+
var i, len, selector, p, n, pLen, part, part2, type, currentLine, nextLine,
9714
+
selectors = event.selectors;
9715
+
9716
+
for (i = 0, len = selectors.length; i < len; i++) {
9717
+
selector = selectors[i];
9718
+
for (p = 0, pLen = selector.parts.length; p < pLen; p++) {
9719
+
for (n = p + 1; n < pLen; n++) {
9720
+
part = selector.parts[p];
9721
+
part2 = selector.parts[n];
9722
+
type = part.type;
9723
+
currentLine = part.line;
9724
+
nextLine = part2.line;
9725
+
9726
+
if (type === "descendant" && nextLine > currentLine) {
9727
+
reporter.report("newline character found in selector (forgot a comma?)", currentLine, selectors[i].parts[0].col, rule);
9728
+
}
9729
+
}
9730
+
}
9731
+
9732
+
}
9733
+
}
9734
+
9735
+
parser.addListener("startrule", startRule);
9736
+
9737
+
}
9738
+
});
9739
+
9740
+
/*
9741
+
* Rule: Use shorthand properties where possible.
9742
+
*
9743
+
*/
9744
+
9745
+
CSSLint.addRule({
9746
+
9747
+
// rule information
9748
+
id: "shorthand",
9749
+
name: "Require shorthand properties",
9750
+
desc: "Use shorthand properties where possible.",
9751
+
url: "https://github.com/CSSLint/csslint/wiki/Require-shorthand-properties",
9752
+
browsers: "All",
9753
+
9754
+
// initialization
9755
+
init: function(parser, reporter) {
9756
+
"use strict";
9757
+
var rule = this,
9758
+
prop, i, len,
9759
+
propertiesToCheck = {},
9760
+
properties,
9761
+
mapping = {
9762
+
"margin": [
9763
+
"margin-top",
9764
+
"margin-bottom",
9765
+
"margin-left",
9766
+
"margin-right"
9767
+
],
9768
+
"padding": [
9769
+
"padding-top",
9770
+
"padding-bottom",
9771
+
"padding-left",
9772
+
"padding-right"
9773
+
]
9774
+
};
9775
+
9776
+
// initialize propertiesToCheck
9777
+
for (prop in mapping) {
9778
+
if (mapping.hasOwnProperty(prop)) {
9779
+
for (i=0, len=mapping[prop].length; i < len; i++) {
9780
+
propertiesToCheck[mapping[prop][i]] = prop;
9781
+
}
9782
+
}
9783
+
}
9784
+
9785
+
function startRule() {
9786
+
properties = {};
9787
+
}
9788
+
9789
+
// event handler for end of rules
9790
+
function endRule(event) {
9791
+
9792
+
var prop, i, len, total;
9793
+
9794
+
// check which properties this rule has
9795
+
for (prop in mapping) {
9796
+
if (mapping.hasOwnProperty(prop)) {
9797
+
total=0;
9798
+
9799
+
for (i=0, len=mapping[prop].length; i < len; i++) {
9800
+
total += properties[mapping[prop][i]] ? 1 : 0;
9801
+
}
9802
+
9803
+
if (total === mapping[prop].length) {
9804
+
reporter.report("The properties " + mapping[prop].join(", ") + " can be replaced by " + prop + ".", event.line, event.col, rule);
9805
+
}
9806
+
}
9807
+
}
9808
+
}
9809
+
9810
+
parser.addListener("startrule", startRule);
9811
+
parser.addListener("startfontface", startRule);
9812
+
9813
+
// check for use of "font-size"
9814
+
parser.addListener("property", function(event) {
9815
+
var name = event.property.toString().toLowerCase();
9816
+
9817
+
if (propertiesToCheck[name]) {
9818
+
properties[name] = 1;
9819
+
}
9820
+
});
9821
+
9822
+
parser.addListener("endrule", endRule);
9823
+
parser.addListener("endfontface", endRule);
9824
+
9825
+
}
9826
+
9827
+
});
9828
+
9829
+
/*
9830
+
* Rule: Don't use properties with a star prefix.
9831
+
*
9832
+
*/
9833
+
9834
+
CSSLint.addRule({
9835
+
9836
+
// rule information
9837
+
id: "star-property-hack",
9838
+
name: "Disallow properties with a star prefix",
9839
+
desc: "Checks for the star property hack (targets IE6/7)",
9840
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-star-hack",
9841
+
browsers: "All",
9842
+
9843
+
// initialization
9844
+
init: function(parser, reporter) {
9845
+
"use strict";
9846
+
var rule = this;
9847
+
9848
+
// check if property name starts with "*"
9849
+
parser.addListener("property", function(event) {
9850
+
var property = event.property;
9851
+
9852
+
if (property.hack === "*") {
9853
+
reporter.report("Property with star prefix found.", event.property.line, event.property.col, rule);
9854
+
}
9855
+
});
9856
+
}
9857
+
});
9858
+
9859
+
/*
9860
+
* Rule: Don't use text-indent for image replacement if you need to support rtl.
9861
+
*
9862
+
*/
9863
+
9864
+
CSSLint.addRule({
9865
+
9866
+
// rule information
9867
+
id: "text-indent",
9868
+
name: "Disallow negative text-indent",
9869
+
desc: "Checks for text indent less than -99px",
9870
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-negative-text-indent",
9871
+
browsers: "All",
9872
+
9873
+
// initialization
9874
+
init: function(parser, reporter) {
9875
+
"use strict";
9876
+
var rule = this,
9877
+
textIndent,
9878
+
direction;
9879
+
9880
+
9881
+
function startRule() {
9882
+
textIndent = false;
9883
+
direction = "inherit";
9884
+
}
9885
+
9886
+
// event handler for end of rules
9887
+
function endRule() {
9888
+
if (textIndent && direction !== "ltr") {
9889
+
reporter.report("Negative text-indent doesn't work well with RTL. If you use text-indent for image replacement explicitly set direction for that item to ltr.", textIndent.line, textIndent.col, rule);
9890
+
}
9891
+
}
9892
+
9893
+
parser.addListener("startrule", startRule);
9894
+
parser.addListener("startfontface", startRule);
9895
+
9896
+
// check for use of "font-size"
9897
+
parser.addListener("property", function(event) {
9898
+
var name = event.property.toString().toLowerCase(),
9899
+
value = event.value;
9900
+
9901
+
if (name === "text-indent" && value.parts[0].value < -99) {
9902
+
textIndent = event.property;
9903
+
} else if (name === "direction" && value.toString() === "ltr") {
9904
+
direction = "ltr";
9905
+
}
9906
+
});
9907
+
9908
+
parser.addListener("endrule", endRule);
9909
+
parser.addListener("endfontface", endRule);
9910
+
9911
+
}
9912
+
9913
+
});
9914
+
9915
+
/*
9916
+
* Rule: Don't use properties with a underscore prefix.
9917
+
*
9918
+
*/
9919
+
9920
+
CSSLint.addRule({
9921
+
9922
+
// rule information
9923
+
id: "underscore-property-hack",
9924
+
name: "Disallow properties with an underscore prefix",
9925
+
desc: "Checks for the underscore property hack (targets IE6)",
9926
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-underscore-hack",
9927
+
browsers: "All",
9928
+
9929
+
// initialization
9930
+
init: function(parser, reporter) {
9931
+
"use strict";
9932
+
var rule = this;
9933
+
9934
+
// check if property name starts with "_"
9935
+
parser.addListener("property", function(event) {
9936
+
var property = event.property;
9937
+
9938
+
if (property.hack === "_") {
9939
+
reporter.report("Property with underscore prefix found.", event.property.line, event.property.col, rule);
9940
+
}
9941
+
});
9942
+
}
9943
+
});
9944
+
9945
+
/*
9946
+
* Rule: Headings (h1-h6) should be defined only once.
9947
+
*/
9948
+
9949
+
CSSLint.addRule({
9950
+
9951
+
// rule information
9952
+
id: "unique-headings",
9953
+
name: "Headings should only be defined once",
9954
+
desc: "Headings should be defined only once.",
9955
+
url: "https://github.com/CSSLint/csslint/wiki/Headings-should-only-be-defined-once",
9956
+
browsers: "All",
9957
+
9958
+
// initialization
9959
+
init: function(parser, reporter) {
9960
+
"use strict";
9961
+
var rule = this;
9962
+
9963
+
var headings = {
9964
+
h1: 0,
9965
+
h2: 0,
9966
+
h3: 0,
9967
+
h4: 0,
9968
+
h5: 0,
9969
+
h6: 0
9970
+
};
9971
+
9972
+
parser.addListener("startrule", function(event) {
9973
+
var selectors = event.selectors,
9974
+
selector,
9975
+
part,
9976
+
pseudo,
9977
+
i, j;
9978
+
9979
+
for (i=0; i < selectors.length; i++) {
9980
+
selector = selectors[i];
9981
+
part = selector.parts[selector.parts.length-1];
9982
+
9983
+
if (part.elementName && /(h[1-6])/i.test(part.elementName.toString())) {
9984
+
9985
+
for (j=0; j < part.modifiers.length; j++) {
9986
+
if (part.modifiers[j].type === "pseudo") {
9987
+
pseudo = true;
9988
+
break;
9989
+
}
9990
+
}
9991
+
9992
+
if (!pseudo) {
9993
+
headings[RegExp.$1]++;
9994
+
if (headings[RegExp.$1] > 1) {
9995
+
reporter.report("Heading (" + part.elementName + ") has already been defined.", part.line, part.col, rule);
9996
+
}
9997
+
}
9998
+
}
9999
+
}
10000
+
});
10001
+
10002
+
parser.addListener("endstylesheet", function() {
10003
+
var prop,
10004
+
messages = [];
10005
+
10006
+
for (prop in headings) {
10007
+
if (headings.hasOwnProperty(prop)) {
10008
+
if (headings[prop] > 1) {
10009
+
messages.push(headings[prop] + " " + prop + "s");
10010
+
}
10011
+
}
10012
+
}
10013
+
10014
+
if (messages.length) {
10015
+
reporter.rollupWarn("You have " + messages.join(", ") + " defined in this stylesheet.", rule);
10016
+
}
10017
+
});
10018
+
}
10019
+
10020
+
});
10021
+
10022
+
/*
10023
+
* Rule: Don't use universal selector because it's slow.
10024
+
*/
10025
+
10026
+
CSSLint.addRule({
10027
+
10028
+
// rule information
10029
+
id: "universal-selector",
10030
+
name: "Disallow universal selector",
10031
+
desc: "The universal selector (*) is known to be slow.",
10032
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-universal-selector",
10033
+
browsers: "All",
10034
+
10035
+
// initialization
10036
+
init: function(parser, reporter) {
10037
+
"use strict";
10038
+
var rule = this;
10039
+
10040
+
parser.addListener("startrule", function(event) {
10041
+
var selectors = event.selectors,
10042
+
selector,
10043
+
part,
10044
+
i;
10045
+
10046
+
for (i=0; i < selectors.length; i++) {
10047
+
selector = selectors[i];
10048
+
10049
+
part = selector.parts[selector.parts.length-1];
10050
+
if (part.elementName === "*") {
10051
+
reporter.report(rule.desc, part.line, part.col, rule);
10052
+
}
10053
+
}
10054
+
});
10055
+
}
10056
+
10057
+
});
10058
+
10059
+
/*
10060
+
* Rule: Don't use unqualified attribute selectors because they're just like universal selectors.
10061
+
*/
10062
+
10063
+
CSSLint.addRule({
10064
+
10065
+
// rule information
10066
+
id: "unqualified-attributes",
10067
+
name: "Disallow unqualified attribute selectors",
10068
+
desc: "Unqualified attribute selectors are known to be slow.",
10069
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-unqualified-attribute-selectors",
10070
+
browsers: "All",
10071
+
10072
+
// initialization
10073
+
init: function(parser, reporter) {
10074
+
"use strict";
10075
+
10076
+
var rule = this;
10077
+
10078
+
parser.addListener("startrule", function(event) {
10079
+
10080
+
var selectors = event.selectors,
10081
+
selectorContainsClassOrId = false,
10082
+
selector,
10083
+
part,
10084
+
modifier,
10085
+
i, k;
10086
+
10087
+
for (i=0; i < selectors.length; i++) {
10088
+
selector = selectors[i];
10089
+
10090
+
part = selector.parts[selector.parts.length-1];
10091
+
if (part.type === parser.SELECTOR_PART_TYPE) {
10092
+
for (k=0; k < part.modifiers.length; k++) {
10093
+
modifier = part.modifiers[k];
10094
+
10095
+
if (modifier.type === "class" || modifier.type === "id") {
10096
+
selectorContainsClassOrId = true;
10097
+
break;
10098
+
}
10099
+
}
10100
+
10101
+
if (!selectorContainsClassOrId) {
10102
+
for (k=0; k < part.modifiers.length; k++) {
10103
+
modifier = part.modifiers[k];
10104
+
if (modifier.type === "attribute" && (!part.elementName || part.elementName === "*")) {
10105
+
reporter.report(rule.desc, part.line, part.col, rule);
10106
+
}
10107
+
}
10108
+
}
10109
+
}
10110
+
10111
+
}
10112
+
});
10113
+
}
10114
+
10115
+
});
10116
+
10117
+
/*
10118
+
* Rule: When using a vendor-prefixed property, make sure to
10119
+
* include the standard one.
10120
+
*/
10121
+
10122
+
CSSLint.addRule({
10123
+
10124
+
// rule information
10125
+
id: "vendor-prefix",
10126
+
name: "Require standard property with vendor prefix",
10127
+
desc: "When using a vendor-prefixed property, make sure to include the standard one.",
10128
+
url: "https://github.com/CSSLint/csslint/wiki/Require-standard-property-with-vendor-prefix",
10129
+
browsers: "All",
10130
+
10131
+
// initialization
10132
+
init: function(parser, reporter) {
10133
+
"use strict";
10134
+
var rule = this,
10135
+
properties,
10136
+
num,
10137
+
propertiesToCheck = {
10138
+
"-webkit-border-radius": "border-radius",
10139
+
"-webkit-border-top-left-radius": "border-top-left-radius",
10140
+
"-webkit-border-top-right-radius": "border-top-right-radius",
10141
+
"-webkit-border-bottom-left-radius": "border-bottom-left-radius",
10142
+
"-webkit-border-bottom-right-radius": "border-bottom-right-radius",
10143
+
10144
+
"-o-border-radius": "border-radius",
10145
+
"-o-border-top-left-radius": "border-top-left-radius",
10146
+
"-o-border-top-right-radius": "border-top-right-radius",
10147
+
"-o-border-bottom-left-radius": "border-bottom-left-radius",
10148
+
"-o-border-bottom-right-radius": "border-bottom-right-radius",
10149
+
10150
+
"-moz-border-radius": "border-radius",
10151
+
"-moz-border-radius-topleft": "border-top-left-radius",
10152
+
"-moz-border-radius-topright": "border-top-right-radius",
10153
+
"-moz-border-radius-bottomleft": "border-bottom-left-radius",
10154
+
"-moz-border-radius-bottomright": "border-bottom-right-radius",
10155
+
10156
+
"-moz-column-count": "column-count",
10157
+
"-webkit-column-count": "column-count",
10158
+
10159
+
"-moz-column-gap": "column-gap",
10160
+
"-webkit-column-gap": "column-gap",
10161
+
10162
+
"-moz-column-rule": "column-rule",
10163
+
"-webkit-column-rule": "column-rule",
10164
+
10165
+
"-moz-column-rule-style": "column-rule-style",
10166
+
"-webkit-column-rule-style": "column-rule-style",
10167
+
10168
+
"-moz-column-rule-color": "column-rule-color",
10169
+
"-webkit-column-rule-color": "column-rule-color",
10170
+
10171
+
"-moz-column-rule-width": "column-rule-width",
10172
+
"-webkit-column-rule-width": "column-rule-width",
10173
+
10174
+
"-moz-column-width": "column-width",
10175
+
"-webkit-column-width": "column-width",
10176
+
10177
+
"-webkit-column-span": "column-span",
10178
+
"-webkit-columns": "columns",
10179
+
10180
+
"-moz-box-shadow": "box-shadow",
10181
+
"-webkit-box-shadow": "box-shadow",
10182
+
10183
+
"-moz-transform": "transform",
10184
+
"-webkit-transform": "transform",
10185
+
"-o-transform": "transform",
10186
+
"-ms-transform": "transform",
10187
+
10188
+
"-moz-transform-origin": "transform-origin",
10189
+
"-webkit-transform-origin": "transform-origin",
10190
+
"-o-transform-origin": "transform-origin",
10191
+
"-ms-transform-origin": "transform-origin",
10192
+
10193
+
"-moz-box-sizing": "box-sizing",
10194
+
"-webkit-box-sizing": "box-sizing"
10195
+
};
10196
+
10197
+
// event handler for beginning of rules
10198
+
function startRule() {
10199
+
properties = {};
10200
+
num = 1;
10201
+
}
10202
+
10203
+
// event handler for end of rules
10204
+
function endRule() {
10205
+
var prop,
10206
+
i,
10207
+
len,
10208
+
needed,
10209
+
actual,
10210
+
needsStandard = [];
10211
+
10212
+
for (prop in properties) {
10213
+
if (propertiesToCheck[prop]) {
10214
+
needsStandard.push({
10215
+
actual: prop,
10216
+
needed: propertiesToCheck[prop]
10217
+
});
10218
+
}
10219
+
}
10220
+
10221
+
for (i=0, len=needsStandard.length; i < len; i++) {
10222
+
needed = needsStandard[i].needed;
10223
+
actual = needsStandard[i].actual;
10224
+
10225
+
if (!properties[needed]) {
10226
+
reporter.report("Missing standard property '" + needed + "' to go along with '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
10227
+
} else {
10228
+
// make sure standard property is last
10229
+
if (properties[needed][0].pos < properties[actual][0].pos) {
10230
+
reporter.report("Standard property '" + needed + "' should come after vendor-prefixed property '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
10231
+
}
10232
+
}
10233
+
}
10234
+
10235
+
}
10236
+
10237
+
parser.addListener("startrule", startRule);
10238
+
parser.addListener("startfontface", startRule);
10239
+
parser.addListener("startpage", startRule);
10240
+
parser.addListener("startpagemargin", startRule);
10241
+
parser.addListener("startkeyframerule", startRule);
10242
+
parser.addListener("startviewport", startRule);
10243
+
10244
+
parser.addListener("property", function(event) {
10245
+
var name = event.property.text.toLowerCase();
10246
+
10247
+
if (!properties[name]) {
10248
+
properties[name] = [];
10249
+
}
10250
+
10251
+
properties[name].push({
10252
+
name: event.property,
10253
+
value: event.value,
10254
+
pos: num++
10255
+
});
10256
+
});
10257
+
10258
+
parser.addListener("endrule", endRule);
10259
+
parser.addListener("endfontface", endRule);
10260
+
parser.addListener("endpage", endRule);
10261
+
parser.addListener("endpagemargin", endRule);
10262
+
parser.addListener("endkeyframerule", endRule);
10263
+
parser.addListener("endviewport", endRule);
10264
+
}
10265
+
10266
+
});
10267
+
10268
+
/*
10269
+
* Rule: You don't need to specify units when a value is 0.
10270
+
*/
10271
+
10272
+
CSSLint.addRule({
10273
+
10274
+
// rule information
10275
+
id: "zero-units",
10276
+
name: "Disallow units for 0 values",
10277
+
desc: "You don't need to specify units when a value is 0.",
10278
+
url: "https://github.com/CSSLint/csslint/wiki/Disallow-units-for-zero-values",
10279
+
browsers: "All",
10280
+
10281
+
// initialization
10282
+
init: function(parser, reporter) {
10283
+
"use strict";
10284
+
var rule = this;
10285
+
10286
+
// count how many times "float" is used
10287
+
parser.addListener("property", function(event) {
10288
+
var parts = event.value.parts,
10289
+
i = 0,
10290
+
len = parts.length;
10291
+
10292
+
while (i < len) {
10293
+
if ((parts[i].units || parts[i].type === "percentage") && parts[i].value === 0 && parts[i].type !== "time") {
10294
+
reporter.report("Values of 0 shouldn't have units specified.", parts[i].line, parts[i].col, rule);
10295
+
}
10296
+
i++;
10297
+
}
10298
+
10299
+
});
10300
+
10301
+
}
10302
+
10303
+
});
10304
+
10305
+
(function() {
10306
+
"use strict";
10307
+
10308
+
/**
10309
+
* Replace special characters before write to output.
10310
+
*
10311
+
* Rules:
10312
+
* - single quotes is the escape sequence for double-quotes
10313
+
* - & is the escape sequence for &
10314
+
* - < is the escape sequence for <
10315
+
* - > 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 """;
10329
+
case "&":
10330
+
return "&";
10331
+
case "<":
10332
+
return "<";
10333
+
case ">":
10334
+
return ">";
10335
+
}
10336
+
});
10337
+
};
10338
+
10339
+
CSSLint.addFormatter({
10340
+
// format information
10341
+
id: "checkstyle-xml",
10342
+
name: "Checkstyle XML format",
10343
+
10344
+
/**
10345
+
* Return opening root XML tag.
10346
+
* @return {String} to prepend before all results
10347
+
*/
10348
+
startFormat: function() {
10349
+
return "<?xml version=\"1.0\" encoding=\"utf-8\"?><checkstyle>";
10350
+
},
10351
+
10352
+
/**
10353
+
* Return closing root XML tag.
10354
+
* @return {String} to append after all results
10355
+
*/
10356
+
endFormat: function() {
10357
+
return "</checkstyle>";
10358
+
},
10359
+
10360
+
/**
10361
+
* Returns message when there is a file read error.
10362
+
* @param {String} filename The name of the file that caused the error.
10363
+
* @param {String} message The error message
10364
+
* @return {String} The error message.
10365
+
*/
10366
+
readError: function(filename, message) {
10367
+
return "<file name=\"" + xmlEscape(filename) + "\"><error line=\"0\" column=\"0\" severty=\"error\" message=\"" + xmlEscape(message) + "\"></error></file>";
10368
+
},
10369
+
10370
+
/**
10371
+
* Given CSS Lint results for a file, return output for this format.
10372
+
* @param results {Object} with error and warning messages
10373
+
* @param filename {String} relative file path
10374
+
* @param options {Object} (UNUSED for now) specifies special handling of output
10375
+
* @return {String} output for results
10376
+
*/
10377
+
formatResults: function(results, filename/*, options*/) {
10378
+
var messages = results.messages,
10379
+
output = [];
10380
+
10381
+
/**
10382
+
* Generate a source string for a rule.
10383
+
* Checkstyle source strings usually resemble Java class names e.g
10384
+
* net.csslint.SomeRuleName
10385
+
* @param {Object} rule
10386
+
* @return rule source as {String}
10387
+
*/
10388
+
var generateSource = function(rule) {
10389
+
if (!rule || !("name" in rule)) {
10390
+
return "";
10391
+
}
10392
+
return "net.csslint." + rule.name.replace(/\s/g, "");
10393
+
};
10394
+
10395
+
10396
+
if (messages.length > 0) {
10397
+
output.push("<file name=\""+filename+"\">");
10398
+
CSSLint.Util.forEach(messages, function (message) {
10399
+
// ignore rollups for now
10400
+
if (!message.rollup) {
10401
+
output.push("<error line=\"" + message.line + "\" column=\"" + message.col + "\" severity=\"" + message.type + "\"" +
10402
+
" message=\"" + xmlEscape(message.message) + "\" source=\"" + generateSource(message.rule) +"\"/>");
10403
+
}
10404
+
});
10405
+
output.push("</file>");
10406
+
}
10407
+
10408
+
return output.join("");
10409
+
}
10410
+
});
10411
+
10412
+
}());
10413
+
10414
+
CSSLint.addFormatter({
10415
+
// format information
10416
+
id: "compact",
10417
+
name: "Compact, 'porcelain' format",
10418
+
10419
+
/**
10420
+
* Return content to be printed before all file results.
10421
+
* @return {String} to prepend before all results
10422
+
*/
10423
+
startFormat: function() {
10424
+
"use strict";
10425
+
return "";
10426
+
},
10427
+
10428
+
/**
10429
+
* Return content to be printed after all file results.
10430
+
* @return {String} to append after all results
10431
+
*/
10432
+
endFormat: function() {
10433
+
"use strict";
10434
+
return "";
10435
+
},
10436
+
10437
+
/**
10438
+
* Given CSS Lint results for a file, return output for this format.
10439
+
* @param results {Object} with error and warning messages
10440
+
* @param filename {String} relative file path
10441
+
* @param options {Object} (Optional) specifies special handling of output
10442
+
* @return {String} output for results
10443
+
*/
10444
+
formatResults: function(results, filename, options) {
10445
+
"use strict";
10446
+
var messages = results.messages,
10447
+
output = "";
10448
+
options = options || {};
10449
+
10450
+
/**
10451
+
* Capitalize and return given string.
10452
+
* @param str {String} to capitalize
10453
+
* @return {String} capitalized
10454
+
*/
10455
+
var capitalize = function(str) {
10456
+
return str.charAt(0).toUpperCase() + str.slice(1);
10457
+
};
10458
+
10459
+
if (messages.length === 0) {
10460
+
return options.quiet ? "" : filename + ": Lint Free!";
10461
+
}
10462
+
10463
+
CSSLint.Util.forEach(messages, function(message) {
10464
+
if (message.rollup) {
10465
+
output += filename + ": " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
10466
+
} else {
10467
+
output += filename + ": line " + message.line +
10468
+
", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
10469
+
}
10470
+
});
10471
+
10472
+
return output;
10473
+
}
10474
+
});
10475
+
10476
+
CSSLint.addFormatter({
10477
+
// format information
10478
+
id: "csslint-xml",
10479
+
name: "CSSLint XML format",
10480
+
10481
+
/**
10482
+
* Return opening root XML tag.
10483
+
* @return {String} to prepend before all results
10484
+
*/
10485
+
startFormat: function() {
10486
+
"use strict";
10487
+
return "<?xml version=\"1.0\" encoding=\"utf-8\"?><csslint>";
10488
+
},
10489
+
10490
+
/**
10491
+
* Return closing root XML tag.
10492
+
* @return {String} to append after all results
10493
+
*/
10494
+
endFormat: function() {
10495
+
"use strict";
10496
+
return "</csslint>";
10497
+
},
10498
+
10499
+
/**
10500
+
* Given CSS Lint results for a file, return output for this format.
10501
+
* @param results {Object} with error and warning messages
10502
+
* @param filename {String} relative file path
10503
+
* @param options {Object} (UNUSED for now) specifies special handling of output
10504
+
* @return {String} output for results
10505
+
*/
10506
+
formatResults: function(results, filename/*, options*/) {
10507
+
"use strict";
10508
+
var messages = results.messages,
10509
+
output = [];
10510
+
10511
+
/**
10512
+
* Replace special characters before write to output.
10513
+
*
10514
+
* Rules:
10515
+
* - single quotes is the escape sequence for double-quotes
10516
+
* - & is the escape sequence for &
10517
+
* - < is the escape sequence for <
10518
+
* - > 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, "&").replace(/</g, "<").replace(/>/g, ">");
10528
+
};
10529
+
10530
+
if (messages.length > 0) {
10531
+
output.push("<file name=\""+filename+"\">");
10532
+
CSSLint.Util.forEach(messages, function (message) {
10533
+
if (message.rollup) {
10534
+
output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
10535
+
} else {
10536
+
output.push("<issue line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
10537
+
" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
10538
+
}
10539
+
});
10540
+
output.push("</file>");
10541
+
}
10542
+
10543
+
return output.join("");
10544
+
}
10545
+
});
10546
+
10547
+
/* globals JSON: true */
10548
+
10549
+
CSSLint.addFormatter({
10550
+
// format information
10551
+
id: "json",
10552
+
name: "JSON",
10553
+
10554
+
/**
10555
+
* Return content to be printed before all file results.
10556
+
* @return {String} to prepend before all results
10557
+
*/
10558
+
startFormat: function() {
10559
+
"use strict";
10560
+
this.json = [];
10561
+
return "";
10562
+
},
10563
+
10564
+
/**
10565
+
* Return content to be printed after all file results.
10566
+
* @return {String} to append after all results
10567
+
*/
10568
+
endFormat: function() {
10569
+
"use strict";
10570
+
var ret = "";
10571
+
if (this.json.length > 0) {
10572
+
if (this.json.length === 1) {
10573
+
ret = JSON.stringify(this.json[0]);
10574
+
} else {
10575
+
ret = JSON.stringify(this.json);
10576
+
}
10577
+
}
10578
+
return ret;
10579
+
},
10580
+
10581
+
/**
10582
+
* Given CSS Lint results for a file, return output for this format.
10583
+
* @param results {Object} with error and warning messages
10584
+
* @param filename {String} relative file path (Unused)
10585
+
* @return {String} output for results
10586
+
*/
10587
+
formatResults: function(results, filename, options) {
10588
+
"use strict";
10589
+
if (results.messages.length > 0 || !options.quiet) {
10590
+
this.json.push({
10591
+
filename: filename,
10592
+
messages: results.messages,
10593
+
stats: results.stats
10594
+
});
10595
+
}
10596
+
return "";
10597
+
}
10598
+
});
10599
+
10600
+
CSSLint.addFormatter({
10601
+
// format information
10602
+
id: "junit-xml",
10603
+
name: "JUNIT XML format",
10604
+
10605
+
/**
10606
+
* Return opening root XML tag.
10607
+
* @return {String} to prepend before all results
10608
+
*/
10609
+
startFormat: function() {
10610
+
"use strict";
10611
+
return "<?xml version=\"1.0\" encoding=\"utf-8\"?><testsuites>";
10612
+
},
10613
+
10614
+
/**
10615
+
* Return closing root XML tag.
10616
+
* @return {String} to append after all results
10617
+
*/
10618
+
endFormat: function() {
10619
+
"use strict";
10620
+
return "</testsuites>";
10621
+
},
10622
+
10623
+
/**
10624
+
* Given CSS Lint results for a file, return output for this format.
10625
+
* @param results {Object} with error and warning messages
10626
+
* @param filename {String} relative file path
10627
+
* @param options {Object} (UNUSED for now) specifies special handling of output
10628
+
* @return {String} output for results
10629
+
*/
10630
+
formatResults: function(results, filename/*, options*/) {
10631
+
"use strict";
10632
+
10633
+
var messages = results.messages,
10634
+
output = [],
10635
+
tests = {
10636
+
"error": 0,
10637
+
"failure": 0
10638
+
};
10639
+
10640
+
/**
10641
+
* Generate a source string for a rule.
10642
+
* JUNIT source strings usually resemble Java class names e.g
10643
+
* net.csslint.SomeRuleName
10644
+
* @param {Object} rule
10645
+
* @return rule source as {String}
10646
+
*/
10647
+
var generateSource = function(rule) {
10648
+
if (!rule || !("name" in rule)) {
10649
+
return "";
10650
+
}
10651
+
return "net.csslint." + rule.name.replace(/\s/g, "");
10652
+
};
10653
+
10654
+
/**
10655
+
* Replace special characters before write to output.
10656
+
*
10657
+
* Rules:
10658
+
* - single quotes is the escape sequence for double-quotes
10659
+
* - < is the escape sequence for <
10660
+
* - > 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, "<").replace(/>/g, ">");
10672
+
10673
+
};
10674
+
10675
+
if (messages.length > 0) {
10676
+
10677
+
messages.forEach(function (message) {
10678
+
10679
+
// since junit has no warning class
10680
+
// all issues as errors
10681
+
var type = message.type === "warning" ? "error" : message.type;
10682
+
10683
+
// ignore rollups for now
10684
+
if (!message.rollup) {
10685
+
10686
+
// build the test case separately, once joined
10687
+
// we'll add it to a custom array filtered by type
10688
+
output.push("<testcase time=\"0\" name=\"" + generateSource(message.rule) + "\">");
10689
+
output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\"><![CDATA[" + message.line + ":" + message.col + ":" + escapeSpecialCharacters(message.evidence) + "]]></" + type + ">");
10690
+
output.push("</testcase>");
10691
+
10692
+
tests[type] += 1;
10693
+
10694
+
}
10695
+
10696
+
});
10697
+
10698
+
output.unshift("<testsuite time=\"0\" tests=\"" + messages.length + "\" skipped=\"0\" errors=\"" + tests.error + "\" failures=\"" + tests.failure + "\" package=\"net.csslint\" name=\"" + filename + "\">");
10699
+
output.push("</testsuite>");
10700
+
10701
+
}
10702
+
10703
+
return output.join("");
10704
+
10705
+
}
10706
+
});
10707
+
10708
+
CSSLint.addFormatter({
10709
+
// format information
10710
+
id: "lint-xml",
10711
+
name: "Lint XML format",
10712
+
10713
+
/**
10714
+
* Return opening root XML tag.
10715
+
* @return {String} to prepend before all results
10716
+
*/
10717
+
startFormat: function() {
10718
+
"use strict";
10719
+
return "<?xml version=\"1.0\" encoding=\"utf-8\"?><lint>";
10720
+
},
10721
+
10722
+
/**
10723
+
* Return closing root XML tag.
10724
+
* @return {String} to append after all results
10725
+
*/
10726
+
endFormat: function() {
10727
+
"use strict";
10728
+
return "</lint>";
10729
+
},
10730
+
10731
+
/**
10732
+
* Given CSS Lint results for a file, return output for this format.
10733
+
* @param results {Object} with error and warning messages
10734
+
* @param filename {String} relative file path
10735
+
* @param options {Object} (UNUSED for now) specifies special handling of output
10736
+
* @return {String} output for results
10737
+
*/
10738
+
formatResults: function(results, filename/*, options*/) {
10739
+
"use strict";
10740
+
var messages = results.messages,
10741
+
output = [];
10742
+
10743
+
/**
10744
+
* Replace special characters before write to output.
10745
+
*
10746
+
* Rules:
10747
+
* - single quotes is the escape sequence for double-quotes
10748
+
* - & is the escape sequence for &
10749
+
* - < is the escape sequence for <
10750
+
* - > 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, "&").replace(/</g, "<").replace(/>/g, ">");
10760
+
};
10761
+
10762
+
if (messages.length > 0) {
10763
+
10764
+
output.push("<file name=\""+filename+"\">");
10765
+
CSSLint.Util.forEach(messages, function (message) {
10766
+
if (message.rollup) {
10767
+
output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
10768
+
} else {
10769
+
var rule = "";
10770
+
if (message.rule && message.rule.id) {
10771
+
rule = "rule=\"" + escapeSpecialCharacters(message.rule.id) + "\" ";
10772
+
}
10773
+
output.push("<issue " + rule + "line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
10774
+
" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
10775
+
}
10776
+
});
10777
+
output.push("</file>");
10778
+
}
10779
+
10780
+
return output.join("");
10781
+
}
10782
+
});
10783
+
10784
+
CSSLint.addFormatter({
10785
+
// format information
10786
+
id: "text",
10787
+
name: "Plain Text",
10788
+
10789
+
/**
10790
+
* Return content to be printed before all file results.
10791
+
* @return {String} to prepend before all results
10792
+
*/
10793
+
startFormat: function() {
10794
+
"use strict";
10795
+
return "";
10796
+
},
10797
+
10798
+
/**
10799
+
* Return content to be printed after all file results.
10800
+
* @return {String} to append after all results
10801
+
*/
10802
+
endFormat: function() {
10803
+
"use strict";
10804
+
return "";
10805
+
},
10806
+
10807
+
/**
10808
+
* Given CSS Lint results for a file, return output for this format.
10809
+
* @param results {Object} with error and warning messages
10810
+
* @param filename {String} relative file path
10811
+
* @param options {Object} (Optional) specifies special handling of output
10812
+
* @return {String} output for results
10813
+
*/
10814
+
formatResults: function(results, filename, options) {
10815
+
"use strict";
10816
+
var messages = results.messages,
10817
+
output = "";
10818
+
options = options || {};
10819
+
10820
+
if (messages.length === 0) {
10821
+
return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + ".";
10822
+
}
10823
+
10824
+
output = "\n\ncsslint: There ";
10825
+
if (messages.length === 1) {
10826
+
output += "is 1 problem";
10827
+
} else {
10828
+
output += "are " + messages.length + " problems";
10829
+
}
10830
+
output += " in " + filename + ".";
10831
+
10832
+
var pos = filename.lastIndexOf("/"),
10833
+
shortFilename = filename;
10834
+
10835
+
if (pos === -1) {
10836
+
pos = filename.lastIndexOf("\\");
10837
+
}
10838
+
if (pos > -1) {
10839
+
shortFilename = filename.substring(pos+1);
10840
+
}
10841
+
10842
+
CSSLint.Util.forEach(messages, function (message, i) {
10843
+
output = output + "\n\n" + shortFilename;
10844
+
if (message.rollup) {
10845
+
output += "\n" + (i+1) + ": " + message.type;
10846
+
output += "\n" + message.message;
10847
+
} else {
10848
+
output += "\n" + (i+1) + ": " + message.type + " at line " + message.line + ", col " + message.col;
10849
+
output += "\n" + message.message;
10850
+
output += "\n" + message.evidence;
10851
+
}
10852
+
});
10853
+
10854
+
return output;
10855
+
}
10856
+
});
10857
+
10858
+
return CSSLint;
10859
+
})();