css_validator 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/README.md +2 -2
- data/benchmark.rb +27 -0
- data/css_validator.gemspec +1 -1
- data/lib/css_validator.rb +3 -3
- data/vendor/cli.js +441 -0
- data/vendor/lib/csslint-node.js +2851 -0
- data/vendor/{csslint-rhino.js → lib/node-parserlib.js} +81 -3276
- metadata +7 -5
- data/vendor/js.jar +0 -0
@@ -1,30 +1,4 @@
|
|
1
1
|
/*!
|
2
|
-
CSSLint
|
3
|
-
Copyright (c) 2013 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
|
-
/* Build: v0.10.0 15-August-2013 01:07:22 */
|
25
|
-
var exports = exports || {};
|
26
|
-
var CSSLint = (function(){
|
27
|
-
/*!
|
28
2
|
Parser-Lib
|
29
3
|
Copyright (c) 2009-2011 Nicholas C. Zakas. All rights reserved.
|
30
4
|
|
@@ -47,7 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
47
21
|
THE SOFTWARE.
|
48
22
|
|
49
23
|
*/
|
50
|
-
/* Version v0.2.
|
24
|
+
/* Version v0.2.4, Build time: 7-January-2014 07:32:49 */
|
51
25
|
var parserlib = {};
|
52
26
|
(function(){
|
53
27
|
|
@@ -957,7 +931,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
957
931
|
THE SOFTWARE.
|
958
932
|
|
959
933
|
*/
|
960
|
-
/* Version v0.2.
|
934
|
+
/* Version v0.2.4, Build time: 7-January-2014 07:32:49 */
|
961
935
|
(function(){
|
962
936
|
var EventTarget = parserlib.util.EventTarget,
|
963
937
|
TokenStreamBase = parserlib.util.TokenStreamBase,
|
@@ -992,6 +966,7 @@ var Colors = {
|
|
992
966
|
darkcyan :"#008b8b",
|
993
967
|
darkgoldenrod :"#b8860b",
|
994
968
|
darkgray :"#a9a9a9",
|
969
|
+
darkgrey :"#a9a9a9",
|
995
970
|
darkgreen :"#006400",
|
996
971
|
darkkhaki :"#bdb76b",
|
997
972
|
darkmagenta :"#8b008b",
|
@@ -1003,11 +978,13 @@ var Colors = {
|
|
1003
978
|
darkseagreen :"#8fbc8f",
|
1004
979
|
darkslateblue :"#483d8b",
|
1005
980
|
darkslategray :"#2f4f4f",
|
981
|
+
darkslategrey :"#2f4f4f",
|
1006
982
|
darkturquoise :"#00ced1",
|
1007
983
|
darkviolet :"#9400d3",
|
1008
984
|
deeppink :"#ff1493",
|
1009
985
|
deepskyblue :"#00bfff",
|
1010
986
|
dimgray :"#696969",
|
987
|
+
dimgrey :"#696969",
|
1011
988
|
dodgerblue :"#1e90ff",
|
1012
989
|
firebrick :"#b22222",
|
1013
990
|
floralwhite :"#fffaf0",
|
@@ -1018,6 +995,7 @@ var Colors = {
|
|
1018
995
|
gold :"#ffd700",
|
1019
996
|
goldenrod :"#daa520",
|
1020
997
|
gray :"#808080",
|
998
|
+
grey :"#808080",
|
1021
999
|
green :"#008000",
|
1022
1000
|
greenyellow :"#adff2f",
|
1023
1001
|
honeydew :"#f0fff0",
|
@@ -1035,12 +1013,14 @@ var Colors = {
|
|
1035
1013
|
lightcyan :"#e0ffff",
|
1036
1014
|
lightgoldenrodyellow :"#fafad2",
|
1037
1015
|
lightgray :"#d3d3d3",
|
1016
|
+
lightgrey :"#d3d3d3",
|
1038
1017
|
lightgreen :"#90ee90",
|
1039
1018
|
lightpink :"#ffb6c1",
|
1040
1019
|
lightsalmon :"#ffa07a",
|
1041
1020
|
lightseagreen :"#20b2aa",
|
1042
1021
|
lightskyblue :"#87cefa",
|
1043
1022
|
lightslategray :"#778899",
|
1023
|
+
lightslategrey :"#778899",
|
1044
1024
|
lightsteelblue :"#b0c4de",
|
1045
1025
|
lightyellow :"#ffffe0",
|
1046
1026
|
lime :"#00ff00",
|
@@ -1093,6 +1073,7 @@ var Colors = {
|
|
1093
1073
|
skyblue :"#87ceeb",
|
1094
1074
|
slateblue :"#6a5acd",
|
1095
1075
|
slategray :"#708090",
|
1076
|
+
slategrey :"#708090",
|
1096
1077
|
snow :"#fffafa",
|
1097
1078
|
springgreen :"#00ff7f",
|
1098
1079
|
steelblue :"#4682b4",
|
@@ -1118,6 +1099,7 @@ var Colors = {
|
|
1118
1099
|
buttontext :"Text on push buttons.",
|
1119
1100
|
captiontext :"Text in caption, size box, and scrollbar arrow box.",
|
1120
1101
|
graytext :"Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.",
|
1102
|
+
greytext :"Greyed (disabled) text. This color is set to #000 if the current display driver does not support a solid grey color.",
|
1121
1103
|
highlight :"Item(s) selected in a control.",
|
1122
1104
|
highlighttext :"Text of item(s) selected in a control.",
|
1123
1105
|
inactiveborder :"Inactive window border.",
|
@@ -2812,7 +2794,7 @@ Parser.prototype = function(){
|
|
2812
2794
|
|
2813
2795
|
var tokenStream = this._tokenStream,
|
2814
2796
|
values = [],
|
2815
|
-
|
2797
|
+
//valueParts = [],
|
2816
2798
|
value = null,
|
2817
2799
|
operator = null;
|
2818
2800
|
|
@@ -2829,9 +2811,9 @@ Parser.prototype = function(){
|
|
2829
2811
|
values.push(operator);
|
2830
2812
|
} /*else {
|
2831
2813
|
//if there's not an operator, you have a full value
|
2832
|
-
|
2833
|
-
|
2834
|
-
|
2814
|
+
values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
|
2815
|
+
valueParts = [];
|
2816
|
+
}*/
|
2835
2817
|
|
2836
2818
|
value = this._term();
|
2837
2819
|
|
@@ -2843,7 +2825,7 @@ Parser.prototype = function(){
|
|
2843
2825
|
} while(true);
|
2844
2826
|
}
|
2845
2827
|
|
2846
|
-
|
2828
|
+
//cleanup
|
2847
2829
|
/*if (valueParts.length){
|
2848
2830
|
values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
|
2849
2831
|
}*/
|
@@ -3534,6 +3516,12 @@ nth
|
|
3534
3516
|
var Properties = {
|
3535
3517
|
|
3536
3518
|
//A
|
3519
|
+
"align-items" : "flex-start | flex-end | center | baseline | stretch",
|
3520
|
+
"align-content" : "flex-start | flex-end | center | space-between | space-around | stretch",
|
3521
|
+
"align-self" : "auto | flex-start | flex-end | center | baseline | stretch",
|
3522
|
+
"-webkit-align-items" : "flex-start | flex-end | center | baseline | stretch",
|
3523
|
+
"-webkit-align-content" : "flex-start | flex-end | center | space-between | space-around | stretch",
|
3524
|
+
"-webkit-align-self" : "auto | flex-start | flex-end | center | baseline | stretch",
|
3537
3525
|
"alignment-adjust" : "auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | <percentage> | <length>",
|
3538
3526
|
"alignment-baseline" : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
|
3539
3527
|
"animation" : 1,
|
@@ -3729,15 +3717,24 @@ var Properties = {
|
|
3729
3717
|
"border-top-width" : "<border-width>",
|
3730
3718
|
"border-width" : { multi: "<border-width>", max: 4 },
|
3731
3719
|
"bottom" : "<margin-width> | inherit",
|
3732
|
-
"box-align"
|
3733
|
-
"box-decoration-break"
|
3734
|
-
"box-direction"
|
3735
|
-
"box-flex"
|
3736
|
-
"box-flex-group"
|
3737
|
-
"box-lines"
|
3738
|
-
"box-ordinal-group"
|
3739
|
-
"box-orient"
|
3740
|
-
"box-pack"
|
3720
|
+
"-moz-box-align" : "start | end | center | baseline | stretch",
|
3721
|
+
"-moz-box-decoration-break" : "slice |clone",
|
3722
|
+
"-moz-box-direction" : "normal | reverse | inherit",
|
3723
|
+
"-moz-box-flex" : "<number>",
|
3724
|
+
"-moz-box-flex-group" : "<integer>",
|
3725
|
+
"-moz-box-lines" : "single | multiple",
|
3726
|
+
"-moz-box-ordinal-group" : "<integer>",
|
3727
|
+
"-moz-box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit",
|
3728
|
+
"-moz-box-pack" : "start | end | center | justify",
|
3729
|
+
"-webkit-box-align" : "start | end | center | baseline | stretch",
|
3730
|
+
"-webkit-box-decoration-break" : "slice |clone",
|
3731
|
+
"-webkit-box-direction" : "normal | reverse | inherit",
|
3732
|
+
"-webkit-box-flex" : "<number>",
|
3733
|
+
"-webkit-box-flex-group" : "<integer>",
|
3734
|
+
"-webkit-box-lines" : "single | multiple",
|
3735
|
+
"-webkit-box-ordinal-group" : "<integer>",
|
3736
|
+
"-webkit-box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit",
|
3737
|
+
"-webkit-box-pack" : "start | end | center | justify",
|
3741
3738
|
"box-shadow" : function (expression) {
|
3742
3739
|
var result = false,
|
3743
3740
|
part;
|
@@ -3783,7 +3780,7 @@ var Properties = {
|
|
3783
3780
|
|
3784
3781
|
//D
|
3785
3782
|
"direction" : "ltr | rtl | inherit",
|
3786
|
-
"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 |
|
3783
|
+
"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 | none | inherit | -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",
|
3787
3784
|
"dominant-baseline" : 1,
|
3788
3785
|
"drop-initial-after-adjust" : "central | middle | after-edge | text-after-edge | ideographic | alphabetic | mathematical | <percentage> | <length>",
|
3789
3786
|
"drop-initial-after-align" : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical",
|
@@ -3800,6 +3797,26 @@ var Properties = {
|
|
3800
3797
|
"filter" : 1,
|
3801
3798
|
"fit" : "fill | hidden | meet | slice",
|
3802
3799
|
"fit-position" : 1,
|
3800
|
+
"flex" : "none | [ <flex-grow> <flex-shrink>? || <flex-basis>",
|
3801
|
+
"flex-basis" : "<width>",
|
3802
|
+
"flex-direction" : "row | row-reverse | column | column-reverse",
|
3803
|
+
"flex-flow" : "<flex-direction> || <flex-wrap>",
|
3804
|
+
"flex-grow" : "<number>",
|
3805
|
+
"flex-shrink" : "<number>",
|
3806
|
+
"flex-wrap" : "nowrap | wrap | wrap-reverse",
|
3807
|
+
"-webkit-flex" : "none | [ <flex-grow> <flex-shrink>? || <flex-basis>",
|
3808
|
+
"-webkit-flex-basis" : "<width>",
|
3809
|
+
"-webkit-flex-direction" : "row | row-reverse | column | column-reverse",
|
3810
|
+
"-webkit-flex-flow" : "<flex-direction> || <flex-wrap>",
|
3811
|
+
"-webkit-flex-grow" : "<number>",
|
3812
|
+
"-webkit-flex-shrink" : "<number>",
|
3813
|
+
"-webkit-flex-wrap" : "nowrap | wrap | wrap-reverse",
|
3814
|
+
"-ms-flex" : "[[ <number> <number>? ] || [ <length> || <percentage> || auto ] ] | none",
|
3815
|
+
"-ms-flex-align" : "start | end | center | stretch | baseline",
|
3816
|
+
"-ms-flex-direction" : "row | column | row-reverse | column-reverse | inherit",
|
3817
|
+
"-ms-flex-order" : "<number>",
|
3818
|
+
"-ms-flex-pack" : "start | end | center | justify",
|
3819
|
+
"-ms-flex-wrap" : "nowrap | wrap | wrap-reverse",
|
3803
3820
|
"float" : "left | right | none | inherit",
|
3804
3821
|
"float-offset" : 1,
|
3805
3822
|
"font" : 1,
|
@@ -3843,6 +3860,10 @@ var Properties = {
|
|
3843
3860
|
"image-resolution" : 1,
|
3844
3861
|
"inline-box-align" : "initial | last | <integer>",
|
3845
3862
|
|
3863
|
+
//J
|
3864
|
+
"justify-content" : "flex-start | flex-end | center | space-between | space-around",
|
3865
|
+
"-webkit-justify-content" : "flex-start | flex-end | center | space-between | space-around",
|
3866
|
+
|
3846
3867
|
//L
|
3847
3868
|
"left" : "<margin-width> | inherit",
|
3848
3869
|
"letter-spacing" : "<length> | normal | inherit",
|
@@ -3886,6 +3907,8 @@ var Properties = {
|
|
3886
3907
|
|
3887
3908
|
//O
|
3888
3909
|
"opacity" : "<number> | inherit",
|
3910
|
+
"order" : "<integer>",
|
3911
|
+
"-webkit-order" : "<integer>",
|
3889
3912
|
"orphans" : "<integer> | inherit",
|
3890
3913
|
"outline" : 1,
|
3891
3914
|
"outline-color" : "<color> | invert | inherit",
|
@@ -3894,6 +3917,7 @@ var Properties = {
|
|
3894
3917
|
"outline-width" : "<border-width> | inherit",
|
3895
3918
|
"overflow" : "visible | hidden | scroll | auto | inherit",
|
3896
3919
|
"overflow-style" : 1,
|
3920
|
+
"overflow-wrap" : "normal | break-word",
|
3897
3921
|
"overflow-x" : 1,
|
3898
3922
|
"overflow-y" : 1,
|
3899
3923
|
|
@@ -3971,6 +3995,8 @@ var Properties = {
|
|
3971
3995
|
"text-transform" : "capitalize | uppercase | lowercase | none | inherit",
|
3972
3996
|
"text-wrap" : "normal | none | avoid",
|
3973
3997
|
"top" : "<margin-width> | inherit",
|
3998
|
+
"-ms-touch-action" : "auto | none | pan-x | pan-y",
|
3999
|
+
"touch-action" : "auto | none | pan-x | pan-y",
|
3974
4000
|
"transform" : 1,
|
3975
4001
|
"transform-origin" : 1,
|
3976
4002
|
"transform-style" : 1,
|
@@ -3981,7 +4007,7 @@ var Properties = {
|
|
3981
4007
|
"transition-timing-function" : 1,
|
3982
4008
|
|
3983
4009
|
//U
|
3984
|
-
"unicode-bidi" : "normal | embed | bidi-override | inherit",
|
4010
|
+
"unicode-bidi" : "normal | embed | isolate | bidi-override | isolate-override | plaintext | inherit",
|
3985
4011
|
"user-modify" : "read-only | read-write | write-only | inherit",
|
3986
4012
|
"user-select" : "none | text | toggle | element | elements | all | inherit",
|
3987
4013
|
|
@@ -4005,7 +4031,8 @@ var Properties = {
|
|
4005
4031
|
"width" : "<length> | <percentage> | auto | inherit" ,
|
4006
4032
|
"word-break" : "normal | keep-all | break-all",
|
4007
4033
|
"word-spacing" : "<length> | normal | inherit",
|
4008
|
-
"word-wrap" :
|
4034
|
+
"word-wrap" : "normal | break-word",
|
4035
|
+
"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 | inherit",
|
4009
4036
|
|
4010
4037
|
//Z
|
4011
4038
|
"z-index" : "<integer> | auto | inherit",
|
@@ -4276,9 +4303,6 @@ function PropertyValuePart(text, line, col){
|
|
4276
4303
|
|
4277
4304
|
}
|
4278
4305
|
|
4279
|
-
} else if (/^([+\-]?[\d\.]+)%$/i.test(text)){ //percentage
|
4280
|
-
this.type = "percentage";
|
4281
|
-
this.value = +RegExp.$1;
|
4282
4306
|
} else if (/^([+\-]?[\d\.]+)%$/i.test(text)){ //percentage
|
4283
4307
|
this.type = "percentage";
|
4284
4308
|
this.value = +RegExp.$1;
|
@@ -4375,6 +4399,7 @@ PropertyValuePart.prototype.constructor = PropertyValuePart;
|
|
4375
4399
|
PropertyValuePart.fromToken = function(token){
|
4376
4400
|
return new PropertyValuePart(token.value, token.startLine, token.startCol);
|
4377
4401
|
};
|
4402
|
+
|
4378
4403
|
var Pseudos = {
|
4379
4404
|
":first-letter": 1,
|
4380
4405
|
":first-line": 1,
|
@@ -4668,12 +4693,12 @@ function isIdentStart(c){
|
|
4668
4693
|
}
|
4669
4694
|
|
4670
4695
|
function mix(receiver, supplier){
|
4671
|
-
|
4672
|
-
|
4673
|
-
|
4674
|
-
|
4675
|
-
|
4676
|
-
|
4696
|
+
for (var prop in supplier){
|
4697
|
+
if (supplier.hasOwnProperty(prop)){
|
4698
|
+
receiver[prop] = supplier[prop];
|
4699
|
+
}
|
4700
|
+
}
|
4701
|
+
return receiver;
|
4677
4702
|
}
|
4678
4703
|
|
4679
4704
|
//-----------------------------------------------------------------------------
|
@@ -4689,7 +4714,7 @@ function mix(receiver, supplier){
|
|
4689
4714
|
* @namespace parserlib.css
|
4690
4715
|
*/
|
4691
4716
|
function TokenStream(input){
|
4692
|
-
|
4717
|
+
TokenStreamBase.call(this, input, Tokens);
|
4693
4718
|
}
|
4694
4719
|
|
4695
4720
|
TokenStream.prototype = mix(new TokenStreamBase(), {
|
@@ -5662,7 +5687,7 @@ var Tokens = [
|
|
5662
5687
|
{ name: "FONT_FACE_SYM", text: "@font-face"},
|
5663
5688
|
{ name: "CHARSET_SYM", text: "@charset"},
|
5664
5689
|
{ name: "NAMESPACE_SYM", text: "@namespace"},
|
5665
|
-
{ name: "VIEWPORT_SYM", text: "@viewport"},
|
5690
|
+
{ name: "VIEWPORT_SYM", text: ["@viewport", "@-ms-viewport"]},
|
5666
5691
|
{ name: "UNKNOWN_SYM" },
|
5667
5692
|
//{ name: "ATKEYWORD"},
|
5668
5693
|
|
@@ -6429,3223 +6454,3 @@ exports[prop] = parserlib[prop];
|
|
6429
6454
|
}
|
6430
6455
|
})();
|
6431
6456
|
|
6432
|
-
|
6433
|
-
/**
|
6434
|
-
* Main CSSLint object.
|
6435
|
-
* @class CSSLint
|
6436
|
-
* @static
|
6437
|
-
* @extends parserlib.util.EventTarget
|
6438
|
-
*/
|
6439
|
-
/*global parserlib, Reporter*/
|
6440
|
-
var CSSLint = (function(){
|
6441
|
-
|
6442
|
-
var rules = [],
|
6443
|
-
formatters = [],
|
6444
|
-
embeddedRuleset = /\/\*csslint([^\*]*)\*\//,
|
6445
|
-
api = new parserlib.util.EventTarget();
|
6446
|
-
|
6447
|
-
api.version = "0.10.0";
|
6448
|
-
|
6449
|
-
//-------------------------------------------------------------------------
|
6450
|
-
// Rule Management
|
6451
|
-
//-------------------------------------------------------------------------
|
6452
|
-
|
6453
|
-
/**
|
6454
|
-
* Adds a new rule to the engine.
|
6455
|
-
* @param {Object} rule The rule to add.
|
6456
|
-
* @method addRule
|
6457
|
-
*/
|
6458
|
-
api.addRule = function(rule){
|
6459
|
-
rules.push(rule);
|
6460
|
-
rules[rule.id] = rule;
|
6461
|
-
};
|
6462
|
-
|
6463
|
-
/**
|
6464
|
-
* Clears all rule from the engine.
|
6465
|
-
* @method clearRules
|
6466
|
-
*/
|
6467
|
-
api.clearRules = function(){
|
6468
|
-
rules = [];
|
6469
|
-
};
|
6470
|
-
|
6471
|
-
/**
|
6472
|
-
* Returns the rule objects.
|
6473
|
-
* @return An array of rule objects.
|
6474
|
-
* @method getRules
|
6475
|
-
*/
|
6476
|
-
api.getRules = function(){
|
6477
|
-
return [].concat(rules).sort(function(a,b){
|
6478
|
-
return a.id > b.id ? 1 : 0;
|
6479
|
-
});
|
6480
|
-
};
|
6481
|
-
|
6482
|
-
/**
|
6483
|
-
* Returns a ruleset configuration object with all current rules.
|
6484
|
-
* @return A ruleset object.
|
6485
|
-
* @method getRuleset
|
6486
|
-
*/
|
6487
|
-
api.getRuleset = function() {
|
6488
|
-
var ruleset = {},
|
6489
|
-
i = 0,
|
6490
|
-
len = rules.length;
|
6491
|
-
|
6492
|
-
while (i < len){
|
6493
|
-
ruleset[rules[i++].id] = 1; //by default, everything is a warning
|
6494
|
-
}
|
6495
|
-
|
6496
|
-
return ruleset;
|
6497
|
-
};
|
6498
|
-
|
6499
|
-
/**
|
6500
|
-
* Returns a ruleset object based on embedded rules.
|
6501
|
-
* @param {String} text A string of css containing embedded rules.
|
6502
|
-
* @param {Object} ruleset A ruleset object to modify.
|
6503
|
-
* @return {Object} A ruleset object.
|
6504
|
-
* @method getEmbeddedRuleset
|
6505
|
-
*/
|
6506
|
-
function applyEmbeddedRuleset(text, ruleset){
|
6507
|
-
var valueMap,
|
6508
|
-
embedded = text && text.match(embeddedRuleset),
|
6509
|
-
rules = embedded && embedded[1];
|
6510
|
-
|
6511
|
-
if (rules) {
|
6512
|
-
valueMap = {
|
6513
|
-
"true": 2, // true is error
|
6514
|
-
"": 1, // blank is warning
|
6515
|
-
"false": 0, // false is ignore
|
6516
|
-
|
6517
|
-
"2": 2, // explicit error
|
6518
|
-
"1": 1, // explicit warning
|
6519
|
-
"0": 0 // explicit ignore
|
6520
|
-
};
|
6521
|
-
|
6522
|
-
rules.toLowerCase().split(",").forEach(function(rule){
|
6523
|
-
var pair = rule.split(":"),
|
6524
|
-
property = pair[0] || "",
|
6525
|
-
value = pair[1] || "";
|
6526
|
-
|
6527
|
-
ruleset[property.trim()] = valueMap[value.trim()];
|
6528
|
-
});
|
6529
|
-
}
|
6530
|
-
|
6531
|
-
return ruleset;
|
6532
|
-
}
|
6533
|
-
|
6534
|
-
//-------------------------------------------------------------------------
|
6535
|
-
// Formatters
|
6536
|
-
//-------------------------------------------------------------------------
|
6537
|
-
|
6538
|
-
/**
|
6539
|
-
* Adds a new formatter to the engine.
|
6540
|
-
* @param {Object} formatter The formatter to add.
|
6541
|
-
* @method addFormatter
|
6542
|
-
*/
|
6543
|
-
api.addFormatter = function(formatter) {
|
6544
|
-
// formatters.push(formatter);
|
6545
|
-
formatters[formatter.id] = formatter;
|
6546
|
-
};
|
6547
|
-
|
6548
|
-
/**
|
6549
|
-
* Retrieves a formatter for use.
|
6550
|
-
* @param {String} formatId The name of the format to retrieve.
|
6551
|
-
* @return {Object} The formatter or undefined.
|
6552
|
-
* @method getFormatter
|
6553
|
-
*/
|
6554
|
-
api.getFormatter = function(formatId){
|
6555
|
-
return formatters[formatId];
|
6556
|
-
};
|
6557
|
-
|
6558
|
-
/**
|
6559
|
-
* Formats the results in a particular format for a single file.
|
6560
|
-
* @param {Object} result The results returned from CSSLint.verify().
|
6561
|
-
* @param {String} filename The filename for which the results apply.
|
6562
|
-
* @param {String} formatId The name of the formatter to use.
|
6563
|
-
* @param {Object} options (Optional) for special output handling.
|
6564
|
-
* @return {String} A formatted string for the results.
|
6565
|
-
* @method format
|
6566
|
-
*/
|
6567
|
-
api.format = function(results, filename, formatId, options) {
|
6568
|
-
var formatter = this.getFormatter(formatId),
|
6569
|
-
result = null;
|
6570
|
-
|
6571
|
-
if (formatter){
|
6572
|
-
result = formatter.startFormat();
|
6573
|
-
result += formatter.formatResults(results, filename, options || {});
|
6574
|
-
result += formatter.endFormat();
|
6575
|
-
}
|
6576
|
-
|
6577
|
-
return result;
|
6578
|
-
};
|
6579
|
-
|
6580
|
-
/**
|
6581
|
-
* Indicates if the given format is supported.
|
6582
|
-
* @param {String} formatId The ID of the format to check.
|
6583
|
-
* @return {Boolean} True if the format exists, false if not.
|
6584
|
-
* @method hasFormat
|
6585
|
-
*/
|
6586
|
-
api.hasFormat = function(formatId){
|
6587
|
-
return formatters.hasOwnProperty(formatId);
|
6588
|
-
};
|
6589
|
-
|
6590
|
-
//-------------------------------------------------------------------------
|
6591
|
-
// Verification
|
6592
|
-
//-------------------------------------------------------------------------
|
6593
|
-
|
6594
|
-
/**
|
6595
|
-
* Starts the verification process for the given CSS text.
|
6596
|
-
* @param {String} text The CSS text to verify.
|
6597
|
-
* @param {Object} ruleset (Optional) List of rules to apply. If null, then
|
6598
|
-
* all rules are used. If a rule has a value of 1 then it's a warning,
|
6599
|
-
* a value of 2 means it's an error.
|
6600
|
-
* @return {Object} Results of the verification.
|
6601
|
-
* @method verify
|
6602
|
-
*/
|
6603
|
-
api.verify = function(text, ruleset){
|
6604
|
-
|
6605
|
-
var i = 0,
|
6606
|
-
len = rules.length,
|
6607
|
-
reporter,
|
6608
|
-
lines,
|
6609
|
-
report,
|
6610
|
-
parser = new parserlib.css.Parser({ starHack: true, ieFilters: true,
|
6611
|
-
underscoreHack: true, strict: false });
|
6612
|
-
|
6613
|
-
// normalize line endings
|
6614
|
-
lines = text.replace(/\n\r?/g, "$split$").split('$split$');
|
6615
|
-
|
6616
|
-
if (!ruleset){
|
6617
|
-
ruleset = this.getRuleset();
|
6618
|
-
}
|
6619
|
-
|
6620
|
-
if (embeddedRuleset.test(text)){
|
6621
|
-
ruleset = applyEmbeddedRuleset(text, ruleset);
|
6622
|
-
}
|
6623
|
-
|
6624
|
-
reporter = new Reporter(lines, ruleset);
|
6625
|
-
|
6626
|
-
ruleset.errors = 2; //always report parsing errors as errors
|
6627
|
-
for (i in ruleset){
|
6628
|
-
if(ruleset.hasOwnProperty(i) && ruleset[i]){
|
6629
|
-
if (rules[i]){
|
6630
|
-
rules[i].init(parser, reporter);
|
6631
|
-
}
|
6632
|
-
}
|
6633
|
-
}
|
6634
|
-
|
6635
|
-
|
6636
|
-
//capture most horrible error type
|
6637
|
-
try {
|
6638
|
-
parser.parse(text);
|
6639
|
-
} catch (ex) {
|
6640
|
-
reporter.error("Fatal error, cannot continue: " + ex.message, ex.line, ex.col, {});
|
6641
|
-
}
|
6642
|
-
|
6643
|
-
report = {
|
6644
|
-
messages : reporter.messages,
|
6645
|
-
stats : reporter.stats,
|
6646
|
-
ruleset : reporter.ruleset
|
6647
|
-
};
|
6648
|
-
|
6649
|
-
//sort by line numbers, rollups at the bottom
|
6650
|
-
report.messages.sort(function (a, b){
|
6651
|
-
if (a.rollup && !b.rollup){
|
6652
|
-
return 1;
|
6653
|
-
} else if (!a.rollup && b.rollup){
|
6654
|
-
return -1;
|
6655
|
-
} else {
|
6656
|
-
return a.line - b.line;
|
6657
|
-
}
|
6658
|
-
});
|
6659
|
-
|
6660
|
-
return report;
|
6661
|
-
};
|
6662
|
-
|
6663
|
-
//-------------------------------------------------------------------------
|
6664
|
-
// Publish the API
|
6665
|
-
//-------------------------------------------------------------------------
|
6666
|
-
|
6667
|
-
return api;
|
6668
|
-
|
6669
|
-
})();
|
6670
|
-
|
6671
|
-
/*global CSSLint*/
|
6672
|
-
/**
|
6673
|
-
* An instance of Report is used to report results of the
|
6674
|
-
* verification back to the main API.
|
6675
|
-
* @class Reporter
|
6676
|
-
* @constructor
|
6677
|
-
* @param {String[]} lines The text lines of the source.
|
6678
|
-
* @param {Object} ruleset The set of rules to work with, including if
|
6679
|
-
* they are errors or warnings.
|
6680
|
-
*/
|
6681
|
-
function Reporter(lines, ruleset){
|
6682
|
-
|
6683
|
-
/**
|
6684
|
-
* List of messages being reported.
|
6685
|
-
* @property messages
|
6686
|
-
* @type String[]
|
6687
|
-
*/
|
6688
|
-
this.messages = [];
|
6689
|
-
|
6690
|
-
/**
|
6691
|
-
* List of statistics being reported.
|
6692
|
-
* @property stats
|
6693
|
-
* @type String[]
|
6694
|
-
*/
|
6695
|
-
this.stats = [];
|
6696
|
-
|
6697
|
-
/**
|
6698
|
-
* Lines of code being reported on. Used to provide contextual information
|
6699
|
-
* for messages.
|
6700
|
-
* @property lines
|
6701
|
-
* @type String[]
|
6702
|
-
*/
|
6703
|
-
this.lines = lines;
|
6704
|
-
|
6705
|
-
/**
|
6706
|
-
* Information about the rules. Used to determine whether an issue is an
|
6707
|
-
* error or warning.
|
6708
|
-
* @property ruleset
|
6709
|
-
* @type Object
|
6710
|
-
*/
|
6711
|
-
this.ruleset = ruleset;
|
6712
|
-
}
|
6713
|
-
|
6714
|
-
Reporter.prototype = {
|
6715
|
-
|
6716
|
-
//restore constructor
|
6717
|
-
constructor: Reporter,
|
6718
|
-
|
6719
|
-
/**
|
6720
|
-
* Report an error.
|
6721
|
-
* @param {String} message The message to store.
|
6722
|
-
* @param {int} line The line number.
|
6723
|
-
* @param {int} col The column number.
|
6724
|
-
* @param {Object} rule The rule this message relates to.
|
6725
|
-
* @method error
|
6726
|
-
*/
|
6727
|
-
error: function(message, line, col, rule){
|
6728
|
-
this.messages.push({
|
6729
|
-
type : "error",
|
6730
|
-
line : line,
|
6731
|
-
col : col,
|
6732
|
-
message : message,
|
6733
|
-
evidence: this.lines[line-1],
|
6734
|
-
rule : rule || {}
|
6735
|
-
});
|
6736
|
-
},
|
6737
|
-
|
6738
|
-
/**
|
6739
|
-
* Report an warning.
|
6740
|
-
* @param {String} message The message to store.
|
6741
|
-
* @param {int} line The line number.
|
6742
|
-
* @param {int} col The column number.
|
6743
|
-
* @param {Object} rule The rule this message relates to.
|
6744
|
-
* @method warn
|
6745
|
-
* @deprecated Use report instead.
|
6746
|
-
*/
|
6747
|
-
warn: function(message, line, col, rule){
|
6748
|
-
this.report(message, line, col, rule);
|
6749
|
-
},
|
6750
|
-
|
6751
|
-
/**
|
6752
|
-
* Report an issue.
|
6753
|
-
* @param {String} message The message to store.
|
6754
|
-
* @param {int} line The line number.
|
6755
|
-
* @param {int} col The column number.
|
6756
|
-
* @param {Object} rule The rule this message relates to.
|
6757
|
-
* @method report
|
6758
|
-
*/
|
6759
|
-
report: function(message, line, col, rule){
|
6760
|
-
this.messages.push({
|
6761
|
-
type : this.ruleset[rule.id] == 2 ? "error" : "warning",
|
6762
|
-
line : line,
|
6763
|
-
col : col,
|
6764
|
-
message : message,
|
6765
|
-
evidence: this.lines[line-1],
|
6766
|
-
rule : rule
|
6767
|
-
});
|
6768
|
-
},
|
6769
|
-
|
6770
|
-
/**
|
6771
|
-
* Report some informational text.
|
6772
|
-
* @param {String} message The message to store.
|
6773
|
-
* @param {int} line The line number.
|
6774
|
-
* @param {int} col The column number.
|
6775
|
-
* @param {Object} rule The rule this message relates to.
|
6776
|
-
* @method info
|
6777
|
-
*/
|
6778
|
-
info: function(message, line, col, rule){
|
6779
|
-
this.messages.push({
|
6780
|
-
type : "info",
|
6781
|
-
line : line,
|
6782
|
-
col : col,
|
6783
|
-
message : message,
|
6784
|
-
evidence: this.lines[line-1],
|
6785
|
-
rule : rule
|
6786
|
-
});
|
6787
|
-
},
|
6788
|
-
|
6789
|
-
/**
|
6790
|
-
* Report some rollup error information.
|
6791
|
-
* @param {String} message The message to store.
|
6792
|
-
* @param {Object} rule The rule this message relates to.
|
6793
|
-
* @method rollupError
|
6794
|
-
*/
|
6795
|
-
rollupError: function(message, rule){
|
6796
|
-
this.messages.push({
|
6797
|
-
type : "error",
|
6798
|
-
rollup : true,
|
6799
|
-
message : message,
|
6800
|
-
rule : rule
|
6801
|
-
});
|
6802
|
-
},
|
6803
|
-
|
6804
|
-
/**
|
6805
|
-
* Report some rollup warning information.
|
6806
|
-
* @param {String} message The message to store.
|
6807
|
-
* @param {Object} rule The rule this message relates to.
|
6808
|
-
* @method rollupWarn
|
6809
|
-
*/
|
6810
|
-
rollupWarn: function(message, rule){
|
6811
|
-
this.messages.push({
|
6812
|
-
type : "warning",
|
6813
|
-
rollup : true,
|
6814
|
-
message : message,
|
6815
|
-
rule : rule
|
6816
|
-
});
|
6817
|
-
},
|
6818
|
-
|
6819
|
-
/**
|
6820
|
-
* Report a statistic.
|
6821
|
-
* @param {String} name The name of the stat to store.
|
6822
|
-
* @param {Variant} value The value of the stat.
|
6823
|
-
* @method stat
|
6824
|
-
*/
|
6825
|
-
stat: function(name, value){
|
6826
|
-
this.stats[name] = value;
|
6827
|
-
}
|
6828
|
-
};
|
6829
|
-
|
6830
|
-
//expose for testing purposes
|
6831
|
-
CSSLint._Reporter = Reporter;
|
6832
|
-
|
6833
|
-
/*global CSSLint*/
|
6834
|
-
|
6835
|
-
/*
|
6836
|
-
* Utility functions that make life easier.
|
6837
|
-
*/
|
6838
|
-
CSSLint.Util = {
|
6839
|
-
/*
|
6840
|
-
* Adds all properties from supplier onto receiver,
|
6841
|
-
* overwriting if the same name already exists on
|
6842
|
-
* reciever.
|
6843
|
-
* @param {Object} The object to receive the properties.
|
6844
|
-
* @param {Object} The object to provide the properties.
|
6845
|
-
* @return {Object} The receiver
|
6846
|
-
*/
|
6847
|
-
mix: function(receiver, supplier){
|
6848
|
-
var prop;
|
6849
|
-
|
6850
|
-
for (prop in supplier){
|
6851
|
-
if (supplier.hasOwnProperty(prop)){
|
6852
|
-
receiver[prop] = supplier[prop];
|
6853
|
-
}
|
6854
|
-
}
|
6855
|
-
|
6856
|
-
return prop;
|
6857
|
-
},
|
6858
|
-
|
6859
|
-
/*
|
6860
|
-
* Polyfill for array indexOf() method.
|
6861
|
-
* @param {Array} values The array to search.
|
6862
|
-
* @param {Variant} value The value to search for.
|
6863
|
-
* @return {int} The index of the value if found, -1 if not.
|
6864
|
-
*/
|
6865
|
-
indexOf: function(values, value){
|
6866
|
-
if (values.indexOf){
|
6867
|
-
return values.indexOf(value);
|
6868
|
-
} else {
|
6869
|
-
for (var i=0, len=values.length; i < len; i++){
|
6870
|
-
if (values[i] === value){
|
6871
|
-
return i;
|
6872
|
-
}
|
6873
|
-
}
|
6874
|
-
return -1;
|
6875
|
-
}
|
6876
|
-
},
|
6877
|
-
|
6878
|
-
/*
|
6879
|
-
* Polyfill for array forEach() method.
|
6880
|
-
* @param {Array} values The array to operate on.
|
6881
|
-
* @param {Function} func The function to call on each item.
|
6882
|
-
* @return {void}
|
6883
|
-
*/
|
6884
|
-
forEach: function(values, func) {
|
6885
|
-
if (values.forEach){
|
6886
|
-
return values.forEach(func);
|
6887
|
-
} else {
|
6888
|
-
for (var i=0, len=values.length; i < len; i++){
|
6889
|
-
func(values[i], i, values);
|
6890
|
-
}
|
6891
|
-
}
|
6892
|
-
}
|
6893
|
-
};
|
6894
|
-
/*global CSSLint*/
|
6895
|
-
/*
|
6896
|
-
* Rule: Don't use adjoining classes (.foo.bar).
|
6897
|
-
*/
|
6898
|
-
CSSLint.addRule({
|
6899
|
-
|
6900
|
-
//rule information
|
6901
|
-
id: "adjoining-classes",
|
6902
|
-
name: "Disallow adjoining classes",
|
6903
|
-
desc: "Don't use adjoining classes.",
|
6904
|
-
browsers: "IE6",
|
6905
|
-
|
6906
|
-
//initialization
|
6907
|
-
init: function(parser, reporter){
|
6908
|
-
var rule = this;
|
6909
|
-
parser.addListener("startrule", function(event){
|
6910
|
-
var selectors = event.selectors,
|
6911
|
-
selector,
|
6912
|
-
part,
|
6913
|
-
modifier,
|
6914
|
-
classCount,
|
6915
|
-
i, j, k;
|
6916
|
-
|
6917
|
-
for (i=0; i < selectors.length; i++){
|
6918
|
-
selector = selectors[i];
|
6919
|
-
for (j=0; j < selector.parts.length; j++){
|
6920
|
-
part = selector.parts[j];
|
6921
|
-
if (part.type == parser.SELECTOR_PART_TYPE){
|
6922
|
-
classCount = 0;
|
6923
|
-
for (k=0; k < part.modifiers.length; k++){
|
6924
|
-
modifier = part.modifiers[k];
|
6925
|
-
if (modifier.type == "class"){
|
6926
|
-
classCount++;
|
6927
|
-
}
|
6928
|
-
if (classCount > 1){
|
6929
|
-
reporter.report("Don't use adjoining classes.", part.line, part.col, rule);
|
6930
|
-
}
|
6931
|
-
}
|
6932
|
-
}
|
6933
|
-
}
|
6934
|
-
}
|
6935
|
-
});
|
6936
|
-
}
|
6937
|
-
|
6938
|
-
});
|
6939
|
-
/*global CSSLint*/
|
6940
|
-
|
6941
|
-
/*
|
6942
|
-
* Rule: Don't use width or height when using padding or border.
|
6943
|
-
*/
|
6944
|
-
CSSLint.addRule({
|
6945
|
-
|
6946
|
-
//rule information
|
6947
|
-
id: "box-model",
|
6948
|
-
name: "Beware of broken box size",
|
6949
|
-
desc: "Don't use width or height when using padding or border.",
|
6950
|
-
browsers: "All",
|
6951
|
-
|
6952
|
-
//initialization
|
6953
|
-
init: function(parser, reporter){
|
6954
|
-
var rule = this,
|
6955
|
-
widthProperties = {
|
6956
|
-
border: 1,
|
6957
|
-
"border-left": 1,
|
6958
|
-
"border-right": 1,
|
6959
|
-
padding: 1,
|
6960
|
-
"padding-left": 1,
|
6961
|
-
"padding-right": 1
|
6962
|
-
},
|
6963
|
-
heightProperties = {
|
6964
|
-
border: 1,
|
6965
|
-
"border-bottom": 1,
|
6966
|
-
"border-top": 1,
|
6967
|
-
padding: 1,
|
6968
|
-
"padding-bottom": 1,
|
6969
|
-
"padding-top": 1
|
6970
|
-
},
|
6971
|
-
properties,
|
6972
|
-
boxSizing = false;
|
6973
|
-
|
6974
|
-
function startRule(){
|
6975
|
-
properties = {};
|
6976
|
-
boxSizing = false;
|
6977
|
-
}
|
6978
|
-
|
6979
|
-
function endRule(){
|
6980
|
-
var prop, value;
|
6981
|
-
|
6982
|
-
if (!boxSizing) {
|
6983
|
-
if (properties.height){
|
6984
|
-
for (prop in heightProperties){
|
6985
|
-
if (heightProperties.hasOwnProperty(prop) && properties[prop]){
|
6986
|
-
value = properties[prop].value;
|
6987
|
-
//special case for padding
|
6988
|
-
if (!(prop == "padding" && value.parts.length === 2 && value.parts[0].value === 0)){
|
6989
|
-
reporter.report("Using height with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
|
6990
|
-
}
|
6991
|
-
}
|
6992
|
-
}
|
6993
|
-
}
|
6994
|
-
|
6995
|
-
if (properties.width){
|
6996
|
-
for (prop in widthProperties){
|
6997
|
-
if (widthProperties.hasOwnProperty(prop) && properties[prop]){
|
6998
|
-
value = properties[prop].value;
|
6999
|
-
|
7000
|
-
if (!(prop == "padding" && value.parts.length === 2 && value.parts[1].value === 0)){
|
7001
|
-
reporter.report("Using width with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
|
7002
|
-
}
|
7003
|
-
}
|
7004
|
-
}
|
7005
|
-
}
|
7006
|
-
}
|
7007
|
-
}
|
7008
|
-
|
7009
|
-
parser.addListener("startrule", startRule);
|
7010
|
-
parser.addListener("startfontface", startRule);
|
7011
|
-
parser.addListener("startpage", startRule);
|
7012
|
-
parser.addListener("startpagemargin", startRule);
|
7013
|
-
parser.addListener("startkeyframerule", startRule);
|
7014
|
-
|
7015
|
-
parser.addListener("property", function(event){
|
7016
|
-
var name = event.property.text.toLowerCase();
|
7017
|
-
|
7018
|
-
if (heightProperties[name] || widthProperties[name]){
|
7019
|
-
if (!/^0\S*$/.test(event.value) && !(name == "border" && event.value == "none")){
|
7020
|
-
properties[name] = { line: event.property.line, col: event.property.col, value: event.value };
|
7021
|
-
}
|
7022
|
-
} else {
|
7023
|
-
if (/^(width|height)/i.test(name) && /^(length|percentage)/.test(event.value.parts[0].type)){
|
7024
|
-
properties[name] = 1;
|
7025
|
-
} else if (name == "box-sizing") {
|
7026
|
-
boxSizing = true;
|
7027
|
-
}
|
7028
|
-
}
|
7029
|
-
|
7030
|
-
});
|
7031
|
-
|
7032
|
-
parser.addListener("endrule", endRule);
|
7033
|
-
parser.addListener("endfontface", endRule);
|
7034
|
-
parser.addListener("endpage", endRule);
|
7035
|
-
parser.addListener("endpagemargin", endRule);
|
7036
|
-
parser.addListener("endkeyframerule", endRule);
|
7037
|
-
}
|
7038
|
-
|
7039
|
-
});
|
7040
|
-
/*global CSSLint*/
|
7041
|
-
|
7042
|
-
/*
|
7043
|
-
* Rule: box-sizing doesn't work in IE6 and IE7.
|
7044
|
-
*/
|
7045
|
-
CSSLint.addRule({
|
7046
|
-
|
7047
|
-
//rule information
|
7048
|
-
id: "box-sizing",
|
7049
|
-
name: "Disallow use of box-sizing",
|
7050
|
-
desc: "The box-sizing properties isn't supported in IE6 and IE7.",
|
7051
|
-
browsers: "IE6, IE7",
|
7052
|
-
tags: ["Compatibility"],
|
7053
|
-
|
7054
|
-
//initialization
|
7055
|
-
init: function(parser, reporter){
|
7056
|
-
var rule = this;
|
7057
|
-
|
7058
|
-
parser.addListener("property", function(event){
|
7059
|
-
var name = event.property.text.toLowerCase();
|
7060
|
-
|
7061
|
-
if (name == "box-sizing"){
|
7062
|
-
reporter.report("The box-sizing property isn't supported in IE6 and IE7.", event.line, event.col, rule);
|
7063
|
-
}
|
7064
|
-
});
|
7065
|
-
}
|
7066
|
-
|
7067
|
-
});
|
7068
|
-
/*
|
7069
|
-
* Rule: Use the bulletproof @font-face syntax to avoid 404's in old IE
|
7070
|
-
* (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax)
|
7071
|
-
*/
|
7072
|
-
/*global CSSLint*/
|
7073
|
-
CSSLint.addRule({
|
7074
|
-
|
7075
|
-
//rule information
|
7076
|
-
id: "bulletproof-font-face",
|
7077
|
-
name: "Use the bulletproof @font-face syntax",
|
7078
|
-
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).",
|
7079
|
-
browsers: "All",
|
7080
|
-
|
7081
|
-
//initialization
|
7082
|
-
init: function(parser, reporter){
|
7083
|
-
var rule = this,
|
7084
|
-
count = 0,
|
7085
|
-
fontFaceRule = false,
|
7086
|
-
firstSrc = true,
|
7087
|
-
ruleFailed = false,
|
7088
|
-
line, col;
|
7089
|
-
|
7090
|
-
// Mark the start of a @font-face declaration so we only test properties inside it
|
7091
|
-
parser.addListener("startfontface", function(event){
|
7092
|
-
fontFaceRule = true;
|
7093
|
-
});
|
7094
|
-
|
7095
|
-
parser.addListener("property", function(event){
|
7096
|
-
// If we aren't inside an @font-face declaration then just return
|
7097
|
-
if (!fontFaceRule) {
|
7098
|
-
return;
|
7099
|
-
}
|
7100
|
-
|
7101
|
-
var propertyName = event.property.toString().toLowerCase(),
|
7102
|
-
value = event.value.toString();
|
7103
|
-
|
7104
|
-
// Set the line and col numbers for use in the endfontface listener
|
7105
|
-
line = event.line;
|
7106
|
-
col = event.col;
|
7107
|
-
|
7108
|
-
// This is the property that we care about, we can ignore the rest
|
7109
|
-
if (propertyName === 'src') {
|
7110
|
-
var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i;
|
7111
|
-
|
7112
|
-
// We need to handle the advanced syntax with two src properties
|
7113
|
-
if (!value.match(regex) && firstSrc) {
|
7114
|
-
ruleFailed = true;
|
7115
|
-
firstSrc = false;
|
7116
|
-
} else if (value.match(regex) && !firstSrc) {
|
7117
|
-
ruleFailed = false;
|
7118
|
-
}
|
7119
|
-
}
|
7120
|
-
|
7121
|
-
|
7122
|
-
});
|
7123
|
-
|
7124
|
-
// Back to normal rules that we don't need to test
|
7125
|
-
parser.addListener("endfontface", function(event){
|
7126
|
-
fontFaceRule = false;
|
7127
|
-
|
7128
|
-
if (ruleFailed) {
|
7129
|
-
reporter.report("@font-face declaration doesn't follow the fontspring bulletproof syntax.", line, col, rule);
|
7130
|
-
}
|
7131
|
-
});
|
7132
|
-
}
|
7133
|
-
});
|
7134
|
-
/*
|
7135
|
-
* Rule: Include all compatible vendor prefixes to reach a wider
|
7136
|
-
* range of users.
|
7137
|
-
*/
|
7138
|
-
/*global CSSLint*/
|
7139
|
-
CSSLint.addRule({
|
7140
|
-
|
7141
|
-
//rule information
|
7142
|
-
id: "compatible-vendor-prefixes",
|
7143
|
-
name: "Require compatible vendor prefixes",
|
7144
|
-
desc: "Include all compatible vendor prefixes to reach a wider range of users.",
|
7145
|
-
browsers: "All",
|
7146
|
-
|
7147
|
-
//initialization
|
7148
|
-
init: function (parser, reporter) {
|
7149
|
-
var rule = this,
|
7150
|
-
compatiblePrefixes,
|
7151
|
-
properties,
|
7152
|
-
prop,
|
7153
|
-
variations,
|
7154
|
-
prefixed,
|
7155
|
-
i,
|
7156
|
-
len,
|
7157
|
-
inKeyFrame = false,
|
7158
|
-
arrayPush = Array.prototype.push,
|
7159
|
-
applyTo = [];
|
7160
|
-
|
7161
|
-
// See http://peter.sh/experiments/vendor-prefixed-css-property-overview/ for details
|
7162
|
-
compatiblePrefixes = {
|
7163
|
-
"animation" : "webkit moz",
|
7164
|
-
"animation-delay" : "webkit moz",
|
7165
|
-
"animation-direction" : "webkit moz",
|
7166
|
-
"animation-duration" : "webkit moz",
|
7167
|
-
"animation-fill-mode" : "webkit moz",
|
7168
|
-
"animation-iteration-count" : "webkit moz",
|
7169
|
-
"animation-name" : "webkit moz",
|
7170
|
-
"animation-play-state" : "webkit moz",
|
7171
|
-
"animation-timing-function" : "webkit moz",
|
7172
|
-
"appearance" : "webkit moz",
|
7173
|
-
"border-end" : "webkit moz",
|
7174
|
-
"border-end-color" : "webkit moz",
|
7175
|
-
"border-end-style" : "webkit moz",
|
7176
|
-
"border-end-width" : "webkit moz",
|
7177
|
-
"border-image" : "webkit moz o",
|
7178
|
-
"border-radius" : "webkit",
|
7179
|
-
"border-start" : "webkit moz",
|
7180
|
-
"border-start-color" : "webkit moz",
|
7181
|
-
"border-start-style" : "webkit moz",
|
7182
|
-
"border-start-width" : "webkit moz",
|
7183
|
-
"box-align" : "webkit moz ms",
|
7184
|
-
"box-direction" : "webkit moz ms",
|
7185
|
-
"box-flex" : "webkit moz ms",
|
7186
|
-
"box-lines" : "webkit ms",
|
7187
|
-
"box-ordinal-group" : "webkit moz ms",
|
7188
|
-
"box-orient" : "webkit moz ms",
|
7189
|
-
"box-pack" : "webkit moz ms",
|
7190
|
-
"box-sizing" : "webkit moz",
|
7191
|
-
"box-shadow" : "webkit moz",
|
7192
|
-
"column-count" : "webkit moz ms",
|
7193
|
-
"column-gap" : "webkit moz ms",
|
7194
|
-
"column-rule" : "webkit moz ms",
|
7195
|
-
"column-rule-color" : "webkit moz ms",
|
7196
|
-
"column-rule-style" : "webkit moz ms",
|
7197
|
-
"column-rule-width" : "webkit moz ms",
|
7198
|
-
"column-width" : "webkit moz ms",
|
7199
|
-
"hyphens" : "epub moz",
|
7200
|
-
"line-break" : "webkit ms",
|
7201
|
-
"margin-end" : "webkit moz",
|
7202
|
-
"margin-start" : "webkit moz",
|
7203
|
-
"marquee-speed" : "webkit wap",
|
7204
|
-
"marquee-style" : "webkit wap",
|
7205
|
-
"padding-end" : "webkit moz",
|
7206
|
-
"padding-start" : "webkit moz",
|
7207
|
-
"tab-size" : "moz o",
|
7208
|
-
"text-size-adjust" : "webkit ms",
|
7209
|
-
"transform" : "webkit moz ms o",
|
7210
|
-
"transform-origin" : "webkit moz ms o",
|
7211
|
-
"transition" : "webkit moz o",
|
7212
|
-
"transition-delay" : "webkit moz o",
|
7213
|
-
"transition-duration" : "webkit moz o",
|
7214
|
-
"transition-property" : "webkit moz o",
|
7215
|
-
"transition-timing-function" : "webkit moz o",
|
7216
|
-
"user-modify" : "webkit moz",
|
7217
|
-
"user-select" : "webkit moz ms",
|
7218
|
-
"word-break" : "epub ms",
|
7219
|
-
"writing-mode" : "epub ms"
|
7220
|
-
};
|
7221
|
-
|
7222
|
-
|
7223
|
-
for (prop in compatiblePrefixes) {
|
7224
|
-
if (compatiblePrefixes.hasOwnProperty(prop)) {
|
7225
|
-
variations = [];
|
7226
|
-
prefixed = compatiblePrefixes[prop].split(' ');
|
7227
|
-
for (i = 0, len = prefixed.length; i < len; i++) {
|
7228
|
-
variations.push('-' + prefixed[i] + '-' + prop);
|
7229
|
-
}
|
7230
|
-
compatiblePrefixes[prop] = variations;
|
7231
|
-
arrayPush.apply(applyTo, variations);
|
7232
|
-
}
|
7233
|
-
}
|
7234
|
-
|
7235
|
-
parser.addListener("startrule", function () {
|
7236
|
-
properties = [];
|
7237
|
-
});
|
7238
|
-
|
7239
|
-
parser.addListener("startkeyframes", function (event) {
|
7240
|
-
inKeyFrame = event.prefix || true;
|
7241
|
-
});
|
7242
|
-
|
7243
|
-
parser.addListener("endkeyframes", function (event) {
|
7244
|
-
inKeyFrame = false;
|
7245
|
-
});
|
7246
|
-
|
7247
|
-
parser.addListener("property", function (event) {
|
7248
|
-
var name = event.property;
|
7249
|
-
if (CSSLint.Util.indexOf(applyTo, name.text) > -1) {
|
7250
|
-
|
7251
|
-
// e.g., -moz-transform is okay to be alone in @-moz-keyframes
|
7252
|
-
if (!inKeyFrame || typeof inKeyFrame != "string" ||
|
7253
|
-
name.text.indexOf("-" + inKeyFrame + "-") !== 0) {
|
7254
|
-
properties.push(name);
|
7255
|
-
}
|
7256
|
-
}
|
7257
|
-
});
|
7258
|
-
|
7259
|
-
parser.addListener("endrule", function (event) {
|
7260
|
-
if (!properties.length) {
|
7261
|
-
return;
|
7262
|
-
}
|
7263
|
-
|
7264
|
-
var propertyGroups = {},
|
7265
|
-
i,
|
7266
|
-
len,
|
7267
|
-
name,
|
7268
|
-
prop,
|
7269
|
-
variations,
|
7270
|
-
value,
|
7271
|
-
full,
|
7272
|
-
actual,
|
7273
|
-
item,
|
7274
|
-
propertiesSpecified;
|
7275
|
-
|
7276
|
-
for (i = 0, len = properties.length; i < len; i++) {
|
7277
|
-
name = properties[i];
|
7278
|
-
|
7279
|
-
for (prop in compatiblePrefixes) {
|
7280
|
-
if (compatiblePrefixes.hasOwnProperty(prop)) {
|
7281
|
-
variations = compatiblePrefixes[prop];
|
7282
|
-
if (CSSLint.Util.indexOf(variations, name.text) > -1) {
|
7283
|
-
if (!propertyGroups[prop]) {
|
7284
|
-
propertyGroups[prop] = {
|
7285
|
-
full : variations.slice(0),
|
7286
|
-
actual : [],
|
7287
|
-
actualNodes: []
|
7288
|
-
};
|
7289
|
-
}
|
7290
|
-
if (CSSLint.Util.indexOf(propertyGroups[prop].actual, name.text) === -1) {
|
7291
|
-
propertyGroups[prop].actual.push(name.text);
|
7292
|
-
propertyGroups[prop].actualNodes.push(name);
|
7293
|
-
}
|
7294
|
-
}
|
7295
|
-
}
|
7296
|
-
}
|
7297
|
-
}
|
7298
|
-
|
7299
|
-
for (prop in propertyGroups) {
|
7300
|
-
if (propertyGroups.hasOwnProperty(prop)) {
|
7301
|
-
value = propertyGroups[prop];
|
7302
|
-
full = value.full;
|
7303
|
-
actual = value.actual;
|
7304
|
-
|
7305
|
-
if (full.length > actual.length) {
|
7306
|
-
for (i = 0, len = full.length; i < len; i++) {
|
7307
|
-
item = full[i];
|
7308
|
-
if (CSSLint.Util.indexOf(actual, item) === -1) {
|
7309
|
-
propertiesSpecified = (actual.length === 1) ? actual[0] : (actual.length == 2) ? actual.join(" and ") : actual.join(", ");
|
7310
|
-
reporter.report("The property " + item + " is compatible with " + propertiesSpecified + " and should be included as well.", value.actualNodes[0].line, value.actualNodes[0].col, rule);
|
7311
|
-
}
|
7312
|
-
}
|
7313
|
-
|
7314
|
-
}
|
7315
|
-
}
|
7316
|
-
}
|
7317
|
-
});
|
7318
|
-
}
|
7319
|
-
});
|
7320
|
-
/*
|
7321
|
-
* Rule: Certain properties don't play well with certain display values.
|
7322
|
-
* - float should not be used with inline-block
|
7323
|
-
* - height, width, margin-top, margin-bottom, float should not be used with inline
|
7324
|
-
* - vertical-align should not be used with block
|
7325
|
-
* - margin, float should not be used with table-*
|
7326
|
-
*/
|
7327
|
-
/*global CSSLint*/
|
7328
|
-
CSSLint.addRule({
|
7329
|
-
|
7330
|
-
//rule information
|
7331
|
-
id: "display-property-grouping",
|
7332
|
-
name: "Require properties appropriate for display",
|
7333
|
-
desc: "Certain properties shouldn't be used with certain display property values.",
|
7334
|
-
browsers: "All",
|
7335
|
-
|
7336
|
-
//initialization
|
7337
|
-
init: function(parser, reporter){
|
7338
|
-
var rule = this;
|
7339
|
-
|
7340
|
-
var propertiesToCheck = {
|
7341
|
-
display: 1,
|
7342
|
-
"float": "none",
|
7343
|
-
height: 1,
|
7344
|
-
width: 1,
|
7345
|
-
margin: 1,
|
7346
|
-
"margin-left": 1,
|
7347
|
-
"margin-right": 1,
|
7348
|
-
"margin-bottom": 1,
|
7349
|
-
"margin-top": 1,
|
7350
|
-
padding: 1,
|
7351
|
-
"padding-left": 1,
|
7352
|
-
"padding-right": 1,
|
7353
|
-
"padding-bottom": 1,
|
7354
|
-
"padding-top": 1,
|
7355
|
-
"vertical-align": 1
|
7356
|
-
},
|
7357
|
-
properties;
|
7358
|
-
|
7359
|
-
function reportProperty(name, display, msg){
|
7360
|
-
if (properties[name]){
|
7361
|
-
if (typeof propertiesToCheck[name] != "string" || properties[name].value.toLowerCase() != propertiesToCheck[name]){
|
7362
|
-
reporter.report(msg || name + " can't be used with display: " + display + ".", properties[name].line, properties[name].col, rule);
|
7363
|
-
}
|
7364
|
-
}
|
7365
|
-
}
|
7366
|
-
|
7367
|
-
function startRule(){
|
7368
|
-
properties = {};
|
7369
|
-
}
|
7370
|
-
|
7371
|
-
function endRule(){
|
7372
|
-
|
7373
|
-
var display = properties.display ? properties.display.value : null;
|
7374
|
-
if (display){
|
7375
|
-
switch(display){
|
7376
|
-
|
7377
|
-
case "inline":
|
7378
|
-
//height, width, margin-top, margin-bottom, float should not be used with inline
|
7379
|
-
reportProperty("height", display);
|
7380
|
-
reportProperty("width", display);
|
7381
|
-
reportProperty("margin", display);
|
7382
|
-
reportProperty("margin-top", display);
|
7383
|
-
reportProperty("margin-bottom", display);
|
7384
|
-
reportProperty("float", display, "display:inline has no effect on floated elements (but may be used to fix the IE6 double-margin bug).");
|
7385
|
-
break;
|
7386
|
-
|
7387
|
-
case "block":
|
7388
|
-
//vertical-align should not be used with block
|
7389
|
-
reportProperty("vertical-align", display);
|
7390
|
-
break;
|
7391
|
-
|
7392
|
-
case "inline-block":
|
7393
|
-
//float should not be used with inline-block
|
7394
|
-
reportProperty("float", display);
|
7395
|
-
break;
|
7396
|
-
|
7397
|
-
default:
|
7398
|
-
//margin, float should not be used with table
|
7399
|
-
if (display.indexOf("table-") === 0){
|
7400
|
-
reportProperty("margin", display);
|
7401
|
-
reportProperty("margin-left", display);
|
7402
|
-
reportProperty("margin-right", display);
|
7403
|
-
reportProperty("margin-top", display);
|
7404
|
-
reportProperty("margin-bottom", display);
|
7405
|
-
reportProperty("float", display);
|
7406
|
-
}
|
7407
|
-
|
7408
|
-
//otherwise do nothing
|
7409
|
-
}
|
7410
|
-
}
|
7411
|
-
|
7412
|
-
}
|
7413
|
-
|
7414
|
-
parser.addListener("startrule", startRule);
|
7415
|
-
parser.addListener("startfontface", startRule);
|
7416
|
-
parser.addListener("startkeyframerule", startRule);
|
7417
|
-
parser.addListener("startpagemargin", startRule);
|
7418
|
-
parser.addListener("startpage", startRule);
|
7419
|
-
|
7420
|
-
parser.addListener("property", function(event){
|
7421
|
-
var name = event.property.text.toLowerCase();
|
7422
|
-
|
7423
|
-
if (propertiesToCheck[name]){
|
7424
|
-
properties[name] = { value: event.value.text, line: event.property.line, col: event.property.col };
|
7425
|
-
}
|
7426
|
-
});
|
7427
|
-
|
7428
|
-
parser.addListener("endrule", endRule);
|
7429
|
-
parser.addListener("endfontface", endRule);
|
7430
|
-
parser.addListener("endkeyframerule", endRule);
|
7431
|
-
parser.addListener("endpagemargin", endRule);
|
7432
|
-
parser.addListener("endpage", endRule);
|
7433
|
-
|
7434
|
-
}
|
7435
|
-
|
7436
|
-
});
|
7437
|
-
/*
|
7438
|
-
* Rule: Disallow duplicate background-images (using url).
|
7439
|
-
*/
|
7440
|
-
/*global CSSLint*/
|
7441
|
-
CSSLint.addRule({
|
7442
|
-
|
7443
|
-
//rule information
|
7444
|
-
id: "duplicate-background-images",
|
7445
|
-
name: "Disallow duplicate background images",
|
7446
|
-
desc: "Every background-image should be unique. Use a common class for e.g. sprites.",
|
7447
|
-
browsers: "All",
|
7448
|
-
|
7449
|
-
//initialization
|
7450
|
-
init: function(parser, reporter){
|
7451
|
-
var rule = this,
|
7452
|
-
stack = {};
|
7453
|
-
|
7454
|
-
parser.addListener("property", function(event){
|
7455
|
-
var name = event.property.text,
|
7456
|
-
value = event.value,
|
7457
|
-
i, len;
|
7458
|
-
|
7459
|
-
if (name.match(/background/i)) {
|
7460
|
-
for (i=0, len=value.parts.length; i < len; i++) {
|
7461
|
-
if (value.parts[i].type == 'uri') {
|
7462
|
-
if (typeof stack[value.parts[i].uri] === 'undefined') {
|
7463
|
-
stack[value.parts[i].uri] = event;
|
7464
|
-
}
|
7465
|
-
else {
|
7466
|
-
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);
|
7467
|
-
}
|
7468
|
-
}
|
7469
|
-
}
|
7470
|
-
}
|
7471
|
-
});
|
7472
|
-
}
|
7473
|
-
});
|
7474
|
-
/*
|
7475
|
-
* Rule: Duplicate properties must appear one after the other. If an already-defined
|
7476
|
-
* property appears somewhere else in the rule, then it's likely an error.
|
7477
|
-
*/
|
7478
|
-
/*global CSSLint*/
|
7479
|
-
CSSLint.addRule({
|
7480
|
-
|
7481
|
-
//rule information
|
7482
|
-
id: "duplicate-properties",
|
7483
|
-
name: "Disallow duplicate properties",
|
7484
|
-
desc: "Duplicate properties must appear one after the other.",
|
7485
|
-
browsers: "All",
|
7486
|
-
|
7487
|
-
//initialization
|
7488
|
-
init: function(parser, reporter){
|
7489
|
-
var rule = this,
|
7490
|
-
properties,
|
7491
|
-
lastProperty;
|
7492
|
-
|
7493
|
-
function startRule(event){
|
7494
|
-
properties = {};
|
7495
|
-
}
|
7496
|
-
|
7497
|
-
parser.addListener("startrule", startRule);
|
7498
|
-
parser.addListener("startfontface", startRule);
|
7499
|
-
parser.addListener("startpage", startRule);
|
7500
|
-
parser.addListener("startpagemargin", startRule);
|
7501
|
-
parser.addListener("startkeyframerule", startRule);
|
7502
|
-
|
7503
|
-
parser.addListener("property", function(event){
|
7504
|
-
var property = event.property,
|
7505
|
-
name = property.text.toLowerCase();
|
7506
|
-
|
7507
|
-
if (properties[name] && (lastProperty != name || properties[name] == event.value.text)){
|
7508
|
-
reporter.report("Duplicate property '" + event.property + "' found.", event.line, event.col, rule);
|
7509
|
-
}
|
7510
|
-
|
7511
|
-
properties[name] = event.value.text;
|
7512
|
-
lastProperty = name;
|
7513
|
-
|
7514
|
-
});
|
7515
|
-
|
7516
|
-
|
7517
|
-
}
|
7518
|
-
|
7519
|
-
});
|
7520
|
-
/*
|
7521
|
-
* Rule: Style rules without any properties defined should be removed.
|
7522
|
-
*/
|
7523
|
-
/*global CSSLint*/
|
7524
|
-
CSSLint.addRule({
|
7525
|
-
|
7526
|
-
//rule information
|
7527
|
-
id: "empty-rules",
|
7528
|
-
name: "Disallow empty rules",
|
7529
|
-
desc: "Rules without any properties specified should be removed.",
|
7530
|
-
browsers: "All",
|
7531
|
-
|
7532
|
-
//initialization
|
7533
|
-
init: function(parser, reporter){
|
7534
|
-
var rule = this,
|
7535
|
-
count = 0;
|
7536
|
-
|
7537
|
-
parser.addListener("startrule", function(){
|
7538
|
-
count=0;
|
7539
|
-
});
|
7540
|
-
|
7541
|
-
parser.addListener("property", function(){
|
7542
|
-
count++;
|
7543
|
-
});
|
7544
|
-
|
7545
|
-
parser.addListener("endrule", function(event){
|
7546
|
-
var selectors = event.selectors;
|
7547
|
-
if (count === 0){
|
7548
|
-
reporter.report("Rule is empty.", selectors[0].line, selectors[0].col, rule);
|
7549
|
-
}
|
7550
|
-
});
|
7551
|
-
}
|
7552
|
-
|
7553
|
-
});
|
7554
|
-
/*
|
7555
|
-
* Rule: There should be no syntax errors. (Duh.)
|
7556
|
-
*/
|
7557
|
-
/*global CSSLint*/
|
7558
|
-
CSSLint.addRule({
|
7559
|
-
|
7560
|
-
//rule information
|
7561
|
-
id: "errors",
|
7562
|
-
name: "Parsing Errors",
|
7563
|
-
desc: "This rule looks for recoverable syntax errors.",
|
7564
|
-
browsers: "All",
|
7565
|
-
|
7566
|
-
//initialization
|
7567
|
-
init: function(parser, reporter){
|
7568
|
-
var rule = this;
|
7569
|
-
|
7570
|
-
parser.addListener("error", function(event){
|
7571
|
-
reporter.error(event.message, event.line, event.col, rule);
|
7572
|
-
});
|
7573
|
-
|
7574
|
-
}
|
7575
|
-
|
7576
|
-
});
|
7577
|
-
|
7578
|
-
/*global CSSLint*/
|
7579
|
-
CSSLint.addRule({
|
7580
|
-
|
7581
|
-
//rule information
|
7582
|
-
id: "fallback-colors",
|
7583
|
-
name: "Require fallback colors",
|
7584
|
-
desc: "For older browsers that don't support RGBA, HSL, or HSLA, provide a fallback color.",
|
7585
|
-
browsers: "IE6,IE7,IE8",
|
7586
|
-
|
7587
|
-
//initialization
|
7588
|
-
init: function(parser, reporter){
|
7589
|
-
var rule = this,
|
7590
|
-
lastProperty,
|
7591
|
-
propertiesToCheck = {
|
7592
|
-
color: 1,
|
7593
|
-
background: 1,
|
7594
|
-
"border-color": 1,
|
7595
|
-
"border-top-color": 1,
|
7596
|
-
"border-right-color": 1,
|
7597
|
-
"border-bottom-color": 1,
|
7598
|
-
"border-left-color": 1,
|
7599
|
-
border: 1,
|
7600
|
-
"border-top": 1,
|
7601
|
-
"border-right": 1,
|
7602
|
-
"border-bottom": 1,
|
7603
|
-
"border-left": 1,
|
7604
|
-
"background-color": 1
|
7605
|
-
},
|
7606
|
-
properties;
|
7607
|
-
|
7608
|
-
function startRule(event){
|
7609
|
-
properties = {};
|
7610
|
-
lastProperty = null;
|
7611
|
-
}
|
7612
|
-
|
7613
|
-
parser.addListener("startrule", startRule);
|
7614
|
-
parser.addListener("startfontface", startRule);
|
7615
|
-
parser.addListener("startpage", startRule);
|
7616
|
-
parser.addListener("startpagemargin", startRule);
|
7617
|
-
parser.addListener("startkeyframerule", startRule);
|
7618
|
-
|
7619
|
-
parser.addListener("property", function(event){
|
7620
|
-
var property = event.property,
|
7621
|
-
name = property.text.toLowerCase(),
|
7622
|
-
parts = event.value.parts,
|
7623
|
-
i = 0,
|
7624
|
-
colorType = "",
|
7625
|
-
len = parts.length;
|
7626
|
-
|
7627
|
-
if(propertiesToCheck[name]){
|
7628
|
-
while(i < len){
|
7629
|
-
if (parts[i].type == "color"){
|
7630
|
-
if ("alpha" in parts[i] || "hue" in parts[i]){
|
7631
|
-
|
7632
|
-
if (/([^\)]+)\(/.test(parts[i])){
|
7633
|
-
colorType = RegExp.$1.toUpperCase();
|
7634
|
-
}
|
7635
|
-
|
7636
|
-
if (!lastProperty || (lastProperty.property.text.toLowerCase() != name || lastProperty.colorType != "compat")){
|
7637
|
-
reporter.report("Fallback " + name + " (hex or RGB) should precede " + colorType + " " + name + ".", event.line, event.col, rule);
|
7638
|
-
}
|
7639
|
-
} else {
|
7640
|
-
event.colorType = "compat";
|
7641
|
-
}
|
7642
|
-
}
|
7643
|
-
|
7644
|
-
i++;
|
7645
|
-
}
|
7646
|
-
}
|
7647
|
-
|
7648
|
-
lastProperty = event;
|
7649
|
-
});
|
7650
|
-
|
7651
|
-
}
|
7652
|
-
|
7653
|
-
});
|
7654
|
-
/*
|
7655
|
-
* Rule: You shouldn't use more than 10 floats. If you do, there's probably
|
7656
|
-
* room for some abstraction.
|
7657
|
-
*/
|
7658
|
-
/*global CSSLint*/
|
7659
|
-
CSSLint.addRule({
|
7660
|
-
|
7661
|
-
//rule information
|
7662
|
-
id: "floats",
|
7663
|
-
name: "Disallow too many floats",
|
7664
|
-
desc: "This rule tests if the float property is used too many times",
|
7665
|
-
browsers: "All",
|
7666
|
-
|
7667
|
-
//initialization
|
7668
|
-
init: function(parser, reporter){
|
7669
|
-
var rule = this;
|
7670
|
-
var count = 0;
|
7671
|
-
|
7672
|
-
//count how many times "float" is used
|
7673
|
-
parser.addListener("property", function(event){
|
7674
|
-
if (event.property.text.toLowerCase() == "float" &&
|
7675
|
-
event.value.text.toLowerCase() != "none"){
|
7676
|
-
count++;
|
7677
|
-
}
|
7678
|
-
});
|
7679
|
-
|
7680
|
-
//report the results
|
7681
|
-
parser.addListener("endstylesheet", function(){
|
7682
|
-
reporter.stat("floats", count);
|
7683
|
-
if (count >= 10){
|
7684
|
-
reporter.rollupWarn("Too many floats (" + count + "), you're probably using them for layout. Consider using a grid system instead.", rule);
|
7685
|
-
}
|
7686
|
-
});
|
7687
|
-
}
|
7688
|
-
|
7689
|
-
});
|
7690
|
-
/*
|
7691
|
-
* Rule: Avoid too many @font-face declarations in the same stylesheet.
|
7692
|
-
*/
|
7693
|
-
/*global CSSLint*/
|
7694
|
-
CSSLint.addRule({
|
7695
|
-
|
7696
|
-
//rule information
|
7697
|
-
id: "font-faces",
|
7698
|
-
name: "Don't use too many web fonts",
|
7699
|
-
desc: "Too many different web fonts in the same stylesheet.",
|
7700
|
-
browsers: "All",
|
7701
|
-
|
7702
|
-
//initialization
|
7703
|
-
init: function(parser, reporter){
|
7704
|
-
var rule = this,
|
7705
|
-
count = 0;
|
7706
|
-
|
7707
|
-
|
7708
|
-
parser.addListener("startfontface", function(){
|
7709
|
-
count++;
|
7710
|
-
});
|
7711
|
-
|
7712
|
-
parser.addListener("endstylesheet", function(){
|
7713
|
-
if (count > 5){
|
7714
|
-
reporter.rollupWarn("Too many @font-face declarations (" + count + ").", rule);
|
7715
|
-
}
|
7716
|
-
});
|
7717
|
-
}
|
7718
|
-
|
7719
|
-
});
|
7720
|
-
/*
|
7721
|
-
* Rule: You shouldn't need more than 9 font-size declarations.
|
7722
|
-
*/
|
7723
|
-
|
7724
|
-
/*global CSSLint*/
|
7725
|
-
CSSLint.addRule({
|
7726
|
-
|
7727
|
-
//rule information
|
7728
|
-
id: "font-sizes",
|
7729
|
-
name: "Disallow too many font sizes",
|
7730
|
-
desc: "Checks the number of font-size declarations.",
|
7731
|
-
browsers: "All",
|
7732
|
-
|
7733
|
-
//initialization
|
7734
|
-
init: function(parser, reporter){
|
7735
|
-
var rule = this,
|
7736
|
-
count = 0;
|
7737
|
-
|
7738
|
-
//check for use of "font-size"
|
7739
|
-
parser.addListener("property", function(event){
|
7740
|
-
if (event.property == "font-size"){
|
7741
|
-
count++;
|
7742
|
-
}
|
7743
|
-
});
|
7744
|
-
|
7745
|
-
//report the results
|
7746
|
-
parser.addListener("endstylesheet", function(){
|
7747
|
-
reporter.stat("font-sizes", count);
|
7748
|
-
if (count >= 10){
|
7749
|
-
reporter.rollupWarn("Too many font-size declarations (" + count + "), abstraction needed.", rule);
|
7750
|
-
}
|
7751
|
-
});
|
7752
|
-
}
|
7753
|
-
|
7754
|
-
});
|
7755
|
-
/*
|
7756
|
-
* Rule: When using a vendor-prefixed gradient, make sure to use them all.
|
7757
|
-
*/
|
7758
|
-
/*global CSSLint*/
|
7759
|
-
CSSLint.addRule({
|
7760
|
-
|
7761
|
-
//rule information
|
7762
|
-
id: "gradients",
|
7763
|
-
name: "Require all gradient definitions",
|
7764
|
-
desc: "When using a vendor-prefixed gradient, make sure to use them all.",
|
7765
|
-
browsers: "All",
|
7766
|
-
|
7767
|
-
//initialization
|
7768
|
-
init: function(parser, reporter){
|
7769
|
-
var rule = this,
|
7770
|
-
gradients;
|
7771
|
-
|
7772
|
-
parser.addListener("startrule", function(){
|
7773
|
-
gradients = {
|
7774
|
-
moz: 0,
|
7775
|
-
webkit: 0,
|
7776
|
-
oldWebkit: 0,
|
7777
|
-
o: 0
|
7778
|
-
};
|
7779
|
-
});
|
7780
|
-
|
7781
|
-
parser.addListener("property", function(event){
|
7782
|
-
|
7783
|
-
if (/\-(moz|o|webkit)(?:\-(?:linear|radial))\-gradient/i.test(event.value)){
|
7784
|
-
gradients[RegExp.$1] = 1;
|
7785
|
-
} else if (/\-webkit\-gradient/i.test(event.value)){
|
7786
|
-
gradients.oldWebkit = 1;
|
7787
|
-
}
|
7788
|
-
|
7789
|
-
});
|
7790
|
-
|
7791
|
-
parser.addListener("endrule", function(event){
|
7792
|
-
var missing = [];
|
7793
|
-
|
7794
|
-
if (!gradients.moz){
|
7795
|
-
missing.push("Firefox 3.6+");
|
7796
|
-
}
|
7797
|
-
|
7798
|
-
if (!gradients.webkit){
|
7799
|
-
missing.push("Webkit (Safari 5+, Chrome)");
|
7800
|
-
}
|
7801
|
-
|
7802
|
-
if (!gradients.oldWebkit){
|
7803
|
-
missing.push("Old Webkit (Safari 4+, Chrome)");
|
7804
|
-
}
|
7805
|
-
|
7806
|
-
if (!gradients.o){
|
7807
|
-
missing.push("Opera 11.1+");
|
7808
|
-
}
|
7809
|
-
|
7810
|
-
if (missing.length && missing.length < 4){
|
7811
|
-
reporter.report("Missing vendor-prefixed CSS gradients for " + missing.join(", ") + ".", event.selectors[0].line, event.selectors[0].col, rule);
|
7812
|
-
}
|
7813
|
-
|
7814
|
-
});
|
7815
|
-
|
7816
|
-
}
|
7817
|
-
|
7818
|
-
});
|
7819
|
-
|
7820
|
-
/*
|
7821
|
-
* Rule: Don't use IDs for selectors.
|
7822
|
-
*/
|
7823
|
-
/*global CSSLint*/
|
7824
|
-
CSSLint.addRule({
|
7825
|
-
|
7826
|
-
//rule information
|
7827
|
-
id: "ids",
|
7828
|
-
name: "Disallow IDs in selectors",
|
7829
|
-
desc: "Selectors should not contain IDs.",
|
7830
|
-
browsers: "All",
|
7831
|
-
|
7832
|
-
//initialization
|
7833
|
-
init: function(parser, reporter){
|
7834
|
-
var rule = this;
|
7835
|
-
parser.addListener("startrule", function(event){
|
7836
|
-
var selectors = event.selectors,
|
7837
|
-
selector,
|
7838
|
-
part,
|
7839
|
-
modifier,
|
7840
|
-
idCount,
|
7841
|
-
i, j, k;
|
7842
|
-
|
7843
|
-
for (i=0; i < selectors.length; i++){
|
7844
|
-
selector = selectors[i];
|
7845
|
-
idCount = 0;
|
7846
|
-
|
7847
|
-
for (j=0; j < selector.parts.length; j++){
|
7848
|
-
part = selector.parts[j];
|
7849
|
-
if (part.type == parser.SELECTOR_PART_TYPE){
|
7850
|
-
for (k=0; k < part.modifiers.length; k++){
|
7851
|
-
modifier = part.modifiers[k];
|
7852
|
-
if (modifier.type == "id"){
|
7853
|
-
idCount++;
|
7854
|
-
}
|
7855
|
-
}
|
7856
|
-
}
|
7857
|
-
}
|
7858
|
-
|
7859
|
-
if (idCount == 1){
|
7860
|
-
reporter.report("Don't use IDs in selectors.", selector.line, selector.col, rule);
|
7861
|
-
} else if (idCount > 1){
|
7862
|
-
reporter.report(idCount + " IDs in the selector, really?", selector.line, selector.col, rule);
|
7863
|
-
}
|
7864
|
-
}
|
7865
|
-
|
7866
|
-
});
|
7867
|
-
}
|
7868
|
-
|
7869
|
-
});
|
7870
|
-
/*
|
7871
|
-
* Rule: Don't use @import, use <link> instead.
|
7872
|
-
*/
|
7873
|
-
/*global CSSLint*/
|
7874
|
-
CSSLint.addRule({
|
7875
|
-
|
7876
|
-
//rule information
|
7877
|
-
id: "import",
|
7878
|
-
name: "Disallow @import",
|
7879
|
-
desc: "Don't use @import, use <link> instead.",
|
7880
|
-
browsers: "All",
|
7881
|
-
|
7882
|
-
//initialization
|
7883
|
-
init: function(parser, reporter){
|
7884
|
-
var rule = this;
|
7885
|
-
|
7886
|
-
parser.addListener("import", function(event){
|
7887
|
-
reporter.report("@import prevents parallel downloads, use <link> instead.", event.line, event.col, rule);
|
7888
|
-
});
|
7889
|
-
|
7890
|
-
}
|
7891
|
-
|
7892
|
-
});
|
7893
|
-
/*
|
7894
|
-
* Rule: Make sure !important is not overused, this could lead to specificity
|
7895
|
-
* war. Display a warning on !important declarations, an error if it's
|
7896
|
-
* used more at least 10 times.
|
7897
|
-
*/
|
7898
|
-
/*global CSSLint*/
|
7899
|
-
CSSLint.addRule({
|
7900
|
-
|
7901
|
-
//rule information
|
7902
|
-
id: "important",
|
7903
|
-
name: "Disallow !important",
|
7904
|
-
desc: "Be careful when using !important declaration",
|
7905
|
-
browsers: "All",
|
7906
|
-
|
7907
|
-
//initialization
|
7908
|
-
init: function(parser, reporter){
|
7909
|
-
var rule = this,
|
7910
|
-
count = 0;
|
7911
|
-
|
7912
|
-
//warn that important is used and increment the declaration counter
|
7913
|
-
parser.addListener("property", function(event){
|
7914
|
-
if (event.important === true){
|
7915
|
-
count++;
|
7916
|
-
reporter.report("Use of !important", event.line, event.col, rule);
|
7917
|
-
}
|
7918
|
-
});
|
7919
|
-
|
7920
|
-
//if there are more than 10, show an error
|
7921
|
-
parser.addListener("endstylesheet", function(){
|
7922
|
-
reporter.stat("important", count);
|
7923
|
-
if (count >= 10){
|
7924
|
-
reporter.rollupWarn("Too many !important declarations (" + count + "), try to use less than 10 to avoid specificity issues.", rule);
|
7925
|
-
}
|
7926
|
-
});
|
7927
|
-
}
|
7928
|
-
|
7929
|
-
});
|
7930
|
-
/*
|
7931
|
-
* Rule: Properties should be known (listed in CSS3 specification) or
|
7932
|
-
* be a vendor-prefixed property.
|
7933
|
-
*/
|
7934
|
-
/*global CSSLint*/
|
7935
|
-
CSSLint.addRule({
|
7936
|
-
|
7937
|
-
//rule information
|
7938
|
-
id: "known-properties",
|
7939
|
-
name: "Require use of known properties",
|
7940
|
-
desc: "Properties should be known (listed in CSS3 specification) or be a vendor-prefixed property.",
|
7941
|
-
browsers: "All",
|
7942
|
-
|
7943
|
-
//initialization
|
7944
|
-
init: function(parser, reporter){
|
7945
|
-
var rule = this;
|
7946
|
-
|
7947
|
-
parser.addListener("property", function(event){
|
7948
|
-
var name = event.property.text.toLowerCase();
|
7949
|
-
|
7950
|
-
// the check is handled entirely by the parser-lib (https://github.com/nzakas/parser-lib)
|
7951
|
-
if (event.invalid) {
|
7952
|
-
reporter.report(event.invalid.message, event.line, event.col, rule);
|
7953
|
-
}
|
7954
|
-
|
7955
|
-
});
|
7956
|
-
}
|
7957
|
-
|
7958
|
-
});
|
7959
|
-
/*
|
7960
|
-
* Rule: outline: none or outline: 0 should only be used in a :focus rule
|
7961
|
-
* and only if there are other properties in the same rule.
|
7962
|
-
*/
|
7963
|
-
/*global CSSLint*/
|
7964
|
-
CSSLint.addRule({
|
7965
|
-
|
7966
|
-
//rule information
|
7967
|
-
id: "outline-none",
|
7968
|
-
name: "Disallow outline: none",
|
7969
|
-
desc: "Use of outline: none or outline: 0 should be limited to :focus rules.",
|
7970
|
-
browsers: "All",
|
7971
|
-
tags: ["Accessibility"],
|
7972
|
-
|
7973
|
-
//initialization
|
7974
|
-
init: function(parser, reporter){
|
7975
|
-
var rule = this,
|
7976
|
-
lastRule;
|
7977
|
-
|
7978
|
-
function startRule(event){
|
7979
|
-
if (event.selectors){
|
7980
|
-
lastRule = {
|
7981
|
-
line: event.line,
|
7982
|
-
col: event.col,
|
7983
|
-
selectors: event.selectors,
|
7984
|
-
propCount: 0,
|
7985
|
-
outline: false
|
7986
|
-
};
|
7987
|
-
} else {
|
7988
|
-
lastRule = null;
|
7989
|
-
}
|
7990
|
-
}
|
7991
|
-
|
7992
|
-
function endRule(event){
|
7993
|
-
if (lastRule){
|
7994
|
-
if (lastRule.outline){
|
7995
|
-
if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") == -1){
|
7996
|
-
reporter.report("Outlines should only be modified using :focus.", lastRule.line, lastRule.col, rule);
|
7997
|
-
} else if (lastRule.propCount == 1) {
|
7998
|
-
reporter.report("Outlines shouldn't be hidden unless other visual changes are made.", lastRule.line, lastRule.col, rule);
|
7999
|
-
}
|
8000
|
-
}
|
8001
|
-
}
|
8002
|
-
}
|
8003
|
-
|
8004
|
-
parser.addListener("startrule", startRule);
|
8005
|
-
parser.addListener("startfontface", startRule);
|
8006
|
-
parser.addListener("startpage", startRule);
|
8007
|
-
parser.addListener("startpagemargin", startRule);
|
8008
|
-
parser.addListener("startkeyframerule", startRule);
|
8009
|
-
|
8010
|
-
parser.addListener("property", function(event){
|
8011
|
-
var name = event.property.text.toLowerCase(),
|
8012
|
-
value = event.value;
|
8013
|
-
|
8014
|
-
if (lastRule){
|
8015
|
-
lastRule.propCount++;
|
8016
|
-
if (name == "outline" && (value == "none" || value == "0")){
|
8017
|
-
lastRule.outline = true;
|
8018
|
-
}
|
8019
|
-
}
|
8020
|
-
|
8021
|
-
});
|
8022
|
-
|
8023
|
-
parser.addListener("endrule", endRule);
|
8024
|
-
parser.addListener("endfontface", endRule);
|
8025
|
-
parser.addListener("endpage", endRule);
|
8026
|
-
parser.addListener("endpagemargin", endRule);
|
8027
|
-
parser.addListener("endkeyframerule", endRule);
|
8028
|
-
|
8029
|
-
}
|
8030
|
-
|
8031
|
-
});
|
8032
|
-
/*
|
8033
|
-
* Rule: Don't use classes or IDs with elements (a.foo or a#foo).
|
8034
|
-
*/
|
8035
|
-
/*global CSSLint*/
|
8036
|
-
CSSLint.addRule({
|
8037
|
-
|
8038
|
-
//rule information
|
8039
|
-
id: "overqualified-elements",
|
8040
|
-
name: "Disallow overqualified elements",
|
8041
|
-
desc: "Don't use classes or IDs with elements (a.foo or a#foo).",
|
8042
|
-
browsers: "All",
|
8043
|
-
|
8044
|
-
//initialization
|
8045
|
-
init: function(parser, reporter){
|
8046
|
-
var rule = this,
|
8047
|
-
classes = {};
|
8048
|
-
|
8049
|
-
parser.addListener("startrule", function(event){
|
8050
|
-
var selectors = event.selectors,
|
8051
|
-
selector,
|
8052
|
-
part,
|
8053
|
-
modifier,
|
8054
|
-
i, j, k;
|
8055
|
-
|
8056
|
-
for (i=0; i < selectors.length; i++){
|
8057
|
-
selector = selectors[i];
|
8058
|
-
|
8059
|
-
for (j=0; j < selector.parts.length; j++){
|
8060
|
-
part = selector.parts[j];
|
8061
|
-
if (part.type == parser.SELECTOR_PART_TYPE){
|
8062
|
-
for (k=0; k < part.modifiers.length; k++){
|
8063
|
-
modifier = part.modifiers[k];
|
8064
|
-
if (part.elementName && modifier.type == "id"){
|
8065
|
-
reporter.report("Element (" + part + ") is overqualified, just use " + modifier + " without element name.", part.line, part.col, rule);
|
8066
|
-
} else if (modifier.type == "class"){
|
8067
|
-
|
8068
|
-
if (!classes[modifier]){
|
8069
|
-
classes[modifier] = [];
|
8070
|
-
}
|
8071
|
-
classes[modifier].push({ modifier: modifier, part: part });
|
8072
|
-
}
|
8073
|
-
}
|
8074
|
-
}
|
8075
|
-
}
|
8076
|
-
}
|
8077
|
-
});
|
8078
|
-
|
8079
|
-
parser.addListener("endstylesheet", function(){
|
8080
|
-
|
8081
|
-
var prop;
|
8082
|
-
for (prop in classes){
|
8083
|
-
if (classes.hasOwnProperty(prop)){
|
8084
|
-
|
8085
|
-
//one use means that this is overqualified
|
8086
|
-
if (classes[prop].length == 1 && classes[prop][0].part.elementName){
|
8087
|
-
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);
|
8088
|
-
}
|
8089
|
-
}
|
8090
|
-
}
|
8091
|
-
});
|
8092
|
-
}
|
8093
|
-
|
8094
|
-
});
|
8095
|
-
/*
|
8096
|
-
* Rule: Headings (h1-h6) should not be qualified (namespaced).
|
8097
|
-
*/
|
8098
|
-
/*global CSSLint*/
|
8099
|
-
CSSLint.addRule({
|
8100
|
-
|
8101
|
-
//rule information
|
8102
|
-
id: "qualified-headings",
|
8103
|
-
name: "Disallow qualified headings",
|
8104
|
-
desc: "Headings should not be qualified (namespaced).",
|
8105
|
-
browsers: "All",
|
8106
|
-
|
8107
|
-
//initialization
|
8108
|
-
init: function(parser, reporter){
|
8109
|
-
var rule = this;
|
8110
|
-
|
8111
|
-
parser.addListener("startrule", function(event){
|
8112
|
-
var selectors = event.selectors,
|
8113
|
-
selector,
|
8114
|
-
part,
|
8115
|
-
i, j;
|
8116
|
-
|
8117
|
-
for (i=0; i < selectors.length; i++){
|
8118
|
-
selector = selectors[i];
|
8119
|
-
|
8120
|
-
for (j=0; j < selector.parts.length; j++){
|
8121
|
-
part = selector.parts[j];
|
8122
|
-
if (part.type == parser.SELECTOR_PART_TYPE){
|
8123
|
-
if (part.elementName && /h[1-6]/.test(part.elementName.toString()) && j > 0){
|
8124
|
-
reporter.report("Heading (" + part.elementName + ") should not be qualified.", part.line, part.col, rule);
|
8125
|
-
}
|
8126
|
-
}
|
8127
|
-
}
|
8128
|
-
}
|
8129
|
-
});
|
8130
|
-
}
|
8131
|
-
|
8132
|
-
});
|
8133
|
-
/*
|
8134
|
-
* Rule: Selectors that look like regular expressions are slow and should be avoided.
|
8135
|
-
*/
|
8136
|
-
/*global CSSLint*/
|
8137
|
-
CSSLint.addRule({
|
8138
|
-
|
8139
|
-
//rule information
|
8140
|
-
id: "regex-selectors",
|
8141
|
-
name: "Disallow selectors that look like regexs",
|
8142
|
-
desc: "Selectors that look like regular expressions are slow and should be avoided.",
|
8143
|
-
browsers: "All",
|
8144
|
-
|
8145
|
-
//initialization
|
8146
|
-
init: function(parser, reporter){
|
8147
|
-
var rule = this;
|
8148
|
-
|
8149
|
-
parser.addListener("startrule", function(event){
|
8150
|
-
var selectors = event.selectors,
|
8151
|
-
selector,
|
8152
|
-
part,
|
8153
|
-
modifier,
|
8154
|
-
i, j, k;
|
8155
|
-
|
8156
|
-
for (i=0; i < selectors.length; i++){
|
8157
|
-
selector = selectors[i];
|
8158
|
-
for (j=0; j < selector.parts.length; j++){
|
8159
|
-
part = selector.parts[j];
|
8160
|
-
if (part.type == parser.SELECTOR_PART_TYPE){
|
8161
|
-
for (k=0; k < part.modifiers.length; k++){
|
8162
|
-
modifier = part.modifiers[k];
|
8163
|
-
if (modifier.type == "attribute"){
|
8164
|
-
if (/([\~\|\^\$\*]=)/.test(modifier)){
|
8165
|
-
reporter.report("Attribute selectors with " + RegExp.$1 + " are slow!", modifier.line, modifier.col, rule);
|
8166
|
-
}
|
8167
|
-
}
|
8168
|
-
|
8169
|
-
}
|
8170
|
-
}
|
8171
|
-
}
|
8172
|
-
}
|
8173
|
-
});
|
8174
|
-
}
|
8175
|
-
|
8176
|
-
});
|
8177
|
-
/*
|
8178
|
-
* Rule: Total number of rules should not exceed x.
|
8179
|
-
*/
|
8180
|
-
/*global CSSLint*/
|
8181
|
-
CSSLint.addRule({
|
8182
|
-
|
8183
|
-
//rule information
|
8184
|
-
id: "rules-count",
|
8185
|
-
name: "Rules Count",
|
8186
|
-
desc: "Track how many rules there are.",
|
8187
|
-
browsers: "All",
|
8188
|
-
|
8189
|
-
//initialization
|
8190
|
-
init: function(parser, reporter){
|
8191
|
-
var rule = this,
|
8192
|
-
count = 0;
|
8193
|
-
|
8194
|
-
//count each rule
|
8195
|
-
parser.addListener("startrule", function(){
|
8196
|
-
count++;
|
8197
|
-
});
|
8198
|
-
|
8199
|
-
parser.addListener("endstylesheet", function(){
|
8200
|
-
reporter.stat("rule-count", count);
|
8201
|
-
});
|
8202
|
-
}
|
8203
|
-
|
8204
|
-
});
|
8205
|
-
/*
|
8206
|
-
* Rule: Warn people with approaching the IE 4095 limit
|
8207
|
-
*/
|
8208
|
-
/*global CSSLint*/
|
8209
|
-
CSSLint.addRule({
|
8210
|
-
|
8211
|
-
//rule information
|
8212
|
-
id: "selector-max-approaching",
|
8213
|
-
name: "Warn when approaching the 4095 selector limit for IE",
|
8214
|
-
desc: "Will warn when selector count is >= 3800 selectors.",
|
8215
|
-
browsers: "IE",
|
8216
|
-
|
8217
|
-
//initialization
|
8218
|
-
init: function(parser, reporter) {
|
8219
|
-
var rule = this, count = 0;
|
8220
|
-
|
8221
|
-
parser.addListener('startrule', function(event) {
|
8222
|
-
count += event.selectors.length;
|
8223
|
-
});
|
8224
|
-
|
8225
|
-
parser.addListener("endstylesheet", function() {
|
8226
|
-
if (count >= 3800) {
|
8227
|
-
reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.",0,0,rule);
|
8228
|
-
}
|
8229
|
-
});
|
8230
|
-
}
|
8231
|
-
|
8232
|
-
});
|
8233
|
-
|
8234
|
-
/*
|
8235
|
-
* Rule: Warn people past the IE 4095 limit
|
8236
|
-
*/
|
8237
|
-
/*global CSSLint*/
|
8238
|
-
CSSLint.addRule({
|
8239
|
-
|
8240
|
-
//rule information
|
8241
|
-
id: "selector-max",
|
8242
|
-
name: "Error when past the 4095 selector limit for IE",
|
8243
|
-
desc: "Will error when selector count is > 4095.",
|
8244
|
-
browsers: "IE",
|
8245
|
-
|
8246
|
-
//initialization
|
8247
|
-
init: function(parser, reporter){
|
8248
|
-
var rule = this, count = 0;
|
8249
|
-
|
8250
|
-
parser.addListener('startrule',function(event) {
|
8251
|
-
count += event.selectors.length;
|
8252
|
-
});
|
8253
|
-
|
8254
|
-
parser.addListener("endstylesheet", function() {
|
8255
|
-
if (count > 4095) {
|
8256
|
-
reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.",0,0,rule);
|
8257
|
-
}
|
8258
|
-
});
|
8259
|
-
}
|
8260
|
-
|
8261
|
-
});
|
8262
|
-
/*
|
8263
|
-
* Rule: Use shorthand properties where possible.
|
8264
|
-
*
|
8265
|
-
*/
|
8266
|
-
/*global CSSLint*/
|
8267
|
-
CSSLint.addRule({
|
8268
|
-
|
8269
|
-
//rule information
|
8270
|
-
id: "shorthand",
|
8271
|
-
name: "Require shorthand properties",
|
8272
|
-
desc: "Use shorthand properties where possible.",
|
8273
|
-
browsers: "All",
|
8274
|
-
|
8275
|
-
//initialization
|
8276
|
-
init: function(parser, reporter){
|
8277
|
-
var rule = this,
|
8278
|
-
prop, i, len,
|
8279
|
-
propertiesToCheck = {},
|
8280
|
-
properties,
|
8281
|
-
mapping = {
|
8282
|
-
"margin": [
|
8283
|
-
"margin-top",
|
8284
|
-
"margin-bottom",
|
8285
|
-
"margin-left",
|
8286
|
-
"margin-right"
|
8287
|
-
],
|
8288
|
-
"padding": [
|
8289
|
-
"padding-top",
|
8290
|
-
"padding-bottom",
|
8291
|
-
"padding-left",
|
8292
|
-
"padding-right"
|
8293
|
-
]
|
8294
|
-
};
|
8295
|
-
|
8296
|
-
//initialize propertiesToCheck
|
8297
|
-
for (prop in mapping){
|
8298
|
-
if (mapping.hasOwnProperty(prop)){
|
8299
|
-
for (i=0, len=mapping[prop].length; i < len; i++){
|
8300
|
-
propertiesToCheck[mapping[prop][i]] = prop;
|
8301
|
-
}
|
8302
|
-
}
|
8303
|
-
}
|
8304
|
-
|
8305
|
-
function startRule(event){
|
8306
|
-
properties = {};
|
8307
|
-
}
|
8308
|
-
|
8309
|
-
//event handler for end of rules
|
8310
|
-
function endRule(event){
|
8311
|
-
|
8312
|
-
var prop, i, len, total;
|
8313
|
-
|
8314
|
-
//check which properties this rule has
|
8315
|
-
for (prop in mapping){
|
8316
|
-
if (mapping.hasOwnProperty(prop)){
|
8317
|
-
total=0;
|
8318
|
-
|
8319
|
-
for (i=0, len=mapping[prop].length; i < len; i++){
|
8320
|
-
total += properties[mapping[prop][i]] ? 1 : 0;
|
8321
|
-
}
|
8322
|
-
|
8323
|
-
if (total == mapping[prop].length){
|
8324
|
-
reporter.report("The properties " + mapping[prop].join(", ") + " can be replaced by " + prop + ".", event.line, event.col, rule);
|
8325
|
-
}
|
8326
|
-
}
|
8327
|
-
}
|
8328
|
-
}
|
8329
|
-
|
8330
|
-
parser.addListener("startrule", startRule);
|
8331
|
-
parser.addListener("startfontface", startRule);
|
8332
|
-
|
8333
|
-
//check for use of "font-size"
|
8334
|
-
parser.addListener("property", function(event){
|
8335
|
-
var name = event.property.toString().toLowerCase(),
|
8336
|
-
value = event.value.parts[0].value;
|
8337
|
-
|
8338
|
-
if (propertiesToCheck[name]){
|
8339
|
-
properties[name] = 1;
|
8340
|
-
}
|
8341
|
-
});
|
8342
|
-
|
8343
|
-
parser.addListener("endrule", endRule);
|
8344
|
-
parser.addListener("endfontface", endRule);
|
8345
|
-
|
8346
|
-
}
|
8347
|
-
|
8348
|
-
});
|
8349
|
-
/*
|
8350
|
-
* Rule: Don't use properties with a star prefix.
|
8351
|
-
*
|
8352
|
-
*/
|
8353
|
-
/*global CSSLint*/
|
8354
|
-
CSSLint.addRule({
|
8355
|
-
|
8356
|
-
//rule information
|
8357
|
-
id: "star-property-hack",
|
8358
|
-
name: "Disallow properties with a star prefix",
|
8359
|
-
desc: "Checks for the star property hack (targets IE6/7)",
|
8360
|
-
browsers: "All",
|
8361
|
-
|
8362
|
-
//initialization
|
8363
|
-
init: function(parser, reporter){
|
8364
|
-
var rule = this;
|
8365
|
-
|
8366
|
-
//check if property name starts with "*"
|
8367
|
-
parser.addListener("property", function(event){
|
8368
|
-
var property = event.property;
|
8369
|
-
|
8370
|
-
if (property.hack == "*") {
|
8371
|
-
reporter.report("Property with star prefix found.", event.property.line, event.property.col, rule);
|
8372
|
-
}
|
8373
|
-
});
|
8374
|
-
}
|
8375
|
-
});
|
8376
|
-
/*
|
8377
|
-
* Rule: Don't use text-indent for image replacement if you need to support rtl.
|
8378
|
-
*
|
8379
|
-
*/
|
8380
|
-
/*global CSSLint*/
|
8381
|
-
CSSLint.addRule({
|
8382
|
-
|
8383
|
-
//rule information
|
8384
|
-
id: "text-indent",
|
8385
|
-
name: "Disallow negative text-indent",
|
8386
|
-
desc: "Checks for text indent less than -99px",
|
8387
|
-
browsers: "All",
|
8388
|
-
|
8389
|
-
//initialization
|
8390
|
-
init: function(parser, reporter){
|
8391
|
-
var rule = this,
|
8392
|
-
textIndent,
|
8393
|
-
direction;
|
8394
|
-
|
8395
|
-
|
8396
|
-
function startRule(event){
|
8397
|
-
textIndent = false;
|
8398
|
-
direction = "inherit";
|
8399
|
-
}
|
8400
|
-
|
8401
|
-
//event handler for end of rules
|
8402
|
-
function endRule(event){
|
8403
|
-
if (textIndent && direction != "ltr"){
|
8404
|
-
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);
|
8405
|
-
}
|
8406
|
-
}
|
8407
|
-
|
8408
|
-
parser.addListener("startrule", startRule);
|
8409
|
-
parser.addListener("startfontface", startRule);
|
8410
|
-
|
8411
|
-
//check for use of "font-size"
|
8412
|
-
parser.addListener("property", function(event){
|
8413
|
-
var name = event.property.toString().toLowerCase(),
|
8414
|
-
value = event.value;
|
8415
|
-
|
8416
|
-
if (name == "text-indent" && value.parts[0].value < -99){
|
8417
|
-
textIndent = event.property;
|
8418
|
-
} else if (name == "direction" && value == "ltr"){
|
8419
|
-
direction = "ltr";
|
8420
|
-
}
|
8421
|
-
});
|
8422
|
-
|
8423
|
-
parser.addListener("endrule", endRule);
|
8424
|
-
parser.addListener("endfontface", endRule);
|
8425
|
-
|
8426
|
-
}
|
8427
|
-
|
8428
|
-
});
|
8429
|
-
/*
|
8430
|
-
* Rule: Don't use properties with a underscore prefix.
|
8431
|
-
*
|
8432
|
-
*/
|
8433
|
-
/*global CSSLint*/
|
8434
|
-
CSSLint.addRule({
|
8435
|
-
|
8436
|
-
//rule information
|
8437
|
-
id: "underscore-property-hack",
|
8438
|
-
name: "Disallow properties with an underscore prefix",
|
8439
|
-
desc: "Checks for the underscore property hack (targets IE6)",
|
8440
|
-
browsers: "All",
|
8441
|
-
|
8442
|
-
//initialization
|
8443
|
-
init: function(parser, reporter){
|
8444
|
-
var rule = this;
|
8445
|
-
|
8446
|
-
//check if property name starts with "_"
|
8447
|
-
parser.addListener("property", function(event){
|
8448
|
-
var property = event.property;
|
8449
|
-
|
8450
|
-
if (property.hack == "_") {
|
8451
|
-
reporter.report("Property with underscore prefix found.", event.property.line, event.property.col, rule);
|
8452
|
-
}
|
8453
|
-
});
|
8454
|
-
}
|
8455
|
-
});
|
8456
|
-
/*
|
8457
|
-
* Rule: Headings (h1-h6) should be defined only once.
|
8458
|
-
*/
|
8459
|
-
/*global CSSLint*/
|
8460
|
-
CSSLint.addRule({
|
8461
|
-
|
8462
|
-
//rule information
|
8463
|
-
id: "unique-headings",
|
8464
|
-
name: "Headings should only be defined once",
|
8465
|
-
desc: "Headings should be defined only once.",
|
8466
|
-
browsers: "All",
|
8467
|
-
|
8468
|
-
//initialization
|
8469
|
-
init: function(parser, reporter){
|
8470
|
-
var rule = this;
|
8471
|
-
|
8472
|
-
var headings = {
|
8473
|
-
h1: 0,
|
8474
|
-
h2: 0,
|
8475
|
-
h3: 0,
|
8476
|
-
h4: 0,
|
8477
|
-
h5: 0,
|
8478
|
-
h6: 0
|
8479
|
-
};
|
8480
|
-
|
8481
|
-
parser.addListener("startrule", function(event){
|
8482
|
-
var selectors = event.selectors,
|
8483
|
-
selector,
|
8484
|
-
part,
|
8485
|
-
pseudo,
|
8486
|
-
i, j;
|
8487
|
-
|
8488
|
-
for (i=0; i < selectors.length; i++){
|
8489
|
-
selector = selectors[i];
|
8490
|
-
part = selector.parts[selector.parts.length-1];
|
8491
|
-
|
8492
|
-
if (part.elementName && /(h[1-6])/i.test(part.elementName.toString())){
|
8493
|
-
|
8494
|
-
for (j=0; j < part.modifiers.length; j++){
|
8495
|
-
if (part.modifiers[j].type == "pseudo"){
|
8496
|
-
pseudo = true;
|
8497
|
-
break;
|
8498
|
-
}
|
8499
|
-
}
|
8500
|
-
|
8501
|
-
if (!pseudo){
|
8502
|
-
headings[RegExp.$1]++;
|
8503
|
-
if (headings[RegExp.$1] > 1) {
|
8504
|
-
reporter.report("Heading (" + part.elementName + ") has already been defined.", part.line, part.col, rule);
|
8505
|
-
}
|
8506
|
-
}
|
8507
|
-
}
|
8508
|
-
}
|
8509
|
-
});
|
8510
|
-
|
8511
|
-
parser.addListener("endstylesheet", function(event){
|
8512
|
-
var prop,
|
8513
|
-
messages = [];
|
8514
|
-
|
8515
|
-
for (prop in headings){
|
8516
|
-
if (headings.hasOwnProperty(prop)){
|
8517
|
-
if (headings[prop] > 1){
|
8518
|
-
messages.push(headings[prop] + " " + prop + "s");
|
8519
|
-
}
|
8520
|
-
}
|
8521
|
-
}
|
8522
|
-
|
8523
|
-
if (messages.length){
|
8524
|
-
reporter.rollupWarn("You have " + messages.join(", ") + " defined in this stylesheet.", rule);
|
8525
|
-
}
|
8526
|
-
});
|
8527
|
-
}
|
8528
|
-
|
8529
|
-
});
|
8530
|
-
/*
|
8531
|
-
* Rule: Don't use universal selector because it's slow.
|
8532
|
-
*/
|
8533
|
-
/*global CSSLint*/
|
8534
|
-
CSSLint.addRule({
|
8535
|
-
|
8536
|
-
//rule information
|
8537
|
-
id: "universal-selector",
|
8538
|
-
name: "Disallow universal selector",
|
8539
|
-
desc: "The universal selector (*) is known to be slow.",
|
8540
|
-
browsers: "All",
|
8541
|
-
|
8542
|
-
//initialization
|
8543
|
-
init: function(parser, reporter){
|
8544
|
-
var rule = this;
|
8545
|
-
|
8546
|
-
parser.addListener("startrule", function(event){
|
8547
|
-
var selectors = event.selectors,
|
8548
|
-
selector,
|
8549
|
-
part,
|
8550
|
-
modifier,
|
8551
|
-
i, j, k;
|
8552
|
-
|
8553
|
-
for (i=0; i < selectors.length; i++){
|
8554
|
-
selector = selectors[i];
|
8555
|
-
|
8556
|
-
part = selector.parts[selector.parts.length-1];
|
8557
|
-
if (part.elementName == "*"){
|
8558
|
-
reporter.report(rule.desc, part.line, part.col, rule);
|
8559
|
-
}
|
8560
|
-
}
|
8561
|
-
});
|
8562
|
-
}
|
8563
|
-
|
8564
|
-
});
|
8565
|
-
/*
|
8566
|
-
* Rule: Don't use unqualified attribute selectors because they're just like universal selectors.
|
8567
|
-
*/
|
8568
|
-
/*global CSSLint*/
|
8569
|
-
CSSLint.addRule({
|
8570
|
-
|
8571
|
-
//rule information
|
8572
|
-
id: "unqualified-attributes",
|
8573
|
-
name: "Disallow unqualified attribute selectors",
|
8574
|
-
desc: "Unqualified attribute selectors are known to be slow.",
|
8575
|
-
browsers: "All",
|
8576
|
-
|
8577
|
-
//initialization
|
8578
|
-
init: function(parser, reporter){
|
8579
|
-
var rule = this;
|
8580
|
-
|
8581
|
-
parser.addListener("startrule", function(event){
|
8582
|
-
|
8583
|
-
var selectors = event.selectors,
|
8584
|
-
selector,
|
8585
|
-
part,
|
8586
|
-
modifier,
|
8587
|
-
i, j, k;
|
8588
|
-
|
8589
|
-
for (i=0; i < selectors.length; i++){
|
8590
|
-
selector = selectors[i];
|
8591
|
-
|
8592
|
-
part = selector.parts[selector.parts.length-1];
|
8593
|
-
if (part.type == parser.SELECTOR_PART_TYPE){
|
8594
|
-
for (k=0; k < part.modifiers.length; k++){
|
8595
|
-
modifier = part.modifiers[k];
|
8596
|
-
if (modifier.type == "attribute" && (!part.elementName || part.elementName == "*")){
|
8597
|
-
reporter.report(rule.desc, part.line, part.col, rule);
|
8598
|
-
}
|
8599
|
-
}
|
8600
|
-
}
|
8601
|
-
|
8602
|
-
}
|
8603
|
-
});
|
8604
|
-
}
|
8605
|
-
|
8606
|
-
});
|
8607
|
-
/*
|
8608
|
-
* Rule: When using a vendor-prefixed property, make sure to
|
8609
|
-
* include the standard one.
|
8610
|
-
*/
|
8611
|
-
/*global CSSLint*/
|
8612
|
-
CSSLint.addRule({
|
8613
|
-
|
8614
|
-
//rule information
|
8615
|
-
id: "vendor-prefix",
|
8616
|
-
name: "Require standard property with vendor prefix",
|
8617
|
-
desc: "When using a vendor-prefixed property, make sure to include the standard one.",
|
8618
|
-
browsers: "All",
|
8619
|
-
|
8620
|
-
//initialization
|
8621
|
-
init: function(parser, reporter){
|
8622
|
-
var rule = this,
|
8623
|
-
properties,
|
8624
|
-
num,
|
8625
|
-
propertiesToCheck = {
|
8626
|
-
"-webkit-border-radius": "border-radius",
|
8627
|
-
"-webkit-border-top-left-radius": "border-top-left-radius",
|
8628
|
-
"-webkit-border-top-right-radius": "border-top-right-radius",
|
8629
|
-
"-webkit-border-bottom-left-radius": "border-bottom-left-radius",
|
8630
|
-
"-webkit-border-bottom-right-radius": "border-bottom-right-radius",
|
8631
|
-
|
8632
|
-
"-o-border-radius": "border-radius",
|
8633
|
-
"-o-border-top-left-radius": "border-top-left-radius",
|
8634
|
-
"-o-border-top-right-radius": "border-top-right-radius",
|
8635
|
-
"-o-border-bottom-left-radius": "border-bottom-left-radius",
|
8636
|
-
"-o-border-bottom-right-radius": "border-bottom-right-radius",
|
8637
|
-
|
8638
|
-
"-moz-border-radius": "border-radius",
|
8639
|
-
"-moz-border-radius-topleft": "border-top-left-radius",
|
8640
|
-
"-moz-border-radius-topright": "border-top-right-radius",
|
8641
|
-
"-moz-border-radius-bottomleft": "border-bottom-left-radius",
|
8642
|
-
"-moz-border-radius-bottomright": "border-bottom-right-radius",
|
8643
|
-
|
8644
|
-
"-moz-column-count": "column-count",
|
8645
|
-
"-webkit-column-count": "column-count",
|
8646
|
-
|
8647
|
-
"-moz-column-gap": "column-gap",
|
8648
|
-
"-webkit-column-gap": "column-gap",
|
8649
|
-
|
8650
|
-
"-moz-column-rule": "column-rule",
|
8651
|
-
"-webkit-column-rule": "column-rule",
|
8652
|
-
|
8653
|
-
"-moz-column-rule-style": "column-rule-style",
|
8654
|
-
"-webkit-column-rule-style": "column-rule-style",
|
8655
|
-
|
8656
|
-
"-moz-column-rule-color": "column-rule-color",
|
8657
|
-
"-webkit-column-rule-color": "column-rule-color",
|
8658
|
-
|
8659
|
-
"-moz-column-rule-width": "column-rule-width",
|
8660
|
-
"-webkit-column-rule-width": "column-rule-width",
|
8661
|
-
|
8662
|
-
"-moz-column-width": "column-width",
|
8663
|
-
"-webkit-column-width": "column-width",
|
8664
|
-
|
8665
|
-
"-webkit-column-span": "column-span",
|
8666
|
-
"-webkit-columns": "columns",
|
8667
|
-
|
8668
|
-
"-moz-box-shadow": "box-shadow",
|
8669
|
-
"-webkit-box-shadow": "box-shadow",
|
8670
|
-
|
8671
|
-
"-moz-transform" : "transform",
|
8672
|
-
"-webkit-transform" : "transform",
|
8673
|
-
"-o-transform" : "transform",
|
8674
|
-
"-ms-transform" : "transform",
|
8675
|
-
|
8676
|
-
"-moz-transform-origin" : "transform-origin",
|
8677
|
-
"-webkit-transform-origin" : "transform-origin",
|
8678
|
-
"-o-transform-origin" : "transform-origin",
|
8679
|
-
"-ms-transform-origin" : "transform-origin",
|
8680
|
-
|
8681
|
-
"-moz-box-sizing" : "box-sizing",
|
8682
|
-
"-webkit-box-sizing" : "box-sizing",
|
8683
|
-
|
8684
|
-
"-moz-user-select" : "user-select",
|
8685
|
-
"-khtml-user-select" : "user-select",
|
8686
|
-
"-webkit-user-select" : "user-select"
|
8687
|
-
};
|
8688
|
-
|
8689
|
-
//event handler for beginning of rules
|
8690
|
-
function startRule(){
|
8691
|
-
properties = {};
|
8692
|
-
num=1;
|
8693
|
-
}
|
8694
|
-
|
8695
|
-
//event handler for end of rules
|
8696
|
-
function endRule(event){
|
8697
|
-
var prop,
|
8698
|
-
i, len,
|
8699
|
-
standard,
|
8700
|
-
needed,
|
8701
|
-
actual,
|
8702
|
-
needsStandard = [];
|
8703
|
-
|
8704
|
-
for (prop in properties){
|
8705
|
-
if (propertiesToCheck[prop]){
|
8706
|
-
needsStandard.push({ actual: prop, needed: propertiesToCheck[prop]});
|
8707
|
-
}
|
8708
|
-
}
|
8709
|
-
|
8710
|
-
for (i=0, len=needsStandard.length; i < len; i++){
|
8711
|
-
needed = needsStandard[i].needed;
|
8712
|
-
actual = needsStandard[i].actual;
|
8713
|
-
|
8714
|
-
if (!properties[needed]){
|
8715
|
-
reporter.report("Missing standard property '" + needed + "' to go along with '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
|
8716
|
-
} else {
|
8717
|
-
//make sure standard property is last
|
8718
|
-
if (properties[needed][0].pos < properties[actual][0].pos){
|
8719
|
-
reporter.report("Standard property '" + needed + "' should come after vendor-prefixed property '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
|
8720
|
-
}
|
8721
|
-
}
|
8722
|
-
}
|
8723
|
-
|
8724
|
-
}
|
8725
|
-
|
8726
|
-
parser.addListener("startrule", startRule);
|
8727
|
-
parser.addListener("startfontface", startRule);
|
8728
|
-
parser.addListener("startpage", startRule);
|
8729
|
-
parser.addListener("startpagemargin", startRule);
|
8730
|
-
parser.addListener("startkeyframerule", startRule);
|
8731
|
-
|
8732
|
-
parser.addListener("property", function(event){
|
8733
|
-
var name = event.property.text.toLowerCase();
|
8734
|
-
|
8735
|
-
if (!properties[name]){
|
8736
|
-
properties[name] = [];
|
8737
|
-
}
|
8738
|
-
|
8739
|
-
properties[name].push({ name: event.property, value : event.value, pos:num++ });
|
8740
|
-
});
|
8741
|
-
|
8742
|
-
parser.addListener("endrule", endRule);
|
8743
|
-
parser.addListener("endfontface", endRule);
|
8744
|
-
parser.addListener("endpage", endRule);
|
8745
|
-
parser.addListener("endpagemargin", endRule);
|
8746
|
-
parser.addListener("endkeyframerule", endRule);
|
8747
|
-
}
|
8748
|
-
|
8749
|
-
});
|
8750
|
-
/*
|
8751
|
-
* Rule: You don't need to specify units when a value is 0.
|
8752
|
-
*/
|
8753
|
-
/*global CSSLint*/
|
8754
|
-
CSSLint.addRule({
|
8755
|
-
|
8756
|
-
//rule information
|
8757
|
-
id: "zero-units",
|
8758
|
-
name: "Disallow units for 0 values",
|
8759
|
-
desc: "You don't need to specify units when a value is 0.",
|
8760
|
-
browsers: "All",
|
8761
|
-
|
8762
|
-
//initialization
|
8763
|
-
init: function(parser, reporter){
|
8764
|
-
var rule = this;
|
8765
|
-
|
8766
|
-
//count how many times "float" is used
|
8767
|
-
parser.addListener("property", function(event){
|
8768
|
-
var parts = event.value.parts,
|
8769
|
-
i = 0,
|
8770
|
-
len = parts.length;
|
8771
|
-
|
8772
|
-
while(i < len){
|
8773
|
-
if ((parts[i].units || parts[i].type == "percentage") && parts[i].value === 0 && parts[i].type != "time"){
|
8774
|
-
reporter.report("Values of 0 shouldn't have units specified.", parts[i].line, parts[i].col, rule);
|
8775
|
-
}
|
8776
|
-
i++;
|
8777
|
-
}
|
8778
|
-
|
8779
|
-
});
|
8780
|
-
|
8781
|
-
}
|
8782
|
-
|
8783
|
-
});
|
8784
|
-
/*global CSSLint*/
|
8785
|
-
(function() {
|
8786
|
-
|
8787
|
-
/**
|
8788
|
-
* Replace special characters before write to output.
|
8789
|
-
*
|
8790
|
-
* Rules:
|
8791
|
-
* - single quotes is the escape sequence for double-quotes
|
8792
|
-
* - & is the escape sequence for &
|
8793
|
-
* - < is the escape sequence for <
|
8794
|
-
* - > is the escape sequence for >
|
8795
|
-
*
|
8796
|
-
* @param {String} message to escape
|
8797
|
-
* @return escaped message as {String}
|
8798
|
-
*/
|
8799
|
-
var xmlEscape = function(str) {
|
8800
|
-
if (!str || str.constructor !== String) {
|
8801
|
-
return "";
|
8802
|
-
}
|
8803
|
-
|
8804
|
-
return str.replace(/[\"&><]/g, function(match) {
|
8805
|
-
switch (match) {
|
8806
|
-
case "\"":
|
8807
|
-
return """;
|
8808
|
-
case "&":
|
8809
|
-
return "&";
|
8810
|
-
case "<":
|
8811
|
-
return "<";
|
8812
|
-
case ">":
|
8813
|
-
return ">";
|
8814
|
-
}
|
8815
|
-
});
|
8816
|
-
};
|
8817
|
-
|
8818
|
-
CSSLint.addFormatter({
|
8819
|
-
//format information
|
8820
|
-
id: "checkstyle-xml",
|
8821
|
-
name: "Checkstyle XML format",
|
8822
|
-
|
8823
|
-
/**
|
8824
|
-
* Return opening root XML tag.
|
8825
|
-
* @return {String} to prepend before all results
|
8826
|
-
*/
|
8827
|
-
startFormat: function(){
|
8828
|
-
return "<?xml version=\"1.0\" encoding=\"utf-8\"?><checkstyle>";
|
8829
|
-
},
|
8830
|
-
|
8831
|
-
/**
|
8832
|
-
* Return closing root XML tag.
|
8833
|
-
* @return {String} to append after all results
|
8834
|
-
*/
|
8835
|
-
endFormat: function(){
|
8836
|
-
return "</checkstyle>";
|
8837
|
-
},
|
8838
|
-
|
8839
|
-
/**
|
8840
|
-
* Returns message when there is a file read error.
|
8841
|
-
* @param {String} filename The name of the file that caused the error.
|
8842
|
-
* @param {String} message The error message
|
8843
|
-
* @return {String} The error message.
|
8844
|
-
*/
|
8845
|
-
readError: function(filename, message) {
|
8846
|
-
return "<file name=\"" + xmlEscape(filename) + "\"><error line=\"0\" column=\"0\" severty=\"error\" message=\"" + xmlEscape(message) + "\"></error></file>";
|
8847
|
-
},
|
8848
|
-
|
8849
|
-
/**
|
8850
|
-
* Given CSS Lint results for a file, return output for this format.
|
8851
|
-
* @param results {Object} with error and warning messages
|
8852
|
-
* @param filename {String} relative file path
|
8853
|
-
* @param options {Object} (UNUSED for now) specifies special handling of output
|
8854
|
-
* @return {String} output for results
|
8855
|
-
*/
|
8856
|
-
formatResults: function(results, filename, options) {
|
8857
|
-
var messages = results.messages,
|
8858
|
-
output = [];
|
8859
|
-
|
8860
|
-
/**
|
8861
|
-
* Generate a source string for a rule.
|
8862
|
-
* Checkstyle source strings usually resemble Java class names e.g
|
8863
|
-
* net.csslint.SomeRuleName
|
8864
|
-
* @param {Object} rule
|
8865
|
-
* @return rule source as {String}
|
8866
|
-
*/
|
8867
|
-
var generateSource = function(rule) {
|
8868
|
-
if (!rule || !('name' in rule)) {
|
8869
|
-
return "";
|
8870
|
-
}
|
8871
|
-
return 'net.csslint.' + rule.name.replace(/\s/g,'');
|
8872
|
-
};
|
8873
|
-
|
8874
|
-
|
8875
|
-
|
8876
|
-
if (messages.length > 0) {
|
8877
|
-
output.push("<file name=\""+filename+"\">");
|
8878
|
-
CSSLint.Util.forEach(messages, function (message, i) {
|
8879
|
-
//ignore rollups for now
|
8880
|
-
if (!message.rollup) {
|
8881
|
-
output.push("<error line=\"" + message.line + "\" column=\"" + message.col + "\" severity=\"" + message.type + "\"" +
|
8882
|
-
" message=\"" + xmlEscape(message.message) + "\" source=\"" + generateSource(message.rule) +"\"/>");
|
8883
|
-
}
|
8884
|
-
});
|
8885
|
-
output.push("</file>");
|
8886
|
-
}
|
8887
|
-
|
8888
|
-
return output.join("");
|
8889
|
-
}
|
8890
|
-
});
|
8891
|
-
|
8892
|
-
}());
|
8893
|
-
/*global CSSLint*/
|
8894
|
-
CSSLint.addFormatter({
|
8895
|
-
//format information
|
8896
|
-
id: "compact",
|
8897
|
-
name: "Compact, 'porcelain' format",
|
8898
|
-
|
8899
|
-
/**
|
8900
|
-
* Return content to be printed before all file results.
|
8901
|
-
* @return {String} to prepend before all results
|
8902
|
-
*/
|
8903
|
-
startFormat: function() {
|
8904
|
-
return "";
|
8905
|
-
},
|
8906
|
-
|
8907
|
-
/**
|
8908
|
-
* Return content to be printed after all file results.
|
8909
|
-
* @return {String} to append after all results
|
8910
|
-
*/
|
8911
|
-
endFormat: function() {
|
8912
|
-
return "";
|
8913
|
-
},
|
8914
|
-
|
8915
|
-
/**
|
8916
|
-
* Given CSS Lint results for a file, return output for this format.
|
8917
|
-
* @param results {Object} with error and warning messages
|
8918
|
-
* @param filename {String} relative file path
|
8919
|
-
* @param options {Object} (Optional) specifies special handling of output
|
8920
|
-
* @return {String} output for results
|
8921
|
-
*/
|
8922
|
-
formatResults: function(results, filename, options) {
|
8923
|
-
var messages = results.messages,
|
8924
|
-
output = "";
|
8925
|
-
options = options || {};
|
8926
|
-
|
8927
|
-
/**
|
8928
|
-
* Capitalize and return given string.
|
8929
|
-
* @param str {String} to capitalize
|
8930
|
-
* @return {String} capitalized
|
8931
|
-
*/
|
8932
|
-
var capitalize = function(str) {
|
8933
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
8934
|
-
};
|
8935
|
-
|
8936
|
-
if (messages.length === 0) {
|
8937
|
-
return options.quiet ? "" : filename + ": Lint Free!";
|
8938
|
-
}
|
8939
|
-
|
8940
|
-
CSSLint.Util.forEach(messages, function(message, i) {
|
8941
|
-
if (message.rollup) {
|
8942
|
-
output += filename + ": " + capitalize(message.type) + " - " + message.message + "\n";
|
8943
|
-
} else {
|
8944
|
-
output += filename + ": " + "line " + message.line +
|
8945
|
-
", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + "\n";
|
8946
|
-
}
|
8947
|
-
});
|
8948
|
-
|
8949
|
-
return output;
|
8950
|
-
}
|
8951
|
-
});
|
8952
|
-
/*global CSSLint*/
|
8953
|
-
CSSLint.addFormatter({
|
8954
|
-
//format information
|
8955
|
-
id: "csslint-xml",
|
8956
|
-
name: "CSSLint XML format",
|
8957
|
-
|
8958
|
-
/**
|
8959
|
-
* Return opening root XML tag.
|
8960
|
-
* @return {String} to prepend before all results
|
8961
|
-
*/
|
8962
|
-
startFormat: function(){
|
8963
|
-
return "<?xml version=\"1.0\" encoding=\"utf-8\"?><csslint>";
|
8964
|
-
},
|
8965
|
-
|
8966
|
-
/**
|
8967
|
-
* Return closing root XML tag.
|
8968
|
-
* @return {String} to append after all results
|
8969
|
-
*/
|
8970
|
-
endFormat: function(){
|
8971
|
-
return "</csslint>";
|
8972
|
-
},
|
8973
|
-
|
8974
|
-
/**
|
8975
|
-
* Given CSS Lint results for a file, return output for this format.
|
8976
|
-
* @param results {Object} with error and warning messages
|
8977
|
-
* @param filename {String} relative file path
|
8978
|
-
* @param options {Object} (UNUSED for now) specifies special handling of output
|
8979
|
-
* @return {String} output for results
|
8980
|
-
*/
|
8981
|
-
formatResults: function(results, filename, options) {
|
8982
|
-
var messages = results.messages,
|
8983
|
-
output = [];
|
8984
|
-
|
8985
|
-
/**
|
8986
|
-
* Replace special characters before write to output.
|
8987
|
-
*
|
8988
|
-
* Rules:
|
8989
|
-
* - single quotes is the escape sequence for double-quotes
|
8990
|
-
* - & is the escape sequence for &
|
8991
|
-
* - < is the escape sequence for <
|
8992
|
-
* - > is the escape sequence for >
|
8993
|
-
*
|
8994
|
-
* @param {String} message to escape
|
8995
|
-
* @return escaped message as {String}
|
8996
|
-
*/
|
8997
|
-
var escapeSpecialCharacters = function(str) {
|
8998
|
-
if (!str || str.constructor !== String) {
|
8999
|
-
return "";
|
9000
|
-
}
|
9001
|
-
return str.replace(/\"/g, "'").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
9002
|
-
};
|
9003
|
-
|
9004
|
-
if (messages.length > 0) {
|
9005
|
-
output.push("<file name=\""+filename+"\">");
|
9006
|
-
CSSLint.Util.forEach(messages, function (message, i) {
|
9007
|
-
if (message.rollup) {
|
9008
|
-
output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
|
9009
|
-
} else {
|
9010
|
-
output.push("<issue line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
|
9011
|
-
" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
|
9012
|
-
}
|
9013
|
-
});
|
9014
|
-
output.push("</file>");
|
9015
|
-
}
|
9016
|
-
|
9017
|
-
return output.join("");
|
9018
|
-
}
|
9019
|
-
});
|
9020
|
-
/*global CSSLint*/
|
9021
|
-
CSSLint.addFormatter({
|
9022
|
-
//format information
|
9023
|
-
id: "junit-xml",
|
9024
|
-
name: "JUNIT XML format",
|
9025
|
-
|
9026
|
-
/**
|
9027
|
-
* Return opening root XML tag.
|
9028
|
-
* @return {String} to prepend before all results
|
9029
|
-
*/
|
9030
|
-
startFormat: function(){
|
9031
|
-
return "<?xml version=\"1.0\" encoding=\"utf-8\"?><testsuites>";
|
9032
|
-
},
|
9033
|
-
|
9034
|
-
/**
|
9035
|
-
* Return closing root XML tag.
|
9036
|
-
* @return {String} to append after all results
|
9037
|
-
*/
|
9038
|
-
endFormat: function() {
|
9039
|
-
return "</testsuites>";
|
9040
|
-
},
|
9041
|
-
|
9042
|
-
/**
|
9043
|
-
* Given CSS Lint results for a file, return output for this format.
|
9044
|
-
* @param results {Object} with error and warning messages
|
9045
|
-
* @param filename {String} relative file path
|
9046
|
-
* @param options {Object} (UNUSED for now) specifies special handling of output
|
9047
|
-
* @return {String} output for results
|
9048
|
-
*/
|
9049
|
-
formatResults: function(results, filename, options) {
|
9050
|
-
|
9051
|
-
var messages = results.messages,
|
9052
|
-
output = [],
|
9053
|
-
tests = {
|
9054
|
-
'error': 0,
|
9055
|
-
'failure': 0
|
9056
|
-
};
|
9057
|
-
|
9058
|
-
/**
|
9059
|
-
* Generate a source string for a rule.
|
9060
|
-
* JUNIT source strings usually resemble Java class names e.g
|
9061
|
-
* net.csslint.SomeRuleName
|
9062
|
-
* @param {Object} rule
|
9063
|
-
* @return rule source as {String}
|
9064
|
-
*/
|
9065
|
-
var generateSource = function(rule) {
|
9066
|
-
if (!rule || !('name' in rule)) {
|
9067
|
-
return "";
|
9068
|
-
}
|
9069
|
-
return 'net.csslint.' + rule.name.replace(/\s/g,'');
|
9070
|
-
};
|
9071
|
-
|
9072
|
-
/**
|
9073
|
-
* Replace special characters before write to output.
|
9074
|
-
*
|
9075
|
-
* Rules:
|
9076
|
-
* - single quotes is the escape sequence for double-quotes
|
9077
|
-
* - < is the escape sequence for <
|
9078
|
-
* - > is the escape sequence for >
|
9079
|
-
*
|
9080
|
-
* @param {String} message to escape
|
9081
|
-
* @return escaped message as {String}
|
9082
|
-
*/
|
9083
|
-
var escapeSpecialCharacters = function(str) {
|
9084
|
-
|
9085
|
-
if (!str || str.constructor !== String) {
|
9086
|
-
return "";
|
9087
|
-
}
|
9088
|
-
|
9089
|
-
return str.replace(/\"/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
9090
|
-
|
9091
|
-
};
|
9092
|
-
|
9093
|
-
if (messages.length > 0) {
|
9094
|
-
|
9095
|
-
messages.forEach(function (message, i) {
|
9096
|
-
|
9097
|
-
// since junit has no warning class
|
9098
|
-
// all issues as errors
|
9099
|
-
var type = message.type === 'warning' ? 'error' : message.type;
|
9100
|
-
|
9101
|
-
//ignore rollups for now
|
9102
|
-
if (!message.rollup) {
|
9103
|
-
|
9104
|
-
// build the test case seperately, once joined
|
9105
|
-
// we'll add it to a custom array filtered by type
|
9106
|
-
output.push("<testcase time=\"0\" name=\"" + generateSource(message.rule) + "\">");
|
9107
|
-
output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\"><![CDATA[" + message.line + ':' + message.col + ':' + escapeSpecialCharacters(message.evidence) + "]]></" + type + ">");
|
9108
|
-
output.push("</testcase>");
|
9109
|
-
|
9110
|
-
tests[type] += 1;
|
9111
|
-
|
9112
|
-
}
|
9113
|
-
|
9114
|
-
});
|
9115
|
-
|
9116
|
-
output.unshift("<testsuite time=\"0\" tests=\"" + messages.length + "\" skipped=\"0\" errors=\"" + tests.error + "\" failures=\"" + tests.failure + "\" package=\"net.csslint\" name=\"" + filename + "\">");
|
9117
|
-
output.push("</testsuite>");
|
9118
|
-
|
9119
|
-
}
|
9120
|
-
|
9121
|
-
return output.join("");
|
9122
|
-
|
9123
|
-
}
|
9124
|
-
});
|
9125
|
-
/*global CSSLint*/
|
9126
|
-
CSSLint.addFormatter({
|
9127
|
-
//format information
|
9128
|
-
id: "lint-xml",
|
9129
|
-
name: "Lint XML format",
|
9130
|
-
|
9131
|
-
/**
|
9132
|
-
* Return opening root XML tag.
|
9133
|
-
* @return {String} to prepend before all results
|
9134
|
-
*/
|
9135
|
-
startFormat: function(){
|
9136
|
-
return "<?xml version=\"1.0\" encoding=\"utf-8\"?><lint>";
|
9137
|
-
},
|
9138
|
-
|
9139
|
-
/**
|
9140
|
-
* Return closing root XML tag.
|
9141
|
-
* @return {String} to append after all results
|
9142
|
-
*/
|
9143
|
-
endFormat: function(){
|
9144
|
-
return "</lint>";
|
9145
|
-
},
|
9146
|
-
|
9147
|
-
/**
|
9148
|
-
* Given CSS Lint results for a file, return output for this format.
|
9149
|
-
* @param results {Object} with error and warning messages
|
9150
|
-
* @param filename {String} relative file path
|
9151
|
-
* @param options {Object} (UNUSED for now) specifies special handling of output
|
9152
|
-
* @return {String} output for results
|
9153
|
-
*/
|
9154
|
-
formatResults: function(results, filename, options) {
|
9155
|
-
var messages = results.messages,
|
9156
|
-
output = [];
|
9157
|
-
|
9158
|
-
/**
|
9159
|
-
* Replace special characters before write to output.
|
9160
|
-
*
|
9161
|
-
* Rules:
|
9162
|
-
* - single quotes is the escape sequence for double-quotes
|
9163
|
-
* - & is the escape sequence for &
|
9164
|
-
* - < is the escape sequence for <
|
9165
|
-
* - > is the escape sequence for >
|
9166
|
-
*
|
9167
|
-
* @param {String} message to escape
|
9168
|
-
* @return escaped message as {String}
|
9169
|
-
*/
|
9170
|
-
var escapeSpecialCharacters = function(str) {
|
9171
|
-
if (!str || str.constructor !== String) {
|
9172
|
-
return "";
|
9173
|
-
}
|
9174
|
-
return str.replace(/\"/g, "'").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
9175
|
-
};
|
9176
|
-
|
9177
|
-
if (messages.length > 0) {
|
9178
|
-
|
9179
|
-
output.push("<file name=\""+filename+"\">");
|
9180
|
-
CSSLint.Util.forEach(messages, function (message, i) {
|
9181
|
-
if (message.rollup) {
|
9182
|
-
output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
|
9183
|
-
} else {
|
9184
|
-
output.push("<issue line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
|
9185
|
-
" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
|
9186
|
-
}
|
9187
|
-
});
|
9188
|
-
output.push("</file>");
|
9189
|
-
}
|
9190
|
-
|
9191
|
-
return output.join("");
|
9192
|
-
}
|
9193
|
-
});
|
9194
|
-
/*global CSSLint*/
|
9195
|
-
CSSLint.addFormatter({
|
9196
|
-
//format information
|
9197
|
-
id: "text",
|
9198
|
-
name: "Plain Text",
|
9199
|
-
|
9200
|
-
/**
|
9201
|
-
* Return content to be printed before all file results.
|
9202
|
-
* @return {String} to prepend before all results
|
9203
|
-
*/
|
9204
|
-
startFormat: function() {
|
9205
|
-
return "";
|
9206
|
-
},
|
9207
|
-
|
9208
|
-
/**
|
9209
|
-
* Return content to be printed after all file results.
|
9210
|
-
* @return {String} to append after all results
|
9211
|
-
*/
|
9212
|
-
endFormat: function() {
|
9213
|
-
return "";
|
9214
|
-
},
|
9215
|
-
|
9216
|
-
/**
|
9217
|
-
* Given CSS Lint results for a file, return output for this format.
|
9218
|
-
* @param results {Object} with error and warning messages
|
9219
|
-
* @param filename {String} relative file path
|
9220
|
-
* @param options {Object} (Optional) specifies special handling of output
|
9221
|
-
* @return {String} output for results
|
9222
|
-
*/
|
9223
|
-
formatResults: function(results, filename, options) {
|
9224
|
-
var messages = results.messages,
|
9225
|
-
output = "";
|
9226
|
-
options = options || {};
|
9227
|
-
|
9228
|
-
if (messages.length === 0) {
|
9229
|
-
return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + ".";
|
9230
|
-
}
|
9231
|
-
|
9232
|
-
output = "\n\ncsslint: There are " + messages.length + " problems in " + filename + ".";
|
9233
|
-
var pos = filename.lastIndexOf("/"),
|
9234
|
-
shortFilename = filename;
|
9235
|
-
|
9236
|
-
if (pos === -1){
|
9237
|
-
pos = filename.lastIndexOf("\\");
|
9238
|
-
}
|
9239
|
-
if (pos > -1){
|
9240
|
-
shortFilename = filename.substring(pos+1);
|
9241
|
-
}
|
9242
|
-
|
9243
|
-
CSSLint.Util.forEach(messages, function (message, i) {
|
9244
|
-
output = output + "\n\n" + shortFilename;
|
9245
|
-
if (message.rollup) {
|
9246
|
-
output += "\n" + (i+1) + ": " + message.type;
|
9247
|
-
output += "\n" + message.message;
|
9248
|
-
} else {
|
9249
|
-
output += "\n" + (i+1) + ": " + message.type + " at line " + message.line + ", col " + message.col;
|
9250
|
-
output += "\n" + message.message;
|
9251
|
-
output += "\n" + message.evidence;
|
9252
|
-
}
|
9253
|
-
});
|
9254
|
-
|
9255
|
-
return output;
|
9256
|
-
}
|
9257
|
-
});
|
9258
|
-
return CSSLint;
|
9259
|
-
})();
|
9260
|
-
/*
|
9261
|
-
* Encapsulates all of the CLI functionality. The api argument simply
|
9262
|
-
* provides environment-specific functionality.
|
9263
|
-
*/
|
9264
|
-
/*global CSSLint*/
|
9265
|
-
function cli(api){
|
9266
|
-
|
9267
|
-
var globalOptions = {
|
9268
|
-
"help" : { "format" : "", "description" : "Displays this information."},
|
9269
|
-
"format" : { "format" : "<format>", "description" : "Indicate which format to use for output."},
|
9270
|
-
"list-rules" : { "format" : "", "description" : "Outputs all of the rules available."},
|
9271
|
-
"quiet" : { "format" : "", "description" : "Only output when errors are present."},
|
9272
|
-
"errors" : { "format" : "<rule[,rule]+>", "description" : "Indicate which rules to include as errors."},
|
9273
|
-
"warnings" : { "format" : "<rule[,rule]+>", "description" : "Indicate which rules to include as warnings."},
|
9274
|
-
"ignore" : { "format" : "<rule[,rule]+>", "description" : "Indicate which rules to ignore completely."},
|
9275
|
-
"exclude-list": { "format" : "<file|dir[,file|dir]+>", "description" : "Indicate which files/directories to exclude from being linted."},
|
9276
|
-
"version" : { "format" : "", "description" : "Outputs the current version number."}
|
9277
|
-
};
|
9278
|
-
|
9279
|
-
//-------------------------------------------------------------------------
|
9280
|
-
// Helper functions
|
9281
|
-
//-------------------------------------------------------------------------
|
9282
|
-
|
9283
|
-
/**
|
9284
|
-
* Returns an array of messages for a particular type.
|
9285
|
-
* @param messages {Array} Array of CSS Lint messages.
|
9286
|
-
* @param type {String} The type of message to filter on.
|
9287
|
-
* @return {Array} An array of matching messages.
|
9288
|
-
*/
|
9289
|
-
function pluckByType(messages, type){
|
9290
|
-
return messages.filter(function(message) {
|
9291
|
-
return message.type === type;
|
9292
|
-
});
|
9293
|
-
}
|
9294
|
-
|
9295
|
-
/**
|
9296
|
-
* Returns a ruleset object based on the CLI options.
|
9297
|
-
* @param options {Object} The CLI options.
|
9298
|
-
* @return {Object} A ruleset object.
|
9299
|
-
*/
|
9300
|
-
function gatherRules(options, ruleset){
|
9301
|
-
var warnings = options.rules || options.warnings,
|
9302
|
-
errors = options.errors;
|
9303
|
-
|
9304
|
-
if (warnings){
|
9305
|
-
ruleset = ruleset || {};
|
9306
|
-
warnings.split(",").forEach(function(value){
|
9307
|
-
ruleset[value] = 1;
|
9308
|
-
});
|
9309
|
-
}
|
9310
|
-
|
9311
|
-
if (errors){
|
9312
|
-
ruleset = ruleset || {};
|
9313
|
-
errors.split(",").forEach(function(value){
|
9314
|
-
ruleset[value] = 2;
|
9315
|
-
});
|
9316
|
-
}
|
9317
|
-
|
9318
|
-
return ruleset;
|
9319
|
-
}
|
9320
|
-
|
9321
|
-
/**
|
9322
|
-
* Filters out rules using the ignore command line option.
|
9323
|
-
* @param options {Object} the CLI options
|
9324
|
-
* @return {Object} A ruleset object.
|
9325
|
-
*/
|
9326
|
-
function filterRules(options) {
|
9327
|
-
var ignore = options.ignore,
|
9328
|
-
ruleset = null;
|
9329
|
-
|
9330
|
-
if (ignore) {
|
9331
|
-
ruleset = CSSLint.getRuleset();
|
9332
|
-
ignore.split(",").forEach(function(value){
|
9333
|
-
ruleset[value] = 0;
|
9334
|
-
});
|
9335
|
-
}
|
9336
|
-
|
9337
|
-
return ruleset;
|
9338
|
-
}
|
9339
|
-
|
9340
|
-
|
9341
|
-
/**
|
9342
|
-
* Filters out files using the exclude-list command line option.
|
9343
|
-
* @param files {Array} the list of files to check for exclusions
|
9344
|
-
* @param options {Object} the CLI options
|
9345
|
-
* @return {Array} A list of files
|
9346
|
-
*/
|
9347
|
-
function filterFiles(files, options) {
|
9348
|
-
var excludeList = options["exclude-list"],
|
9349
|
-
excludeFiles = [],
|
9350
|
-
filesToLint = files.map(api.getFullPath),
|
9351
|
-
fullPath;
|
9352
|
-
|
9353
|
-
|
9354
|
-
if (excludeList) {
|
9355
|
-
// Build up the exclude list, expanding any directory exclusions that were passed in
|
9356
|
-
excludeList.split(",").forEach(function(value){
|
9357
|
-
if (api.isDirectory(value)) {
|
9358
|
-
excludeFiles = excludeFiles.concat(api.getFiles(value));
|
9359
|
-
} else {
|
9360
|
-
excludeFiles.push(value);
|
9361
|
-
}
|
9362
|
-
});
|
9363
|
-
|
9364
|
-
// Remove the excluded files from the list of files to lint
|
9365
|
-
excludeFiles.forEach(function(value){
|
9366
|
-
fullPath = api.getFullPath(value);
|
9367
|
-
if (filesToLint.indexOf(fullPath) > -1) {
|
9368
|
-
filesToLint.splice(filesToLint.indexOf(fullPath),1);
|
9369
|
-
}
|
9370
|
-
});
|
9371
|
-
}
|
9372
|
-
|
9373
|
-
return filesToLint;
|
9374
|
-
}
|
9375
|
-
|
9376
|
-
/**
|
9377
|
-
* Outputs all available rules to the CLI.
|
9378
|
-
* @return {void}
|
9379
|
-
*/
|
9380
|
-
function printRules(){
|
9381
|
-
api.print("");
|
9382
|
-
var rules = CSSLint.getRules();
|
9383
|
-
rules.forEach(function(rule){
|
9384
|
-
api.print(rule.id + "\n " + rule.desc + "\n");
|
9385
|
-
});
|
9386
|
-
}
|
9387
|
-
|
9388
|
-
/**
|
9389
|
-
* Given a file name and options, run verification and print formatted output.
|
9390
|
-
* @param {String} relativeFilePath absolute file location
|
9391
|
-
* @param {Object} options for processing
|
9392
|
-
* @return {Number} exit code
|
9393
|
-
*/
|
9394
|
-
function processFile(relativeFilePath, options) {
|
9395
|
-
var input = api.readFile(relativeFilePath),
|
9396
|
-
ruleset = filterRules(options),
|
9397
|
-
result = CSSLint.verify(input, gatherRules(options, ruleset)),
|
9398
|
-
formatter = CSSLint.getFormatter(options.format || "text"),
|
9399
|
-
messages = result.messages || [],
|
9400
|
-
output,
|
9401
|
-
exitCode = 0;
|
9402
|
-
|
9403
|
-
if (!input) {
|
9404
|
-
if (formatter.readError) {
|
9405
|
-
api.print(formatter.readError(relativeFilePath, "Could not read file data. Is the file empty?"));
|
9406
|
-
} else {
|
9407
|
-
api.print("csslint: Could not read file data in " + relativeFilePath + ". Is the file empty?");
|
9408
|
-
}
|
9409
|
-
exitCode = 1;
|
9410
|
-
} else {
|
9411
|
-
//var relativeFilePath = getRelativePath(api.getWorkingDirectory(), fullFilePath);
|
9412
|
-
options.fullPath = api.getFullPath(relativeFilePath);
|
9413
|
-
output = formatter.formatResults(result, relativeFilePath, options);
|
9414
|
-
if (output){
|
9415
|
-
api.print(output);
|
9416
|
-
}
|
9417
|
-
|
9418
|
-
if (messages.length > 0 && pluckByType(messages, "error").length > 0) {
|
9419
|
-
exitCode = 1;
|
9420
|
-
}
|
9421
|
-
}
|
9422
|
-
|
9423
|
-
return exitCode;
|
9424
|
-
}
|
9425
|
-
|
9426
|
-
|
9427
|
-
/**
|
9428
|
-
* Outputs the help screen to the CLI.
|
9429
|
-
* @return {void}
|
9430
|
-
*/
|
9431
|
-
function outputHelp(){
|
9432
|
-
var lenToPad = 40,
|
9433
|
-
toPrint = '',
|
9434
|
-
formatString = '';
|
9435
|
-
|
9436
|
-
api.print([
|
9437
|
-
"\nUsage: csslint-rhino.js [options]* [file|dir]*",
|
9438
|
-
" ",
|
9439
|
-
"Global Options"
|
9440
|
-
].join("\n"));
|
9441
|
-
|
9442
|
-
for (var optionName in globalOptions) {
|
9443
|
-
if (globalOptions.hasOwnProperty(optionName)) {
|
9444
|
-
// Print the option name and the format if present
|
9445
|
-
toPrint += " --" + optionName;
|
9446
|
-
if (globalOptions[optionName].format !== "") {
|
9447
|
-
formatString = '=' + globalOptions[optionName].format;
|
9448
|
-
toPrint += formatString;
|
9449
|
-
} else {
|
9450
|
-
formatString = '';
|
9451
|
-
}
|
9452
|
-
|
9453
|
-
// Pad out with the appropriate number of spaces
|
9454
|
-
toPrint += new Array(lenToPad - (optionName.length + formatString.length)).join(' ');
|
9455
|
-
|
9456
|
-
// Print the description
|
9457
|
-
toPrint += globalOptions[optionName].description + "\n";
|
9458
|
-
}
|
9459
|
-
}
|
9460
|
-
api.print(toPrint);
|
9461
|
-
}
|
9462
|
-
|
9463
|
-
/**
|
9464
|
-
* Given an Array of filenames, print wrapping output and process them.
|
9465
|
-
* @param files {Array} filenames list
|
9466
|
-
* @param options {Object} options object
|
9467
|
-
* @return {Number} exit code
|
9468
|
-
*/
|
9469
|
-
function processFiles(fileArray, options){
|
9470
|
-
var exitCode = 0,
|
9471
|
-
formatId = options.format || "text",
|
9472
|
-
formatter,
|
9473
|
-
files = filterFiles(fileArray,options),
|
9474
|
-
output;
|
9475
|
-
|
9476
|
-
if (!files.length) {
|
9477
|
-
api.print("csslint: No files specified.");
|
9478
|
-
exitCode = 1;
|
9479
|
-
} else {
|
9480
|
-
if (!CSSLint.hasFormat(formatId)){
|
9481
|
-
api.print("csslint: Unknown format '" + formatId + "'. Cannot proceed.");
|
9482
|
-
exitCode = 1;
|
9483
|
-
} else {
|
9484
|
-
formatter = CSSLint.getFormatter(formatId);
|
9485
|
-
|
9486
|
-
output = formatter.startFormat();
|
9487
|
-
if (output){
|
9488
|
-
api.print(output);
|
9489
|
-
}
|
9490
|
-
|
9491
|
-
|
9492
|
-
files.forEach(function(file){
|
9493
|
-
if (exitCode === 0) {
|
9494
|
-
exitCode = processFile(file,options);
|
9495
|
-
} else {
|
9496
|
-
processFile(file,options);
|
9497
|
-
}
|
9498
|
-
});
|
9499
|
-
|
9500
|
-
output = formatter.endFormat();
|
9501
|
-
if (output){
|
9502
|
-
api.print(output);
|
9503
|
-
}
|
9504
|
-
}
|
9505
|
-
}
|
9506
|
-
return exitCode;
|
9507
|
-
}
|
9508
|
-
|
9509
|
-
|
9510
|
-
function processArguments(args, options) {
|
9511
|
-
var arg = args.shift(),
|
9512
|
-
argName,
|
9513
|
-
parts,
|
9514
|
-
files = [];
|
9515
|
-
|
9516
|
-
while(arg){
|
9517
|
-
if (arg.indexOf("--") === 0){
|
9518
|
-
argName = arg.substring(2);
|
9519
|
-
|
9520
|
-
if (argName.indexOf("=") > -1){
|
9521
|
-
parts = argName.split("=");
|
9522
|
-
options[parts[0]] = parts[1];
|
9523
|
-
} else {
|
9524
|
-
options[argName] = true;
|
9525
|
-
}
|
9526
|
-
|
9527
|
-
} else {
|
9528
|
-
|
9529
|
-
//see if it's a directory or a file
|
9530
|
-
if (api.isDirectory(arg)){
|
9531
|
-
files = files.concat(api.getFiles(arg));
|
9532
|
-
} else {
|
9533
|
-
files.push(arg);
|
9534
|
-
}
|
9535
|
-
}
|
9536
|
-
arg = args.shift();
|
9537
|
-
}
|
9538
|
-
|
9539
|
-
options.files = files;
|
9540
|
-
return options;
|
9541
|
-
}
|
9542
|
-
|
9543
|
-
function validateOptions(options) {
|
9544
|
-
for (var option_key in options) {
|
9545
|
-
if (!globalOptions.hasOwnProperty(option_key) && option_key !== 'files') {
|
9546
|
-
api.print(option_key + ' is not a valid option. Exiting...');
|
9547
|
-
outputHelp();
|
9548
|
-
api.quit(0);
|
9549
|
-
}
|
9550
|
-
}
|
9551
|
-
}
|
9552
|
-
|
9553
|
-
function readConfigFile(options) {
|
9554
|
-
var data = api.readFile(api.getFullPath(".csslintrc"));
|
9555
|
-
if (data) {
|
9556
|
-
options = processArguments(data.split(/[\s\n\r]+/m), options);
|
9557
|
-
}
|
9558
|
-
|
9559
|
-
return options;
|
9560
|
-
}
|
9561
|
-
|
9562
|
-
|
9563
|
-
|
9564
|
-
//-----------------------------------------------------------------------------
|
9565
|
-
// Process command line
|
9566
|
-
//-----------------------------------------------------------------------------
|
9567
|
-
|
9568
|
-
var args = api.args,
|
9569
|
-
argCount = args.length,
|
9570
|
-
options = {};
|
9571
|
-
|
9572
|
-
// first look for config file .csslintrc
|
9573
|
-
options = readConfigFile(options);
|
9574
|
-
|
9575
|
-
// Command line arguments override config file
|
9576
|
-
options = processArguments(args, options);
|
9577
|
-
|
9578
|
-
if (options.help || argCount === 0){
|
9579
|
-
outputHelp();
|
9580
|
-
api.quit(0);
|
9581
|
-
}
|
9582
|
-
|
9583
|
-
// Validate options
|
9584
|
-
validateOptions(options);
|
9585
|
-
|
9586
|
-
if (options.version){
|
9587
|
-
api.print("v" + CSSLint.version);
|
9588
|
-
api.quit(0);
|
9589
|
-
}
|
9590
|
-
|
9591
|
-
if (options["list-rules"]){
|
9592
|
-
printRules();
|
9593
|
-
api.quit(0);
|
9594
|
-
}
|
9595
|
-
|
9596
|
-
api.quit(processFiles(options.files,options));
|
9597
|
-
}
|
9598
|
-
|
9599
|
-
/*
|
9600
|
-
* CSSLint Rhino Command Line Interface
|
9601
|
-
*/
|
9602
|
-
/*jshint rhino:true*/
|
9603
|
-
/*global cli, File*/
|
9604
|
-
|
9605
|
-
importPackage(java.io);
|
9606
|
-
|
9607
|
-
cli({
|
9608
|
-
args: Array.prototype.concat.call(arguments),
|
9609
|
-
print: print,
|
9610
|
-
quit: quit,
|
9611
|
-
|
9612
|
-
isDirectory: function(name){
|
9613
|
-
var dir = new File(name);
|
9614
|
-
return dir.isDirectory();
|
9615
|
-
},
|
9616
|
-
|
9617
|
-
getFiles: function(dir){
|
9618
|
-
var files = [];
|
9619
|
-
|
9620
|
-
function traverse(dir) {
|
9621
|
-
var dirList = dir.listFiles();
|
9622
|
-
dirList.forEach(function (file) {
|
9623
|
-
if (/\.css$/.test(file)) {
|
9624
|
-
files.push(file.toString());
|
9625
|
-
} else if (file.isDirectory()) {
|
9626
|
-
traverse(file);
|
9627
|
-
}
|
9628
|
-
});
|
9629
|
-
}
|
9630
|
-
|
9631
|
-
traverse(new File(dir));
|
9632
|
-
|
9633
|
-
return files;
|
9634
|
-
},
|
9635
|
-
|
9636
|
-
getWorkingDirectory: function() {
|
9637
|
-
return (new File(".")).getCanonicalPath();
|
9638
|
-
},
|
9639
|
-
|
9640
|
-
getFullPath: function(filename){
|
9641
|
-
return (new File(filename)).getCanonicalPath();
|
9642
|
-
},
|
9643
|
-
|
9644
|
-
readFile: function(filename) {
|
9645
|
-
try {
|
9646
|
-
return readFile(filename);
|
9647
|
-
} catch (ex) {
|
9648
|
-
return "";
|
9649
|
-
}
|
9650
|
-
}
|
9651
|
-
});
|