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.
@@ -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.3, Build time: 19-June-2013 11:16:15 */
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.3, Build time: 19-June-2013 11:16:15 */
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
- //valueParts = [],
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
- values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col));
2833
- valueParts = [];
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
- //cleanup
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" : "start | end | center | baseline | stretch", //http://www.w3.org/TR/2009/WD-css3-flexbox-20090723/
3733
- "box-decoration-break" : "slice |clone",
3734
- "box-direction" : "normal | reverse | inherit",
3735
- "box-flex" : "<number>",
3736
- "box-flex-group" : "<integer>",
3737
- "box-lines" : "single | multiple",
3738
- "box-ordinal-group" : "<integer>",
3739
- "box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit",
3740
- "box-pack" : "start | end | center | justify",
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 | box | inline-box | 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",
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" : 1,
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
- for (var prop in supplier){
4672
- if (supplier.hasOwnProperty(prop)){
4673
- receiver[prop] = supplier[prop];
4674
- }
4675
- }
4676
- return receiver;
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
- TokenStreamBase.call(this, input, Tokens);
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
- * - &amp; is the escape sequence for &
8793
- * - &lt; is the escape sequence for <
8794
- * - &gt; 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 "&quot;";
8808
- case "&":
8809
- return "&amp;";
8810
- case "<":
8811
- return "&lt;";
8812
- case ">":
8813
- return "&gt;";
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
- * - &amp; is the escape sequence for &
8991
- * - &lt; is the escape sequence for <
8992
- * - &gt; 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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
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
- * - &lt; is the escape sequence for <
9078
- * - &gt; 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, "&lt;").replace(/>/g, "&gt;");
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
- * - &amp; is the escape sequence for &
9164
- * - &lt; is the escape sequence for <
9165
- * - &gt; 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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
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
- });