<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[42110] trunk/src/wp-includes/js/codemirror/csslint.js: Codemirror: Unify line endings in csslint.js</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/42110">42110</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/42110","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>obenland</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2017-11-02 22:30:16 +0000 (Thu, 02 Nov 2017)</dd>
</dl>
<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Codemirror: Unify line endings in csslint.js
This is likely a problem caused upstream and will probably be a non-issue once <a href="https://core.trac.wordpress.org/ticket/41870">#41870</a> is merged in a future release.
Fixes <a href="https://core.trac.wordpress.org/ticket/42424">#42424</a>.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludesjscodemirrorcsslintjs">trunk/src/wp-includes/js/codemirror/csslint.js</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpincludesjscodemirrorcsslintjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/js/codemirror/csslint.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/js/codemirror/csslint.js 2017-11-02 19:59:55 UTC (rev 42109)
+++ trunk/src/wp-includes/js/codemirror/csslint.js 2017-11-02 22:30:16 UTC (rev 42110)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,32 +1,32 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/*!
-CSSLint v1.0.4
-Copyright (c) 2016 Nicole Sullivan and Nicholas C. Zakas. All rights reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the 'Software'), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-*/
-
-var CSSLint = (function(){
- var module = module || {},
- exports = exports || {};
-
-/*!
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/*!
+CSSLint v1.0.4
+Copyright (c) 2016 Nicole Sullivan and Nicholas C. Zakas. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the 'Software'), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+*/
+
+var CSSLint = (function(){
+ var module = module || {},
+ exports = exports || {};
+
+/*!
</ins><span class="cx" style="display: block; padding: 0 10px"> Parser-Lib
</span><span class="cx" style="display: block; padding: 0 10px"> Copyright (c) 2009-2016 Nicholas C. Zakas. All rights reserved.
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -46,12 +46,12 @@
</span><span class="cx" style="display: block; padding: 0 10px"> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
</span><span class="cx" style="display: block; padding: 0 10px"> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
</span><span class="cx" style="display: block; padding: 0 10px"> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-THE SOFTWARE.
-*/
-/* Version v1.1.0, Build time: 6-December-2016 10:31:29 */
-var parserlib = (function () {
-var require;
-require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+THE SOFTWARE.
+*/
+/* Version v1.1.0, Build time: 6-December-2016 10:31:29 */
+var parserlib = (function () {
+var require;
+require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /* exported Colors */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -238,8 +238,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> windowframe :"Window frame.",
</span><span class="cx" style="display: block; padding: 0 10px"> windowtext :"Text in windows."
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{}],2:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{}],2:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = Combinator;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -285,8 +285,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> Combinator.prototype = new SyntaxUnit();
</span><span class="cx" style="display: block; padding: 0 10px"> Combinator.prototype.constructor = Combinator;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"../util/SyntaxUnit":26,"./Parser":6}],3:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"../util/SyntaxUnit":26,"./Parser":6}],3:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = Matcher;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -642,8 +642,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> });
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"../util/StringReader":24,"../util/SyntaxError":25,"./ValidationTypes":21}],4:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"../util/StringReader":24,"../util/SyntaxError":25,"./ValidationTypes":21}],4:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = MediaFeature;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -683,8 +683,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> MediaFeature.prototype = new SyntaxUnit();
</span><span class="cx" style="display: block; padding: 0 10px"> MediaFeature.prototype.constructor = MediaFeature;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"../util/SyntaxUnit":26,"./Parser":6}],5:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"../util/SyntaxUnit":26,"./Parser":6}],5:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = MediaQuery;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -735,8 +735,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> MediaQuery.prototype = new SyntaxUnit();
</span><span class="cx" style="display: block; padding: 0 10px"> MediaQuery.prototype.constructor = MediaQuery;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"../util/SyntaxUnit":26,"./Parser":6}],6:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"../util/SyntaxUnit":26,"./Parser":6}],6:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = Parser;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3289,8 +3289,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> ['-'|'+']? INTEGER | {O}{D}{D} | {E}{V}{E}{N} ] S*
</span><span class="cx" style="display: block; padding: 0 10px"> ;
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"../util/EventTarget":23,"../util/SyntaxError":25,"../util/SyntaxUnit":26,"./Combinator":2,"./MediaFeature":4,"./MediaQuery":5,"./PropertyName":8,"./PropertyValue":9,"./PropertyValuePart":11,"./Selector":13,"./SelectorPart":14,"./SelectorSubPart":15,"./TokenStream":17,"./Tokens":18,"./Validation":19}],7:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"../util/EventTarget":23,"../util/SyntaxError":25,"../util/SyntaxUnit":26,"./Combinator":2,"./MediaFeature":4,"./MediaQuery":5,"./PropertyName":8,"./PropertyValue":9,"./PropertyValuePart":11,"./Selector":13,"./SelectorPart":14,"./SelectorSubPart":15,"./TokenStream":17,"./Tokens":18,"./Validation":19}],7:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /* exported Properties */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3783,8 +3783,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> "z-index" : "<integer> | auto",
</span><span class="cx" style="display: block; padding: 0 10px"> "zoom" : "<number> | <percentage> | normal"
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{}],8:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{}],8:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = PropertyName;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3822,8 +3822,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> PropertyName.prototype.toString = function() {
</span><span class="cx" style="display: block; padding: 0 10px"> return (this.hack ? this.hack : "") + this.text;
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"../util/SyntaxUnit":26,"./Parser":6}],9:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"../util/SyntaxUnit":26,"./Parser":6}],9:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = PropertyValue;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3860,8 +3860,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> PropertyValue.prototype = new SyntaxUnit();
</span><span class="cx" style="display: block; padding: 0 10px"> PropertyValue.prototype.constructor = PropertyValue;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"../util/SyntaxUnit":26,"./Parser":6}],10:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"../util/SyntaxUnit":26,"./Parser":6}],10:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = PropertyValueIterator;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3998,8 +3998,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> PropertyValueIterator.prototype.drop = function() {
</span><span class="cx" style="display: block; padding: 0 10px"> this._marks.pop();
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{}],11:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{}],11:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = PropertyValuePart;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4247,8 +4247,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> });
</span><span class="cx" style="display: block; padding: 0 10px"> return part;
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"../util/SyntaxUnit":26,"./Colors":1,"./Parser":6,"./Tokens":18}],12:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"../util/SyntaxUnit":26,"./Colors":1,"./Parser":6,"./Tokens":18}],12:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> var Pseudos = module.exports = {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4265,8 +4265,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> Pseudos.isElement = function(pseudo) {
</span><span class="cx" style="display: block; padding: 0 10px"> return pseudo.indexOf("::") === 0 || Pseudos[pseudo.toLowerCase()] === Pseudos.ELEMENT;
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{}],13:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{}],13:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = Selector;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4310,8 +4310,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> Selector.prototype = new SyntaxUnit();
</span><span class="cx" style="display: block; padding: 0 10px"> Selector.prototype.constructor = Selector;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"../util/SyntaxUnit":26,"./Parser":6,"./Specificity":16}],14:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"../util/SyntaxUnit":26,"./Parser":6,"./Specificity":16}],14:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = SelectorPart;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4361,8 +4361,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> SelectorPart.prototype = new SyntaxUnit();
</span><span class="cx" style="display: block; padding: 0 10px"> SelectorPart.prototype.constructor = SelectorPart;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"../util/SyntaxUnit":26,"./Parser":6}],15:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"../util/SyntaxUnit":26,"./Parser":6}],15:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = SelectorSubPart;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4406,8 +4406,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> SelectorSubPart.prototype = new SyntaxUnit();
</span><span class="cx" style="display: block; padding: 0 10px"> SelectorSubPart.prototype.constructor = SelectorSubPart;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"../util/SyntaxUnit":26,"./Parser":6}],16:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"../util/SyntaxUnit":26,"./Parser":6}],16:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = Specificity;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4537,8 +4537,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> return new Specificity(0, b, c, d);
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"./Pseudos":12,"./SelectorPart":14}],17:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"./Pseudos":12,"./SelectorPart":14}],17:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = TokenStream;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5586,8 +5586,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> });
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"../util/TokenStreamBase":27,"./PropertyValuePart":11,"./Tokens":18}],18:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"../util/TokenStreamBase":27,"./PropertyValuePart":11,"./Tokens":18}],18:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> var Tokens = module.exports = [
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5797,8 +5797,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> return typeMap[c] || -1;
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><span class="cx" style="display: block; padding: 0 10px"> })();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{}],19:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{}],19:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> /* exported Validation */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5865,8 +5865,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"./Matcher":3,"./Properties":7,"./PropertyValueIterator":10,"./ValidationError":20,"./ValidationTypes":21}],20:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"./Matcher":3,"./Properties":7,"./PropertyValueIterator":10,"./ValidationError":20,"./ValidationTypes":21}],20:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = ValidationError;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5907,8 +5907,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> //inherit from Error
</span><span class="cx" style="display: block; padding: 0 10px"> ValidationError.prototype = new Error();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{}],21:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{}],21:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> var ValidationTypes = module.exports;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -6386,8 +6386,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> "<font-variant-caps>",
</span><span class="cx" style="display: block; padding: 0 10px"> { expand: "<font-variant-numeric>" },
</span><span class="cx" style="display: block; padding: 0 10px"> { expand: "<font-variant-east-asian>" });
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"./Matcher":3}],22:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"./Matcher":3}],22:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -6408,8 +6408,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> Tokens : require("./Tokens"),
</span><span class="cx" style="display: block; padding: 0 10px"> ValidationError : require("./ValidationError")
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"./Colors":1,"./Combinator":2,"./Matcher":3,"./MediaFeature":4,"./MediaQuery":5,"./Parser":6,"./PropertyName":8,"./PropertyValue":9,"./PropertyValuePart":11,"./Selector":13,"./SelectorPart":14,"./SelectorSubPart":15,"./Specificity":16,"./TokenStream":17,"./Tokens":18,"./ValidationError":20}],23:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"./Colors":1,"./Combinator":2,"./Matcher":3,"./MediaFeature":4,"./MediaQuery":5,"./Parser":6,"./PropertyName":8,"./PropertyValue":9,"./PropertyValuePart":11,"./Selector":13,"./SelectorPart":14,"./SelectorSubPart":15,"./Specificity":16,"./TokenStream":17,"./Tokens":18,"./ValidationError":20}],23:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = EventTarget;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -6501,8 +6501,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{}],24:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{}],24:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = StringReader;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -6774,8 +6774,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{}],25:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{}],25:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = SyntaxError;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -6819,8 +6819,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> //inherit from Error
</span><span class="cx" style="display: block; padding: 0 10px"> SyntaxError.prototype = Object.create(Error.prototype); // jshint ignore:line
</span><span class="cx" style="display: block; padding: 0 10px"> SyntaxError.prototype.constructor = SyntaxError; // jshint ignore:line
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{}],26:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{}],26:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = SyntaxUnit;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -6903,8 +6903,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{}],27:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{}],27:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = TokenStreamBase;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -7305,8 +7305,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"./StringReader":24,"./SyntaxError":25}],28:[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"./StringReader":24,"./SyntaxError":25}],28:[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -7316,3544 +7316,3544 @@
</span><span class="cx" style="display: block; padding: 0 10px"> EventTarget : require("./EventTarget"),
</span><span class="cx" style="display: block; padding: 0 10px"> TokenStreamBase : require("./TokenStreamBase")
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"./EventTarget":23,"./StringReader":24,"./SyntaxError":25,"./SyntaxUnit":26,"./TokenStreamBase":27}],"parserlib":[function(require,module,exports){
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"./EventTarget":23,"./StringReader":24,"./SyntaxError":25,"./SyntaxUnit":26,"./TokenStreamBase":27}],"parserlib":[function(require,module,exports){
</ins><span class="cx" style="display: block; padding: 0 10px"> "use strict";
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> module.exports = {
</span><span class="cx" style="display: block; padding: 0 10px"> css : require("./css"),
</span><span class="cx" style="display: block; padding: 0 10px"> util : require("./util")
</span><span class="cx" style="display: block; padding: 0 10px"> };
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-},{"./css":22,"./util":28}]},{},[]);
-
-return require('parserlib');
-})();
-var clone = (function() {
-'use strict';
-
-var nativeMap;
-try {
- nativeMap = Map;
-} catch(_) {
- // maybe a reference error because no `Map`. Give it a dummy value that no
- // value will ever be an instanceof.
- nativeMap = function() {};
-}
-
-var nativeSet;
-try {
- nativeSet = Set;
-} catch(_) {
- nativeSet = function() {};
-}
-
-var nativePromise;
-try {
- nativePromise = Promise;
-} catch(_) {
- nativePromise = function() {};
-}
-
-/**
- * Clones (copies) an Object using deep copying.
- *
- * This function supports circular references by default, but if you are certain
- * there are no circular references in your object, you can save some CPU time
- * by calling clone(obj, false).
- *
- * Caution: if `circular` is false and `parent` contains circular references,
- * your program may enter an infinite loop and crash.
- *
- * @param `parent` - the object to be cloned
- * @param `circular` - set to true if the object to be cloned may contain
- * circular references. (optional - true by default)
- * @param `depth` - set to a number if the object is only to be cloned to
- * a particular depth. (optional - defaults to Infinity)
- * @param `prototype` - sets the prototype to be used when cloning an object.
- * (optional - defaults to parent prototype).
- * @param `includeNonEnumerable` - set to true if the non-enumerable properties
- * should be cloned as well. Non-enumerable properties on the prototype
- * chain will be ignored. (optional - false by default)
-*/
-function clone(parent, circular, depth, prototype, includeNonEnumerable) {
- if (typeof circular === 'object') {
- depth = circular.depth;
- prototype = circular.prototype;
- includeNonEnumerable = circular.includeNonEnumerable;
- circular = circular.circular;
- }
- // maintain two arrays for circular references, where corresponding parents
- // and children have the same index
- var allParents = [];
- var allChildren = [];
-
- var useBuffer = typeof Buffer != 'undefined';
-
- if (typeof circular == 'undefined')
- circular = true;
-
- if (typeof depth == 'undefined')
- depth = Infinity;
-
- // recurse this function so we don't reset allParents and allChildren
- function _clone(parent, depth) {
- // cloning null always returns null
- if (parent === null)
- return null;
-
- if (depth === 0)
- return parent;
-
- var child;
- var proto;
- if (typeof parent != 'object') {
- return parent;
- }
-
- if (parent instanceof nativeMap) {
- child = new nativeMap();
- } else if (parent instanceof nativeSet) {
- child = new nativeSet();
- } else if (parent instanceof nativePromise) {
- child = new nativePromise(function (resolve, reject) {
- parent.then(function(value) {
- resolve(_clone(value, depth - 1));
- }, function(err) {
- reject(_clone(err, depth - 1));
- });
- });
- } else if (clone.__isArray(parent)) {
- child = [];
- } else if (clone.__isRegExp(parent)) {
- child = new RegExp(parent.source, __getRegExpFlags(parent));
- if (parent.lastIndex) child.lastIndex = parent.lastIndex;
- } else if (clone.__isDate(parent)) {
- child = new Date(parent.getTime());
- } else if (useBuffer && Buffer.isBuffer(parent)) {
- child = new Buffer(parent.length);
- parent.copy(child);
- return child;
- } else if (parent instanceof Error) {
- child = Object.create(parent);
- } else {
- if (typeof prototype == 'undefined') {
- proto = Object.getPrototypeOf(parent);
- child = Object.create(proto);
- }
- else {
- child = Object.create(prototype);
- proto = prototype;
- }
- }
-
- if (circular) {
- var index = allParents.indexOf(parent);
-
- if (index != -1) {
- return allChildren[index];
- }
- allParents.push(parent);
- allChildren.push(child);
- }
-
- if (parent instanceof nativeMap) {
- var keyIterator = parent.keys();
- while(true) {
- var next = keyIterator.next();
- if (next.done) {
- break;
- }
- var keyChild = _clone(next.value, depth - 1);
- var valueChild = _clone(parent.get(next.value), depth - 1);
- child.set(keyChild, valueChild);
- }
- }
- if (parent instanceof nativeSet) {
- var iterator = parent.keys();
- while(true) {
- var next = iterator.next();
- if (next.done) {
- break;
- }
- var entryChild = _clone(next.value, depth - 1);
- child.add(entryChild);
- }
- }
-
- for (var i in parent) {
- var attrs;
- if (proto) {
- attrs = Object.getOwnPropertyDescriptor(proto, i);
- }
-
- if (attrs && attrs.set == null) {
- continue;
- }
- child[i] = _clone(parent[i], depth - 1);
- }
-
- if (Object.getOwnPropertySymbols) {
- var symbols = Object.getOwnPropertySymbols(parent);
- for (var i = 0; i < symbols.length; i++) {
- // Don't need to worry about cloning a symbol because it is a primitive,
- // like a number or string.
- var symbol = symbols[i];
- var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
- if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
- continue;
- }
- child[symbol] = _clone(parent[symbol], depth - 1);
- if (!descriptor.enumerable) {
- Object.defineProperty(child, symbol, {
- enumerable: false
- });
- }
- }
- }
-
- if (includeNonEnumerable) {
- var allPropertyNames = Object.getOwnPropertyNames(parent);
- for (var i = 0; i < allPropertyNames.length; i++) {
- var propertyName = allPropertyNames[i];
- var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
- if (descriptor && descriptor.enumerable) {
- continue;
- }
- child[propertyName] = _clone(parent[propertyName], depth - 1);
- Object.defineProperty(child, propertyName, {
- enumerable: false
- });
- }
- }
-
- return child;
- }
-
- return _clone(parent, depth);
-}
-
-/**
- * Simple flat clone using prototype, accepts only objects, usefull for property
- * override on FLAT configuration object (no nested props).
- *
- * USE WITH CAUTION! This may not behave as you wish if you do not know how this
- * works.
- */
-clone.clonePrototype = function clonePrototype(parent) {
- if (parent === null)
- return null;
-
- var c = function () {};
- c.prototype = parent;
- return new c();
-};
-
-// private utility functions
-
-function __objToStr(o) {
- return Object.prototype.toString.call(o);
-}
-clone.__objToStr = __objToStr;
-
-function __isDate(o) {
- return typeof o === 'object' && __objToStr(o) === '[object Date]';
-}
-clone.__isDate = __isDate;
-
-function __isArray(o) {
- return typeof o === 'object' && __objToStr(o) === '[object Array]';
-}
-clone.__isArray = __isArray;
-
-function __isRegExp(o) {
- return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
-}
-clone.__isRegExp = __isRegExp;
-
-function __getRegExpFlags(re) {
- var flags = '';
- if (re.global) flags += 'g';
- if (re.ignoreCase) flags += 'i';
- if (re.multiline) flags += 'm';
- return flags;
-}
-clone.__getRegExpFlags = __getRegExpFlags;
-
-return clone;
-})();
-
-if (typeof module === 'object' && module.exports) {
- module.exports = clone;
-}
-
-/**
- * Main CSSLint object.
- * @class CSSLint
- * @static
- * @extends parserlib.util.EventTarget
- */
-
-/* global parserlib, clone, Reporter */
-/* exported CSSLint */
-
-var CSSLint = (function() {
- "use strict";
-
- var rules = [],
- formatters = [],
- embeddedRuleset = /\/\*\s*csslint([^\*]*)\*\//,
- api = new parserlib.util.EventTarget();
-
- api.version = "1.0.4";
-
- //-------------------------------------------------------------------------
- // Rule Management
- //-------------------------------------------------------------------------
-
- /**
- * Adds a new rule to the engine.
- * @param {Object} rule The rule to add.
- * @method addRule
- */
- api.addRule = function(rule) {
- rules.push(rule);
- rules[rule.id] = rule;
- };
-
- /**
- * Clears all rule from the engine.
- * @method clearRules
- */
- api.clearRules = function() {
- rules = [];
- };
-
- /**
- * Returns the rule objects.
- * @return An array of rule objects.
- * @method getRules
- */
- api.getRules = function() {
- return [].concat(rules).sort(function(a, b) {
- return a.id > b.id ? 1 : 0;
- });
- };
-
- /**
- * Returns a ruleset configuration object with all current rules.
- * @return A ruleset object.
- * @method getRuleset
- */
- api.getRuleset = function() {
- var ruleset = {},
- i = 0,
- len = rules.length;
-
- while (i < len) {
- ruleset[rules[i++].id] = 1; // by default, everything is a warning
- }
-
- return ruleset;
- };
-
- /**
- * Returns a ruleset object based on embedded rules.
- * @param {String} text A string of css containing embedded rules.
- * @param {Object} ruleset A ruleset object to modify.
- * @return {Object} A ruleset object.
- * @method getEmbeddedRuleset
- */
- function applyEmbeddedRuleset(text, ruleset) {
- var valueMap,
- embedded = text && text.match(embeddedRuleset),
- rules = embedded && embedded[1];
-
- if (rules) {
- valueMap = {
- "true": 2, // true is error
- "": 1, // blank is warning
- "false": 0, // false is ignore
-
- "2": 2, // explicit error
- "1": 1, // explicit warning
- "0": 0 // explicit ignore
- };
-
- rules.toLowerCase().split(",").forEach(function(rule) {
- var pair = rule.split(":"),
- property = pair[0] || "",
- value = pair[1] || "";
-
- ruleset[property.trim()] = valueMap[value.trim()];
- });
- }
-
- return ruleset;
- }
-
- //-------------------------------------------------------------------------
- // Formatters
- //-------------------------------------------------------------------------
-
- /**
- * Adds a new formatter to the engine.
- * @param {Object} formatter The formatter to add.
- * @method addFormatter
- */
- api.addFormatter = function(formatter) {
- // formatters.push(formatter);
- formatters[formatter.id] = formatter;
- };
-
- /**
- * Retrieves a formatter for use.
- * @param {String} formatId The name of the format to retrieve.
- * @return {Object} The formatter or undefined.
- * @method getFormatter
- */
- api.getFormatter = function(formatId) {
- return formatters[formatId];
- };
-
- /**
- * Formats the results in a particular format for a single file.
- * @param {Object} result The results returned from CSSLint.verify().
- * @param {String} filename The filename for which the results apply.
- * @param {String} formatId The name of the formatter to use.
- * @param {Object} options (Optional) for special output handling.
- * @return {String} A formatted string for the results.
- * @method format
- */
- api.format = function(results, filename, formatId, options) {
- var formatter = this.getFormatter(formatId),
- result = null;
-
- if (formatter) {
- result = formatter.startFormat();
- result += formatter.formatResults(results, filename, options || {});
- result += formatter.endFormat();
- }
-
- return result;
- };
-
- /**
- * Indicates if the given format is supported.
- * @param {String} formatId The ID of the format to check.
- * @return {Boolean} True if the format exists, false if not.
- * @method hasFormat
- */
- api.hasFormat = function(formatId) {
- return formatters.hasOwnProperty(formatId);
- };
-
- //-------------------------------------------------------------------------
- // Verification
- //-------------------------------------------------------------------------
-
- /**
- * Starts the verification process for the given CSS text.
- * @param {String} text The CSS text to verify.
- * @param {Object} ruleset (Optional) List of rules to apply. If null, then
- * all rules are used. If a rule has a value of 1 then it's a warning,
- * a value of 2 means it's an error.
- * @return {Object} Results of the verification.
- * @method verify
- */
- api.verify = function(text, ruleset) {
-
- var i = 0,
- reporter,
- lines,
- allow = {},
- ignore = [],
- report,
- parser = new parserlib.css.Parser({
- starHack: true,
- ieFilters: true,
- underscoreHack: true,
- strict: false
- });
-
- // normalize line endings
- lines = text.replace(/\n\r?/g, "$split$").split("$split$");
-
- // find 'allow' comments
- CSSLint.Util.forEach(lines, function (line, lineno) {
- var allowLine = line && line.match(/\/\*[ \t]*csslint[ \t]+allow:[ \t]*([^\*]*)\*\//i),
- allowRules = allowLine && allowLine[1],
- allowRuleset = {};
-
- if (allowRules) {
- allowRules.toLowerCase().split(",").forEach(function(allowRule) {
- allowRuleset[allowRule.trim()] = true;
- });
- if (Object.keys(allowRuleset).length > 0) {
- allow[lineno + 1] = allowRuleset;
- }
- }
- });
-
- var ignoreStart = null,
- ignoreEnd = null;
- CSSLint.Util.forEach(lines, function (line, lineno) {
- // Keep oldest, "unclosest" ignore:start
- if (ignoreStart === null && line.match(/\/\*[ \t]*csslint[ \t]+ignore:start[ \t]*\*\//i)) {
- ignoreStart = lineno;
- }
-
- if (line.match(/\/\*[ \t]*csslint[ \t]+ignore:end[ \t]*\*\//i)) {
- ignoreEnd = lineno;
- }
-
- if (ignoreStart !== null && ignoreEnd !== null) {
- ignore.push([ignoreStart, ignoreEnd]);
- ignoreStart = ignoreEnd = null;
- }
- });
-
- // Close remaining ignore block, if any
- if (ignoreStart !== null) {
- ignore.push([ignoreStart, lines.length]);
- }
-
- if (!ruleset) {
- ruleset = this.getRuleset();
- }
-
- if (embeddedRuleset.test(text)) {
- // defensively copy so that caller's version does not get modified
- ruleset = clone(ruleset);
- ruleset = applyEmbeddedRuleset(text, ruleset);
- }
-
- reporter = new Reporter(lines, ruleset, allow, ignore);
-
- ruleset.errors = 2; // always report parsing errors as errors
- for (i in ruleset) {
- if (ruleset.hasOwnProperty(i) && ruleset[i]) {
- if (rules[i]) {
- rules[i].init(parser, reporter);
- }
- }
- }
-
-
- // capture most horrible error type
- try {
- parser.parse(text);
- } catch (ex) {
- reporter.error("Fatal error, cannot continue: " + ex.message, ex.line, ex.col, {});
- }
-
- report = {
- messages : reporter.messages,
- stats : reporter.stats,
- ruleset : reporter.ruleset,
- allow : reporter.allow,
- ignore : reporter.ignore
- };
-
- // sort by line numbers, rollups at the bottom
- report.messages.sort(function (a, b) {
- if (a.rollup && !b.rollup) {
- return 1;
- } else if (!a.rollup && b.rollup) {
- return -1;
- } else {
- return a.line - b.line;
- }
- });
-
- return report;
- };
-
- //-------------------------------------------------------------------------
- // Publish the API
- //-------------------------------------------------------------------------
-
- return api;
-
-})();
-
-/**
- * An instance of Report is used to report results of the
- * verification back to the main API.
- * @class Reporter
- * @constructor
- * @param {String[]} lines The text lines of the source.
- * @param {Object} ruleset The set of rules to work with, including if
- * they are errors or warnings.
- * @param {Object} explicitly allowed lines
- * @param {[][]} ingore list of line ranges to be ignored
- */
-function Reporter(lines, ruleset, allow, ignore) {
- "use strict";
-
- /**
- * List of messages being reported.
- * @property messages
- * @type String[]
- */
- this.messages = [];
-
- /**
- * List of statistics being reported.
- * @property stats
- * @type String[]
- */
- this.stats = [];
-
- /**
- * Lines of code being reported on. Used to provide contextual information
- * for messages.
- * @property lines
- * @type String[]
- */
- this.lines = lines;
-
- /**
- * Information about the rules. Used to determine whether an issue is an
- * error or warning.
- * @property ruleset
- * @type Object
- */
- this.ruleset = ruleset;
-
- /**
- * Lines with specific rule messages to leave out of the report.
- * @property allow
- * @type Object
- */
- this.allow = allow;
- if (!this.allow) {
- this.allow = {};
- }
-
- /**
- * Linesets not to include in the report.
- * @property ignore
- * @type [][]
- */
- this.ignore = ignore;
- if (!this.ignore) {
- this.ignore = [];
- }
-}
-
-Reporter.prototype = {
-
- // restore constructor
- constructor: Reporter,
-
- /**
- * Report an error.
- * @param {String} message The message to store.
- * @param {int} line The line number.
- * @param {int} col The column number.
- * @param {Object} rule The rule this message relates to.
- * @method error
- */
- error: function(message, line, col, rule) {
- "use strict";
- this.messages.push({
- type : "error",
- line : line,
- col : col,
- message : message,
- evidence: this.lines[line-1],
- rule : rule || {}
- });
- },
-
- /**
- * Report an warning.
- * @param {String} message The message to store.
- * @param {int} line The line number.
- * @param {int} col The column number.
- * @param {Object} rule The rule this message relates to.
- * @method warn
- * @deprecated Use report instead.
- */
- warn: function(message, line, col, rule) {
- "use strict";
- this.report(message, line, col, rule);
- },
-
- /**
- * Report an issue.
- * @param {String} message The message to store.
- * @param {int} line The line number.
- * @param {int} col The column number.
- * @param {Object} rule The rule this message relates to.
- * @method report
- */
- report: function(message, line, col, rule) {
- "use strict";
-
- // Check if rule violation should be allowed
- if (this.allow.hasOwnProperty(line) && this.allow[line].hasOwnProperty(rule.id)) {
- return;
- }
-
- var ignore = false;
- CSSLint.Util.forEach(this.ignore, function (range) {
- if (range[0] <= line && line <= range[1]) {
- ignore = true;
- }
- });
- if (ignore) {
- return;
- }
-
- this.messages.push({
- type : this.ruleset[rule.id] === 2 ? "error" : "warning",
- line : line,
- col : col,
- message : message,
- evidence: this.lines[line-1],
- rule : rule
- });
- },
-
- /**
- * Report some informational text.
- * @param {String} message The message to store.
- * @param {int} line The line number.
- * @param {int} col The column number.
- * @param {Object} rule The rule this message relates to.
- * @method info
- */
- info: function(message, line, col, rule) {
- "use strict";
- this.messages.push({
- type : "info",
- line : line,
- col : col,
- message : message,
- evidence: this.lines[line-1],
- rule : rule
- });
- },
-
- /**
- * Report some rollup error information.
- * @param {String} message The message to store.
- * @param {Object} rule The rule this message relates to.
- * @method rollupError
- */
- rollupError: function(message, rule) {
- "use strict";
- this.messages.push({
- type : "error",
- rollup : true,
- message : message,
- rule : rule
- });
- },
-
- /**
- * Report some rollup warning information.
- * @param {String} message The message to store.
- * @param {Object} rule The rule this message relates to.
- * @method rollupWarn
- */
- rollupWarn: function(message, rule) {
- "use strict";
- this.messages.push({
- type : "warning",
- rollup : true,
- message : message,
- rule : rule
- });
- },
-
- /**
- * Report a statistic.
- * @param {String} name The name of the stat to store.
- * @param {Variant} value The value of the stat.
- * @method stat
- */
- stat: function(name, value) {
- "use strict";
- this.stats[name] = value;
- }
-};
-
-// expose for testing purposes
-CSSLint._Reporter = Reporter;
-
-/*
- * Utility functions that make life easier.
- */
-CSSLint.Util = {
- /*
- * Adds all properties from supplier onto receiver,
- * overwriting if the same name already exists on
- * receiver.
- * @param {Object} The object to receive the properties.
- * @param {Object} The object to provide the properties.
- * @return {Object} The receiver
- */
- mix: function(receiver, supplier) {
- "use strict";
- var prop;
-
- for (prop in supplier) {
- if (supplier.hasOwnProperty(prop)) {
- receiver[prop] = supplier[prop];
- }
- }
-
- return prop;
- },
-
- /*
- * Polyfill for array indexOf() method.
- * @param {Array} values The array to search.
- * @param {Variant} value The value to search for.
- * @return {int} The index of the value if found, -1 if not.
- */
- indexOf: function(values, value) {
- "use strict";
- if (values.indexOf) {
- return values.indexOf(value);
- } else {
- for (var i=0, len=values.length; i < len; i++) {
- if (values[i] === value) {
- return i;
- }
- }
- return -1;
- }
- },
-
- /*
- * Polyfill for array forEach() method.
- * @param {Array} values The array to operate on.
- * @param {Function} func The function to call on each item.
- * @return {void}
- */
- forEach: function(values, func) {
- "use strict";
- if (values.forEach) {
- return values.forEach(func);
- } else {
- for (var i=0, len=values.length; i < len; i++) {
- func(values[i], i, values);
- }
- }
- }
-};
-
-/*
- * Rule: Don't use adjoining classes (.foo.bar).
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "adjoining-classes",
- name: "Disallow adjoining classes",
- desc: "Don't use adjoining classes.",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-adjoining-classes",
- browsers: "IE6",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this;
- parser.addListener("startrule", function(event) {
- var selectors = event.selectors,
- selector,
- part,
- modifier,
- classCount,
- i, j, k;
-
- for (i=0; i < selectors.length; i++) {
- selector = selectors[i];
- for (j=0; j < selector.parts.length; j++) {
- part = selector.parts[j];
- if (part.type === parser.SELECTOR_PART_TYPE) {
- classCount = 0;
- for (k=0; k < part.modifiers.length; k++) {
- modifier = part.modifiers[k];
- if (modifier.type === "class") {
- classCount++;
- }
- if (classCount > 1){
- reporter.report("Adjoining classes: "+selectors[i].text, part.line, part.col, rule);
- }
- }
- }
- }
- }
- });
- }
-
-});
-
-/*
- * Rule: Don't use width or height when using padding or border.
- */
-CSSLint.addRule({
-
- // rule information
- id: "box-model",
- name: "Beware of broken box size",
- desc: "Don't use width or height when using padding or border.",
- url: "https://github.com/CSSLint/csslint/wiki/Beware-of-box-model-size",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this,
- widthProperties = {
- border: 1,
- "border-left": 1,
- "border-right": 1,
- padding: 1,
- "padding-left": 1,
- "padding-right": 1
- },
- heightProperties = {
- border: 1,
- "border-bottom": 1,
- "border-top": 1,
- padding: 1,
- "padding-bottom": 1,
- "padding-top": 1
- },
- properties,
- boxSizing = false;
-
- function startRule() {
- properties = {};
- boxSizing = false;
- }
-
- function endRule() {
- var prop, value;
-
- if (!boxSizing) {
- if (properties.height) {
- for (prop in heightProperties) {
- if (heightProperties.hasOwnProperty(prop) && properties[prop]) {
- value = properties[prop].value;
- // special case for padding
- if (!(prop === "padding" && value.parts.length === 2 && value.parts[0].value === 0)) {
- reporter.report("Using height with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
- }
- }
- }
- }
-
- if (properties.width) {
- for (prop in widthProperties) {
- if (widthProperties.hasOwnProperty(prop) && properties[prop]) {
- value = properties[prop].value;
-
- if (!(prop === "padding" && value.parts.length === 2 && value.parts[1].value === 0)) {
- reporter.report("Using width with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
- }
- }
- }
- }
- }
- }
-
- parser.addListener("startrule", startRule);
- parser.addListener("startfontface", startRule);
- parser.addListener("startpage", startRule);
- parser.addListener("startpagemargin", startRule);
- parser.addListener("startkeyframerule", startRule);
- parser.addListener("startviewport", startRule);
-
- parser.addListener("property", function(event) {
- var name = event.property.text.toLowerCase();
-
- if (heightProperties[name] || widthProperties[name]) {
- if (!/^0\S*$/.test(event.value) && !(name === "border" && event.value.toString() === "none")) {
- properties[name] = {
- line: event.property.line,
- col: event.property.col,
- value: event.value
- };
- }
- } else {
- if (/^(width|height)/i.test(name) && /^(length|percentage)/.test(event.value.parts[0].type)) {
- properties[name] = 1;
- } else if (name === "box-sizing") {
- boxSizing = true;
- }
- }
-
- });
-
- parser.addListener("endrule", endRule);
- parser.addListener("endfontface", endRule);
- parser.addListener("endpage", endRule);
- parser.addListener("endpagemargin", endRule);
- parser.addListener("endkeyframerule", endRule);
- parser.addListener("endviewport", endRule);
- }
-
-});
-
-/*
- * Rule: box-sizing doesn't work in IE6 and IE7.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "box-sizing",
- name: "Disallow use of box-sizing",
- desc: "The box-sizing properties isn't supported in IE6 and IE7.",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-box-sizing",
- browsers: "IE6, IE7",
- tags: ["Compatibility"],
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this;
-
- parser.addListener("property", function(event) {
- var name = event.property.text.toLowerCase();
-
- if (name === "box-sizing") {
- reporter.report("The box-sizing property isn't supported in IE6 and IE7.", event.line, event.col, rule);
- }
- });
- }
-
-});
-
-/*
- * Rule: Use the bulletproof @font-face syntax to avoid 404's in old IE
- * (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax)
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "bulletproof-font-face",
- name: "Use the bulletproof @font-face syntax",
- 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).",
- url: "https://github.com/CSSLint/csslint/wiki/Bulletproof-font-face",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this,
- fontFaceRule = false,
- firstSrc = true,
- ruleFailed = false,
- line, col;
-
- // Mark the start of a @font-face declaration so we only test properties inside it
- parser.addListener("startfontface", function() {
- fontFaceRule = true;
- });
-
- parser.addListener("property", function(event) {
- // If we aren't inside an @font-face declaration then just return
- if (!fontFaceRule) {
- return;
- }
-
- var propertyName = event.property.toString().toLowerCase(),
- value = event.value.toString();
-
- // Set the line and col numbers for use in the endfontface listener
- line = event.line;
- col = event.col;
-
- // This is the property that we care about, we can ignore the rest
- if (propertyName === "src") {
- var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i;
-
- // We need to handle the advanced syntax with two src properties
- if (!value.match(regex) && firstSrc) {
- ruleFailed = true;
- firstSrc = false;
- } else if (value.match(regex) && !firstSrc) {
- ruleFailed = false;
- }
- }
-
-
- });
-
- // Back to normal rules that we don't need to test
- parser.addListener("endfontface", function() {
- fontFaceRule = false;
-
- if (ruleFailed) {
- reporter.report("@font-face declaration doesn't follow the fontspring bulletproof syntax.", line, col, rule);
- }
- });
- }
-});
-
-/*
- * Rule: Include all compatible vendor prefixes to reach a wider
- * range of users.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "compatible-vendor-prefixes",
- name: "Require compatible vendor prefixes",
- desc: "Include all compatible vendor prefixes to reach a wider range of users.",
- url: "https://github.com/CSSLint/csslint/wiki/Require-compatible-vendor-prefixes",
- browsers: "All",
-
- // initialization
- init: function (parser, reporter) {
- "use strict";
- var rule = this,
- compatiblePrefixes,
- properties,
- prop,
- variations,
- prefixed,
- i,
- len,
- inKeyFrame = false,
- arrayPush = Array.prototype.push,
- applyTo = [];
-
- // See http://peter.sh/experiments/vendor-prefixed-css-property-overview/ for details
- compatiblePrefixes = {
- "animation" : "webkit",
- "animation-delay" : "webkit",
- "animation-direction" : "webkit",
- "animation-duration" : "webkit",
- "animation-fill-mode" : "webkit",
- "animation-iteration-count" : "webkit",
- "animation-name" : "webkit",
- "animation-play-state" : "webkit",
- "animation-timing-function" : "webkit",
- "appearance" : "webkit moz",
- "border-end" : "webkit moz",
- "border-end-color" : "webkit moz",
- "border-end-style" : "webkit moz",
- "border-end-width" : "webkit moz",
- "border-image" : "webkit moz o",
- "border-radius" : "webkit",
- "border-start" : "webkit moz",
- "border-start-color" : "webkit moz",
- "border-start-style" : "webkit moz",
- "border-start-width" : "webkit moz",
- "box-align" : "webkit moz ms",
- "box-direction" : "webkit moz ms",
- "box-flex" : "webkit moz ms",
- "box-lines" : "webkit ms",
- "box-ordinal-group" : "webkit moz ms",
- "box-orient" : "webkit moz ms",
- "box-pack" : "webkit moz ms",
- "box-sizing" : "",
- "box-shadow" : "",
- "column-count" : "webkit moz ms",
- "column-gap" : "webkit moz ms",
- "column-rule" : "webkit moz ms",
- "column-rule-color" : "webkit moz ms",
- "column-rule-style" : "webkit moz ms",
- "column-rule-width" : "webkit moz ms",
- "column-width" : "webkit moz ms",
- "hyphens" : "epub moz",
- "line-break" : "webkit ms",
- "margin-end" : "webkit moz",
- "margin-start" : "webkit moz",
- "marquee-speed" : "webkit wap",
- "marquee-style" : "webkit wap",
- "padding-end" : "webkit moz",
- "padding-start" : "webkit moz",
- "tab-size" : "moz o",
- "text-size-adjust" : "webkit ms",
- "transform" : "webkit ms",
- "transform-origin" : "webkit ms",
- "transition" : "",
- "transition-delay" : "",
- "transition-duration" : "",
- "transition-property" : "",
- "transition-timing-function" : "",
- "user-modify" : "webkit moz",
- "user-select" : "webkit moz ms",
- "word-break" : "epub ms",
- "writing-mode" : "epub ms"
- };
-
-
- for (prop in compatiblePrefixes) {
- if (compatiblePrefixes.hasOwnProperty(prop)) {
- variations = [];
- prefixed = compatiblePrefixes[prop].split(" ");
- for (i = 0, len = prefixed.length; i < len; i++) {
- variations.push("-" + prefixed[i] + "-" + prop);
- }
- compatiblePrefixes[prop] = variations;
- arrayPush.apply(applyTo, variations);
- }
- }
-
- parser.addListener("startrule", function () {
- properties = [];
- });
-
- parser.addListener("startkeyframes", function (event) {
- inKeyFrame = event.prefix || true;
- });
-
- parser.addListener("endkeyframes", function () {
- inKeyFrame = false;
- });
-
- parser.addListener("property", function (event) {
- var name = event.property;
- if (CSSLint.Util.indexOf(applyTo, name.text) > -1) {
-
- // e.g., -moz-transform is okay to be alone in @-moz-keyframes
- if (!inKeyFrame || typeof inKeyFrame !== "string" ||
- name.text.indexOf("-" + inKeyFrame + "-") !== 0) {
- properties.push(name);
- }
- }
- });
-
- parser.addListener("endrule", function () {
- if (!properties.length) {
- return;
- }
-
- var propertyGroups = {},
- i,
- len,
- name,
- prop,
- variations,
- value,
- full,
- actual,
- item,
- propertiesSpecified;
-
- for (i = 0, len = properties.length; i < len; i++) {
- name = properties[i];
-
- for (prop in compatiblePrefixes) {
- if (compatiblePrefixes.hasOwnProperty(prop)) {
- variations = compatiblePrefixes[prop];
- if (CSSLint.Util.indexOf(variations, name.text) > -1) {
- if (!propertyGroups[prop]) {
- propertyGroups[prop] = {
- full: variations.slice(0),
- actual: [],
- actualNodes: []
- };
- }
- if (CSSLint.Util.indexOf(propertyGroups[prop].actual, name.text) === -1) {
- propertyGroups[prop].actual.push(name.text);
- propertyGroups[prop].actualNodes.push(name);
- }
- }
- }
- }
- }
-
- for (prop in propertyGroups) {
- if (propertyGroups.hasOwnProperty(prop)) {
- value = propertyGroups[prop];
- full = value.full;
- actual = value.actual;
-
- if (full.length > actual.length) {
- for (i = 0, len = full.length; i < len; i++) {
- item = full[i];
- if (CSSLint.Util.indexOf(actual, item) === -1) {
- propertiesSpecified = (actual.length === 1) ? actual[0] : (actual.length === 2) ? actual.join(" and ") : actual.join(", ");
- reporter.report("The property " + item + " is compatible with " + propertiesSpecified + " and should be included as well.", value.actualNodes[0].line, value.actualNodes[0].col, rule);
- }
- }
-
- }
- }
- }
- });
- }
-});
-
-/*
- * Rule: Certain properties don't play well with certain display values.
- * - float should not be used with inline-block
- * - height, width, margin-top, margin-bottom, float should not be used with inline
- * - vertical-align should not be used with block
- * - margin, float should not be used with table-*
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "display-property-grouping",
- name: "Require properties appropriate for display",
- desc: "Certain properties shouldn't be used with certain display property values.",
- url: "https://github.com/CSSLint/csslint/wiki/Require-properties-appropriate-for-display",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this;
-
- var propertiesToCheck = {
- display: 1,
- "float": "none",
- height: 1,
- width: 1,
- margin: 1,
- "margin-left": 1,
- "margin-right": 1,
- "margin-bottom": 1,
- "margin-top": 1,
- padding: 1,
- "padding-left": 1,
- "padding-right": 1,
- "padding-bottom": 1,
- "padding-top": 1,
- "vertical-align": 1
- },
- properties;
-
- function reportProperty(name, display, msg) {
- if (properties[name]) {
- if (typeof propertiesToCheck[name] !== "string" || properties[name].value.toLowerCase() !== propertiesToCheck[name]) {
- reporter.report(msg || name + " can't be used with display: " + display + ".", properties[name].line, properties[name].col, rule);
- }
- }
- }
-
- function startRule() {
- properties = {};
- }
-
- function endRule() {
-
- var display = properties.display ? properties.display.value : null;
- if (display) {
- switch (display) {
-
- case "inline":
- // height, width, margin-top, margin-bottom, float should not be used with inline
- reportProperty("height", display);
- reportProperty("width", display);
- reportProperty("margin", display);
- reportProperty("margin-top", display);
- reportProperty("margin-bottom", display);
- reportProperty("float", display, "display:inline has no effect on floated elements (but may be used to fix the IE6 double-margin bug).");
- break;
-
- case "block":
- // vertical-align should not be used with block
- reportProperty("vertical-align", display);
- break;
-
- case "inline-block":
- // float should not be used with inline-block
- reportProperty("float", display);
- break;
-
- default:
- // margin, float should not be used with table
- if (display.indexOf("table-") === 0) {
- reportProperty("margin", display);
- reportProperty("margin-left", display);
- reportProperty("margin-right", display);
- reportProperty("margin-top", display);
- reportProperty("margin-bottom", display);
- reportProperty("float", display);
- }
-
- // otherwise do nothing
- }
- }
-
- }
-
- parser.addListener("startrule", startRule);
- parser.addListener("startfontface", startRule);
- parser.addListener("startkeyframerule", startRule);
- parser.addListener("startpagemargin", startRule);
- parser.addListener("startpage", startRule);
- parser.addListener("startviewport", startRule);
-
- parser.addListener("property", function(event) {
- var name = event.property.text.toLowerCase();
-
- if (propertiesToCheck[name]) {
- properties[name] = {
- value: event.value.text,
- line: event.property.line,
- col: event.property.col
- };
- }
- });
-
- parser.addListener("endrule", endRule);
- parser.addListener("endfontface", endRule);
- parser.addListener("endkeyframerule", endRule);
- parser.addListener("endpagemargin", endRule);
- parser.addListener("endpage", endRule);
- parser.addListener("endviewport", endRule);
-
- }
-
-});
-
-/*
- * Rule: Disallow duplicate background-images (using url).
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "duplicate-background-images",
- name: "Disallow duplicate background images",
- desc: "Every background-image should be unique. Use a common class for e.g. sprites.",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-background-images",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this,
- stack = {};
-
- parser.addListener("property", function(event) {
- var name = event.property.text,
- value = event.value,
- i, len;
-
- if (name.match(/background/i)) {
- for (i=0, len=value.parts.length; i < len; i++) {
- if (value.parts[i].type === "uri") {
- if (typeof stack[value.parts[i].uri] === "undefined") {
- stack[value.parts[i].uri] = event;
- } else {
- 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);
- }
- }
- }
- }
- });
- }
-});
-
-/*
- * Rule: Duplicate properties must appear one after the other. If an already-defined
- * property appears somewhere else in the rule, then it's likely an error.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "duplicate-properties",
- name: "Disallow duplicate properties",
- desc: "Duplicate properties must appear one after the other.",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-properties",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this,
- properties,
- lastProperty;
-
- function startRule() {
- properties = {};
- }
-
- parser.addListener("startrule", startRule);
- parser.addListener("startfontface", startRule);
- parser.addListener("startpage", startRule);
- parser.addListener("startpagemargin", startRule);
- parser.addListener("startkeyframerule", startRule);
- parser.addListener("startviewport", startRule);
-
- parser.addListener("property", function(event) {
- var property = event.property,
- name = property.text.toLowerCase();
-
- if (properties[name] && (lastProperty !== name || properties[name] === event.value.text)) {
- reporter.report("Duplicate property '" + event.property + "' found.", event.line, event.col, rule);
- }
-
- properties[name] = event.value.text;
- lastProperty = name;
-
- });
-
-
- }
-
-});
-
-/*
- * Rule: Style rules without any properties defined should be removed.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "empty-rules",
- name: "Disallow empty rules",
- desc: "Rules without any properties specified should be removed.",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-empty-rules",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this,
- count = 0;
-
- parser.addListener("startrule", function() {
- count=0;
- });
-
- parser.addListener("property", function() {
- count++;
- });
-
- parser.addListener("endrule", function(event) {
- var selectors = event.selectors;
- if (count === 0) {
- reporter.report("Rule is empty.", selectors[0].line, selectors[0].col, rule);
- }
- });
- }
-
-});
-
-/*
- * Rule: There should be no syntax errors. (Duh.)
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "errors",
- name: "Parsing Errors",
- desc: "This rule looks for recoverable syntax errors.",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this;
-
- parser.addListener("error", function(event) {
- reporter.error(event.message, event.line, event.col, rule);
- });
-
- }
-
-});
-
-CSSLint.addRule({
-
- // rule information
- id: "fallback-colors",
- name: "Require fallback colors",
- desc: "For older browsers that don't support RGBA, HSL, or HSLA, provide a fallback color.",
- url: "https://github.com/CSSLint/csslint/wiki/Require-fallback-colors",
- browsers: "IE6,IE7,IE8",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this,
- lastProperty,
- propertiesToCheck = {
- color: 1,
- background: 1,
- "border-color": 1,
- "border-top-color": 1,
- "border-right-color": 1,
- "border-bottom-color": 1,
- "border-left-color": 1,
- border: 1,
- "border-top": 1,
- "border-right": 1,
- "border-bottom": 1,
- "border-left": 1,
- "background-color": 1
- };
-
- function startRule() {
- lastProperty = null;
- }
-
- parser.addListener("startrule", startRule);
- parser.addListener("startfontface", startRule);
- parser.addListener("startpage", startRule);
- parser.addListener("startpagemargin", startRule);
- parser.addListener("startkeyframerule", startRule);
- parser.addListener("startviewport", startRule);
-
- parser.addListener("property", function(event) {
- var property = event.property,
- name = property.text.toLowerCase(),
- parts = event.value.parts,
- i = 0,
- colorType = "",
- len = parts.length;
-
- if (propertiesToCheck[name]) {
- while (i < len) {
- if (parts[i].type === "color") {
- if ("alpha" in parts[i] || "hue" in parts[i]) {
-
- if (/([^\)]+)\(/.test(parts[i])) {
- colorType = RegExp.$1.toUpperCase();
- }
-
- if (!lastProperty || (lastProperty.property.text.toLowerCase() !== name || lastProperty.colorType !== "compat")) {
- reporter.report("Fallback " + name + " (hex or RGB) should precede " + colorType + " " + name + ".", event.line, event.col, rule);
- }
- } else {
- event.colorType = "compat";
- }
- }
-
- i++;
- }
- }
-
- lastProperty = event;
- });
-
- }
-
-});
-
-/*
- * Rule: You shouldn't use more than 10 floats. If you do, there's probably
- * room for some abstraction.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "floats",
- name: "Disallow too many floats",
- desc: "This rule tests if the float property is used too many times",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-too-many-floats",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this;
- var count = 0;
-
- // count how many times "float" is used
- parser.addListener("property", function(event) {
- if (event.property.text.toLowerCase() === "float" &&
- event.value.text.toLowerCase() !== "none") {
- count++;
- }
- });
-
- // report the results
- parser.addListener("endstylesheet", function() {
- reporter.stat("floats", count);
- if (count >= 10) {
- reporter.rollupWarn("Too many floats (" + count + "), you're probably using them for layout. Consider using a grid system instead.", rule);
- }
- });
- }
-
-});
-
-/*
- * Rule: Avoid too many @font-face declarations in the same stylesheet.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "font-faces",
- name: "Don't use too many web fonts",
- desc: "Too many different web fonts in the same stylesheet.",
- url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-web-fonts",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this,
- count = 0;
-
-
- parser.addListener("startfontface", function() {
- count++;
- });
-
- parser.addListener("endstylesheet", function() {
- if (count > 5) {
- reporter.rollupWarn("Too many @font-face declarations (" + count + ").", rule);
- }
- });
- }
-
-});
-
-/*
- * Rule: You shouldn't need more than 9 font-size declarations.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "font-sizes",
- name: "Disallow too many font sizes",
- desc: "Checks the number of font-size declarations.",
- url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-font-size-declarations",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this,
- count = 0;
-
- // check for use of "font-size"
- parser.addListener("property", function(event) {
- if (event.property.toString() === "font-size") {
- count++;
- }
- });
-
- // report the results
- parser.addListener("endstylesheet", function() {
- reporter.stat("font-sizes", count);
- if (count >= 10) {
- reporter.rollupWarn("Too many font-size declarations (" + count + "), abstraction needed.", rule);
- }
- });
- }
-
-});
-
-/*
- * Rule: When using a vendor-prefixed gradient, make sure to use them all.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "gradients",
- name: "Require all gradient definitions",
- desc: "When using a vendor-prefixed gradient, make sure to use them all.",
- url: "https://github.com/CSSLint/csslint/wiki/Require-all-gradient-definitions",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this,
- gradients;
-
- parser.addListener("startrule", function() {
- gradients = {
- moz: 0,
- webkit: 0,
- oldWebkit: 0,
- o: 0
- };
- });
-
- parser.addListener("property", function(event) {
-
- if (/\-(moz|o|webkit)(?:\-(?:linear|radial))\-gradient/i.test(event.value)) {
- gradients[RegExp.$1] = 1;
- } else if (/\-webkit\-gradient/i.test(event.value)) {
- gradients.oldWebkit = 1;
- }
-
- });
-
- parser.addListener("endrule", function(event) {
- var missing = [];
-
- if (!gradients.moz) {
- missing.push("Firefox 3.6+");
- }
-
- if (!gradients.webkit) {
- missing.push("Webkit (Safari 5+, Chrome)");
- }
-
- if (!gradients.oldWebkit) {
- missing.push("Old Webkit (Safari 4+, Chrome)");
- }
-
- if (!gradients.o) {
- missing.push("Opera 11.1+");
- }
-
- if (missing.length && missing.length < 4) {
- reporter.report("Missing vendor-prefixed CSS gradients for " + missing.join(", ") + ".", event.selectors[0].line, event.selectors[0].col, rule);
- }
-
- });
-
- }
-
-});
-
-/*
- * Rule: Don't use IDs for selectors.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "ids",
- name: "Disallow IDs in selectors",
- desc: "Selectors should not contain IDs.",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-IDs-in-selectors",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this;
- parser.addListener("startrule", function(event) {
- var selectors = event.selectors,
- selector,
- part,
- modifier,
- idCount,
- i, j, k;
-
- for (i=0; i < selectors.length; i++) {
- selector = selectors[i];
- idCount = 0;
-
- for (j=0; j < selector.parts.length; j++) {
- part = selector.parts[j];
- if (part.type === parser.SELECTOR_PART_TYPE) {
- for (k=0; k < part.modifiers.length; k++) {
- modifier = part.modifiers[k];
- if (modifier.type === "id") {
- idCount++;
- }
- }
- }
- }
-
- if (idCount === 1) {
- reporter.report("Don't use IDs in selectors.", selector.line, selector.col, rule);
- } else if (idCount > 1) {
- reporter.report(idCount + " IDs in the selector, really?", selector.line, selector.col, rule);
- }
- }
-
- });
- }
-
-});
-
-/*
- * Rule: IE6-9 supports up to 31 stylesheet import.
- * Reference:
- * http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/internet-explorer-stylesheet-rule-selector-import-sheet-limit-maximum.aspx
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "import-ie-limit",
- name: "@import limit on IE6-IE9",
- desc: "IE6-9 supports up to 31 @import per stylesheet",
- browsers: "IE6, IE7, IE8, IE9",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this,
- MAX_IMPORT_COUNT = 31,
- count = 0;
-
- function startPage() {
- count = 0;
- }
-
- parser.addListener("startpage", startPage);
-
- parser.addListener("import", function() {
- count++;
- });
-
- parser.addListener("endstylesheet", function() {
- if (count > MAX_IMPORT_COUNT) {
- reporter.rollupError(
- "Too many @import rules (" + count + "). IE6-9 supports up to 31 import per stylesheet.",
- rule
- );
- }
- });
- }
-
-});
-
-/*
- * Rule: Don't use @import, use <link> instead.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "import",
- name: "Disallow @import",
- desc: "Don't use @import, use <link> instead.",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-%40import",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this;
-
- parser.addListener("import", function(event) {
- reporter.report("@import prevents parallel downloads, use <link> instead.", event.line, event.col, rule);
- });
-
- }
-
-});
-
-/*
- * Rule: Make sure !important is not overused, this could lead to specificity
- * war. Display a warning on !important declarations, an error if it's
- * used more at least 10 times.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "important",
- name: "Disallow !important",
- desc: "Be careful when using !important declaration",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-%21important",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this,
- count = 0;
-
- // warn that important is used and increment the declaration counter
- parser.addListener("property", function(event) {
- if (event.important === true) {
- count++;
- reporter.report("Use of !important", event.line, event.col, rule);
- }
- });
-
- // if there are more than 10, show an error
- parser.addListener("endstylesheet", function() {
- reporter.stat("important", count);
- if (count >= 10) {
- reporter.rollupWarn("Too many !important declarations (" + count + "), try to use less than 10 to avoid specificity issues.", rule);
- }
- });
- }
-
-});
-
-/*
- * Rule: Properties should be known (listed in CSS3 specification) or
- * be a vendor-prefixed property.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "known-properties",
- name: "Require use of known properties",
- desc: "Properties should be known (listed in CSS3 specification) or be a vendor-prefixed property.",
- url: "https://github.com/CSSLint/csslint/wiki/Require-use-of-known-properties",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this;
-
- parser.addListener("property", function(event) {
-
- // the check is handled entirely by the parser-lib (https://github.com/nzakas/parser-lib)
- if (event.invalid) {
- reporter.report(event.invalid.message, event.line, event.col, rule);
- }
-
- });
- }
-
-});
-
-/*
- * Rule: All properties should be in alphabetical order.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "order-alphabetical",
- name: "Alphabetical order",
- desc: "Assure properties are in alphabetical order",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this,
- properties;
-
- var startRule = function () {
- properties = [];
- };
-
- var endRule = function(event) {
- var currentProperties = properties.join(","),
- expectedProperties = properties.sort().join(",");
-
- if (currentProperties !== expectedProperties) {
- reporter.report("Rule doesn't have all its properties in alphabetical order.", event.line, event.col, rule);
- }
- };
-
- parser.addListener("startrule", startRule);
- parser.addListener("startfontface", startRule);
- parser.addListener("startpage", startRule);
- parser.addListener("startpagemargin", startRule);
- parser.addListener("startkeyframerule", startRule);
- parser.addListener("startviewport", startRule);
-
- parser.addListener("property", function(event) {
- var name = event.property.text,
- lowerCasePrefixLessName = name.toLowerCase().replace(/^-.*?-/, "");
-
- properties.push(lowerCasePrefixLessName);
- });
-
- parser.addListener("endrule", endRule);
- parser.addListener("endfontface", endRule);
- parser.addListener("endpage", endRule);
- parser.addListener("endpagemargin", endRule);
- parser.addListener("endkeyframerule", endRule);
- parser.addListener("endviewport", endRule);
- }
-
-});
-
-/*
- * Rule: outline: none or outline: 0 should only be used in a :focus rule
- * and only if there are other properties in the same rule.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "outline-none",
- name: "Disallow outline: none",
- desc: "Use of outline: none or outline: 0 should be limited to :focus rules.",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-outline%3Anone",
- browsers: "All",
- tags: ["Accessibility"],
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this,
- lastRule;
-
- function startRule(event) {
- if (event.selectors) {
- lastRule = {
- line: event.line,
- col: event.col,
- selectors: event.selectors,
- propCount: 0,
- outline: false
- };
- } else {
- lastRule = null;
- }
- }
-
- function endRule() {
- if (lastRule) {
- if (lastRule.outline) {
- if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") === -1) {
- reporter.report("Outlines should only be modified using :focus.", lastRule.line, lastRule.col, rule);
- } else if (lastRule.propCount === 1) {
- reporter.report("Outlines shouldn't be hidden unless other visual changes are made.", lastRule.line, lastRule.col, rule);
- }
- }
- }
- }
-
- parser.addListener("startrule", startRule);
- parser.addListener("startfontface", startRule);
- parser.addListener("startpage", startRule);
- parser.addListener("startpagemargin", startRule);
- parser.addListener("startkeyframerule", startRule);
- parser.addListener("startviewport", startRule);
-
- parser.addListener("property", function(event) {
- var name = event.property.text.toLowerCase(),
- value = event.value;
-
- if (lastRule) {
- lastRule.propCount++;
- if (name === "outline" && (value.toString() === "none" || value.toString() === "0")) {
- lastRule.outline = true;
- }
- }
-
- });
-
- parser.addListener("endrule", endRule);
- parser.addListener("endfontface", endRule);
- parser.addListener("endpage", endRule);
- parser.addListener("endpagemargin", endRule);
- parser.addListener("endkeyframerule", endRule);
- parser.addListener("endviewport", endRule);
-
- }
-
-});
-
-/*
- * Rule: Don't use classes or IDs with elements (a.foo or a#foo).
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "overqualified-elements",
- name: "Disallow overqualified elements",
- desc: "Don't use classes or IDs with elements (a.foo or a#foo).",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-overqualified-elements",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this,
- classes = {};
-
- parser.addListener("startrule", function(event) {
- var selectors = event.selectors,
- selector,
- part,
- modifier,
- i, j, k;
-
- for (i=0; i < selectors.length; i++) {
- selector = selectors[i];
-
- for (j=0; j < selector.parts.length; j++) {
- part = selector.parts[j];
- if (part.type === parser.SELECTOR_PART_TYPE) {
- for (k=0; k < part.modifiers.length; k++) {
- modifier = part.modifiers[k];
- if (part.elementName && modifier.type === "id") {
- reporter.report("Element (" + part + ") is overqualified, just use " + modifier + " without element name.", part.line, part.col, rule);
- } else if (modifier.type === "class") {
-
- if (!classes[modifier]) {
- classes[modifier] = [];
- }
- classes[modifier].push({
- modifier: modifier,
- part: part
- });
- }
- }
- }
- }
- }
- });
-
- parser.addListener("endstylesheet", function() {
-
- var prop;
- for (prop in classes) {
- if (classes.hasOwnProperty(prop)) {
-
- // one use means that this is overqualified
- if (classes[prop].length === 1 && classes[prop][0].part.elementName) {
- 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);
- }
- }
- }
- });
- }
-
-});
-
-/*
- * Rule: Headings (h1-h6) should not be qualified (namespaced).
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "qualified-headings",
- name: "Disallow qualified headings",
- desc: "Headings should not be qualified (namespaced).",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-qualified-headings",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this;
-
- parser.addListener("startrule", function(event) {
- var selectors = event.selectors,
- selector,
- part,
- i, j;
-
- for (i=0; i < selectors.length; i++) {
- selector = selectors[i];
-
- for (j=0; j < selector.parts.length; j++) {
- part = selector.parts[j];
- if (part.type === parser.SELECTOR_PART_TYPE) {
- if (part.elementName && /h[1-6]/.test(part.elementName.toString()) && j > 0) {
- reporter.report("Heading (" + part.elementName + ") should not be qualified.", part.line, part.col, rule);
- }
- }
- }
- }
- });
- }
-
-});
-
-/*
- * Rule: Selectors that look like regular expressions are slow and should be avoided.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "regex-selectors",
- name: "Disallow selectors that look like regexs",
- desc: "Selectors that look like regular expressions are slow and should be avoided.",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-selectors-that-look-like-regular-expressions",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this;
-
- parser.addListener("startrule", function(event) {
- var selectors = event.selectors,
- selector,
- part,
- modifier,
- i, j, k;
-
- for (i=0; i < selectors.length; i++) {
- selector = selectors[i];
- for (j=0; j < selector.parts.length; j++) {
- part = selector.parts[j];
- if (part.type === parser.SELECTOR_PART_TYPE) {
- for (k=0; k < part.modifiers.length; k++) {
- modifier = part.modifiers[k];
- if (modifier.type === "attribute") {
- if (/([~\|\^\$\*]=)/.test(modifier)) {
- reporter.report("Attribute selectors with " + RegExp.$1 + " are slow!", modifier.line, modifier.col, rule);
- }
- }
-
- }
- }
- }
- }
- });
- }
-
-});
-
-/*
- * Rule: Total number of rules should not exceed x.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "rules-count",
- name: "Rules Count",
- desc: "Track how many rules there are.",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var count = 0;
-
- // count each rule
- parser.addListener("startrule", function() {
- count++;
- });
-
- parser.addListener("endstylesheet", function() {
- reporter.stat("rule-count", count);
- });
- }
-
-});
-
-/*
- * Rule: Warn people with approaching the IE 4095 limit
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "selector-max-approaching",
- name: "Warn when approaching the 4095 selector limit for IE",
- desc: "Will warn when selector count is >= 3800 selectors.",
- browsers: "IE",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this, count = 0;
-
- parser.addListener("startrule", function(event) {
- count += event.selectors.length;
- });
-
- parser.addListener("endstylesheet", function() {
- if (count >= 3800) {
- reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule);
- }
- });
- }
-
-});
-
-/*
- * Rule: Warn people past the IE 4095 limit
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "selector-max",
- name: "Error when past the 4095 selector limit for IE",
- desc: "Will error when selector count is > 4095.",
- browsers: "IE",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this, count = 0;
-
- parser.addListener("startrule", function(event) {
- count += event.selectors.length;
- });
-
- parser.addListener("endstylesheet", function() {
- if (count > 4095) {
- reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule);
- }
- });
- }
-
-});
-
-/*
- * Rule: Avoid new-line characters in selectors.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "selector-newline",
- name: "Disallow new-line characters in selectors",
- desc: "New-line characters in selectors are usually a forgotten comma and not a descendant combinator.",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this;
-
- function startRule(event) {
- var i, len, selector, p, n, pLen, part, part2, type, currentLine, nextLine,
- selectors = event.selectors;
-
- for (i = 0, len = selectors.length; i < len; i++) {
- selector = selectors[i];
- for (p = 0, pLen = selector.parts.length; p < pLen; p++) {
- for (n = p + 1; n < pLen; n++) {
- part = selector.parts[p];
- part2 = selector.parts[n];
- type = part.type;
- currentLine = part.line;
- nextLine = part2.line;
-
- if (type === "descendant" && nextLine > currentLine) {
- reporter.report("newline character found in selector (forgot a comma?)", currentLine, selectors[i].parts[0].col, rule);
- }
- }
- }
-
- }
- }
-
- parser.addListener("startrule", startRule);
-
- }
-});
-
-/*
- * Rule: Use shorthand properties where possible.
- *
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "shorthand",
- name: "Require shorthand properties",
- desc: "Use shorthand properties where possible.",
- url: "https://github.com/CSSLint/csslint/wiki/Require-shorthand-properties",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this,
- prop, i, len,
- propertiesToCheck = {},
- properties,
- mapping = {
- "margin": [
- "margin-top",
- "margin-bottom",
- "margin-left",
- "margin-right"
- ],
- "padding": [
- "padding-top",
- "padding-bottom",
- "padding-left",
- "padding-right"
- ]
- };
-
- // initialize propertiesToCheck
- for (prop in mapping) {
- if (mapping.hasOwnProperty(prop)) {
- for (i=0, len=mapping[prop].length; i < len; i++) {
- propertiesToCheck[mapping[prop][i]] = prop;
- }
- }
- }
-
- function startRule() {
- properties = {};
- }
-
- // event handler for end of rules
- function endRule(event) {
-
- var prop, i, len, total;
-
- // check which properties this rule has
- for (prop in mapping) {
- if (mapping.hasOwnProperty(prop)) {
- total=0;
-
- for (i=0, len=mapping[prop].length; i < len; i++) {
- total += properties[mapping[prop][i]] ? 1 : 0;
- }
-
- if (total === mapping[prop].length) {
- reporter.report("The properties " + mapping[prop].join(", ") + " can be replaced by " + prop + ".", event.line, event.col, rule);
- }
- }
- }
- }
-
- parser.addListener("startrule", startRule);
- parser.addListener("startfontface", startRule);
-
- // check for use of "font-size"
- parser.addListener("property", function(event) {
- var name = event.property.toString().toLowerCase();
-
- if (propertiesToCheck[name]) {
- properties[name] = 1;
- }
- });
-
- parser.addListener("endrule", endRule);
- parser.addListener("endfontface", endRule);
-
- }
-
-});
-
-/*
- * Rule: Don't use properties with a star prefix.
- *
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "star-property-hack",
- name: "Disallow properties with a star prefix",
- desc: "Checks for the star property hack (targets IE6/7)",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-star-hack",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this;
-
- // check if property name starts with "*"
- parser.addListener("property", function(event) {
- var property = event.property;
-
- if (property.hack === "*") {
- reporter.report("Property with star prefix found.", event.property.line, event.property.col, rule);
- }
- });
- }
-});
-
-/*
- * Rule: Don't use text-indent for image replacement if you need to support rtl.
- *
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "text-indent",
- name: "Disallow negative text-indent",
- desc: "Checks for text indent less than -99px",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-negative-text-indent",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this,
- textIndent,
- direction;
-
-
- function startRule() {
- textIndent = false;
- direction = "inherit";
- }
-
- // event handler for end of rules
- function endRule() {
- if (textIndent && direction !== "ltr") {
- 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);
- }
- }
-
- parser.addListener("startrule", startRule);
- parser.addListener("startfontface", startRule);
-
- // check for use of "font-size"
- parser.addListener("property", function(event) {
- var name = event.property.toString().toLowerCase(),
- value = event.value;
-
- if (name === "text-indent" && value.parts[0].value < -99) {
- textIndent = event.property;
- } else if (name === "direction" && value.toString() === "ltr") {
- direction = "ltr";
- }
- });
-
- parser.addListener("endrule", endRule);
- parser.addListener("endfontface", endRule);
-
- }
-
-});
-
-/*
- * Rule: Don't use properties with a underscore prefix.
- *
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "underscore-property-hack",
- name: "Disallow properties with an underscore prefix",
- desc: "Checks for the underscore property hack (targets IE6)",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-underscore-hack",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this;
-
- // check if property name starts with "_"
- parser.addListener("property", function(event) {
- var property = event.property;
-
- if (property.hack === "_") {
- reporter.report("Property with underscore prefix found.", event.property.line, event.property.col, rule);
- }
- });
- }
-});
-
-/*
- * Rule: Headings (h1-h6) should be defined only once.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "unique-headings",
- name: "Headings should only be defined once",
- desc: "Headings should be defined only once.",
- url: "https://github.com/CSSLint/csslint/wiki/Headings-should-only-be-defined-once",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this;
-
- var headings = {
- h1: 0,
- h2: 0,
- h3: 0,
- h4: 0,
- h5: 0,
- h6: 0
- };
-
- parser.addListener("startrule", function(event) {
- var selectors = event.selectors,
- selector,
- part,
- pseudo,
- i, j;
-
- for (i=0; i < selectors.length; i++) {
- selector = selectors[i];
- part = selector.parts[selector.parts.length-1];
-
- if (part.elementName && /(h[1-6])/i.test(part.elementName.toString())) {
-
- for (j=0; j < part.modifiers.length; j++) {
- if (part.modifiers[j].type === "pseudo") {
- pseudo = true;
- break;
- }
- }
-
- if (!pseudo) {
- headings[RegExp.$1]++;
- if (headings[RegExp.$1] > 1) {
- reporter.report("Heading (" + part.elementName + ") has already been defined.", part.line, part.col, rule);
- }
- }
- }
- }
- });
-
- parser.addListener("endstylesheet", function() {
- var prop,
- messages = [];
-
- for (prop in headings) {
- if (headings.hasOwnProperty(prop)) {
- if (headings[prop] > 1) {
- messages.push(headings[prop] + " " + prop + "s");
- }
- }
- }
-
- if (messages.length) {
- reporter.rollupWarn("You have " + messages.join(", ") + " defined in this stylesheet.", rule);
- }
- });
- }
-
-});
-
-/*
- * Rule: Don't use universal selector because it's slow.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "universal-selector",
- name: "Disallow universal selector",
- desc: "The universal selector (*) is known to be slow.",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-universal-selector",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this;
-
- parser.addListener("startrule", function(event) {
- var selectors = event.selectors,
- selector,
- part,
- i;
-
- for (i=0; i < selectors.length; i++) {
- selector = selectors[i];
-
- part = selector.parts[selector.parts.length-1];
- if (part.elementName === "*") {
- reporter.report(rule.desc, part.line, part.col, rule);
- }
- }
- });
- }
-
-});
-
-/*
- * Rule: Don't use unqualified attribute selectors because they're just like universal selectors.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "unqualified-attributes",
- name: "Disallow unqualified attribute selectors",
- desc: "Unqualified attribute selectors are known to be slow.",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-unqualified-attribute-selectors",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
-
- var rule = this;
-
- parser.addListener("startrule", function(event) {
-
- var selectors = event.selectors,
- selectorContainsClassOrId = false,
- selector,
- part,
- modifier,
- i, k;
-
- for (i=0; i < selectors.length; i++) {
- selector = selectors[i];
-
- part = selector.parts[selector.parts.length-1];
- if (part.type === parser.SELECTOR_PART_TYPE) {
- for (k=0; k < part.modifiers.length; k++) {
- modifier = part.modifiers[k];
-
- if (modifier.type === "class" || modifier.type === "id") {
- selectorContainsClassOrId = true;
- break;
- }
- }
-
- if (!selectorContainsClassOrId) {
- for (k=0; k < part.modifiers.length; k++) {
- modifier = part.modifiers[k];
- if (modifier.type === "attribute" && (!part.elementName || part.elementName === "*")) {
- reporter.report(rule.desc, part.line, part.col, rule);
- }
- }
- }
- }
-
- }
- });
- }
-
-});
-
-/*
- * Rule: When using a vendor-prefixed property, make sure to
- * include the standard one.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "vendor-prefix",
- name: "Require standard property with vendor prefix",
- desc: "When using a vendor-prefixed property, make sure to include the standard one.",
- url: "https://github.com/CSSLint/csslint/wiki/Require-standard-property-with-vendor-prefix",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this,
- properties,
- num,
- propertiesToCheck = {
- "-webkit-border-radius": "border-radius",
- "-webkit-border-top-left-radius": "border-top-left-radius",
- "-webkit-border-top-right-radius": "border-top-right-radius",
- "-webkit-border-bottom-left-radius": "border-bottom-left-radius",
- "-webkit-border-bottom-right-radius": "border-bottom-right-radius",
-
- "-o-border-radius": "border-radius",
- "-o-border-top-left-radius": "border-top-left-radius",
- "-o-border-top-right-radius": "border-top-right-radius",
- "-o-border-bottom-left-radius": "border-bottom-left-radius",
- "-o-border-bottom-right-radius": "border-bottom-right-radius",
-
- "-moz-border-radius": "border-radius",
- "-moz-border-radius-topleft": "border-top-left-radius",
- "-moz-border-radius-topright": "border-top-right-radius",
- "-moz-border-radius-bottomleft": "border-bottom-left-radius",
- "-moz-border-radius-bottomright": "border-bottom-right-radius",
-
- "-moz-column-count": "column-count",
- "-webkit-column-count": "column-count",
-
- "-moz-column-gap": "column-gap",
- "-webkit-column-gap": "column-gap",
-
- "-moz-column-rule": "column-rule",
- "-webkit-column-rule": "column-rule",
-
- "-moz-column-rule-style": "column-rule-style",
- "-webkit-column-rule-style": "column-rule-style",
-
- "-moz-column-rule-color": "column-rule-color",
- "-webkit-column-rule-color": "column-rule-color",
-
- "-moz-column-rule-width": "column-rule-width",
- "-webkit-column-rule-width": "column-rule-width",
-
- "-moz-column-width": "column-width",
- "-webkit-column-width": "column-width",
-
- "-webkit-column-span": "column-span",
- "-webkit-columns": "columns",
-
- "-moz-box-shadow": "box-shadow",
- "-webkit-box-shadow": "box-shadow",
-
- "-moz-transform": "transform",
- "-webkit-transform": "transform",
- "-o-transform": "transform",
- "-ms-transform": "transform",
-
- "-moz-transform-origin": "transform-origin",
- "-webkit-transform-origin": "transform-origin",
- "-o-transform-origin": "transform-origin",
- "-ms-transform-origin": "transform-origin",
-
- "-moz-box-sizing": "box-sizing",
- "-webkit-box-sizing": "box-sizing"
- };
-
- // event handler for beginning of rules
- function startRule() {
- properties = {};
- num = 1;
- }
-
- // event handler for end of rules
- function endRule() {
- var prop,
- i,
- len,
- needed,
- actual,
- needsStandard = [];
-
- for (prop in properties) {
- if (propertiesToCheck[prop]) {
- needsStandard.push({
- actual: prop,
- needed: propertiesToCheck[prop]
- });
- }
- }
-
- for (i=0, len=needsStandard.length; i < len; i++) {
- needed = needsStandard[i].needed;
- actual = needsStandard[i].actual;
-
- if (!properties[needed]) {
- reporter.report("Missing standard property '" + needed + "' to go along with '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
- } else {
- // make sure standard property is last
- if (properties[needed][0].pos < properties[actual][0].pos) {
- reporter.report("Standard property '" + needed + "' should come after vendor-prefixed property '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
- }
- }
- }
-
- }
-
- parser.addListener("startrule", startRule);
- parser.addListener("startfontface", startRule);
- parser.addListener("startpage", startRule);
- parser.addListener("startpagemargin", startRule);
- parser.addListener("startkeyframerule", startRule);
- parser.addListener("startviewport", startRule);
-
- parser.addListener("property", function(event) {
- var name = event.property.text.toLowerCase();
-
- if (!properties[name]) {
- properties[name] = [];
- }
-
- properties[name].push({
- name: event.property,
- value: event.value,
- pos: num++
- });
- });
-
- parser.addListener("endrule", endRule);
- parser.addListener("endfontface", endRule);
- parser.addListener("endpage", endRule);
- parser.addListener("endpagemargin", endRule);
- parser.addListener("endkeyframerule", endRule);
- parser.addListener("endviewport", endRule);
- }
-
-});
-
-/*
- * Rule: You don't need to specify units when a value is 0.
- */
-
-CSSLint.addRule({
-
- // rule information
- id: "zero-units",
- name: "Disallow units for 0 values",
- desc: "You don't need to specify units when a value is 0.",
- url: "https://github.com/CSSLint/csslint/wiki/Disallow-units-for-zero-values",
- browsers: "All",
-
- // initialization
- init: function(parser, reporter) {
- "use strict";
- var rule = this;
-
- // count how many times "float" is used
- parser.addListener("property", function(event) {
- var parts = event.value.parts,
- i = 0,
- len = parts.length;
-
- while (i < len) {
- if ((parts[i].units || parts[i].type === "percentage") && parts[i].value === 0 && parts[i].type !== "time") {
- reporter.report("Values of 0 shouldn't have units specified.", parts[i].line, parts[i].col, rule);
- }
- i++;
- }
-
- });
-
- }
-
-});
-
-(function() {
- "use strict";
-
- /**
- * Replace special characters before write to output.
- *
- * Rules:
- * - single quotes is the escape sequence for double-quotes
- * - & is the escape sequence for &
- * - < is the escape sequence for <
- * - > is the escape sequence for >
- *
- * @param {String} message to escape
- * @return escaped message as {String}
- */
- var xmlEscape = function(str) {
- if (!str || str.constructor !== String) {
- return "";
- }
-
- return str.replace(/["&><]/g, function(match) {
- switch (match) {
- case "\"":
- return """;
- case "&":
- return "&";
- case "<":
- return "<";
- case ">":
- return ">";
- }
- });
- };
-
- CSSLint.addFormatter({
- // format information
- id: "checkstyle-xml",
- name: "Checkstyle XML format",
-
- /**
- * Return opening root XML tag.
- * @return {String} to prepend before all results
- */
- startFormat: function() {
- return "<?xml version=\"1.0\" encoding=\"utf-8\"?><checkstyle>";
- },
-
- /**
- * Return closing root XML tag.
- * @return {String} to append after all results
- */
- endFormat: function() {
- return "</checkstyle>";
- },
-
- /**
- * Returns message when there is a file read error.
- * @param {String} filename The name of the file that caused the error.
- * @param {String} message The error message
- * @return {String} The error message.
- */
- readError: function(filename, message) {
- return "<file name=\"" + xmlEscape(filename) + "\"><error line=\"0\" column=\"0\" severty=\"error\" message=\"" + xmlEscape(message) + "\"></error></file>";
- },
-
- /**
- * Given CSS Lint results for a file, return output for this format.
- * @param results {Object} with error and warning messages
- * @param filename {String} relative file path
- * @param options {Object} (UNUSED for now) specifies special handling of output
- * @return {String} output for results
- */
- formatResults: function(results, filename/*, options*/) {
- var messages = results.messages,
- output = [];
-
- /**
- * Generate a source string for a rule.
- * Checkstyle source strings usually resemble Java class names e.g
- * net.csslint.SomeRuleName
- * @param {Object} rule
- * @return rule source as {String}
- */
- var generateSource = function(rule) {
- if (!rule || !("name" in rule)) {
- return "";
- }
- return "net.csslint." + rule.name.replace(/\s/g, "");
- };
-
-
- if (messages.length > 0) {
- output.push("<file name=\""+filename+"\">");
- CSSLint.Util.forEach(messages, function (message) {
- // ignore rollups for now
- if (!message.rollup) {
- output.push("<error line=\"" + message.line + "\" column=\"" + message.col + "\" severity=\"" + message.type + "\"" +
- " message=\"" + xmlEscape(message.message) + "\" source=\"" + generateSource(message.rule) +"\"/>");
- }
- });
- output.push("</file>");
- }
-
- return output.join("");
- }
- });
-
-}());
-
-CSSLint.addFormatter({
- // format information
- id: "compact",
- name: "Compact, 'porcelain' format",
-
- /**
- * Return content to be printed before all file results.
- * @return {String} to prepend before all results
- */
- startFormat: function() {
- "use strict";
- return "";
- },
-
- /**
- * Return content to be printed after all file results.
- * @return {String} to append after all results
- */
- endFormat: function() {
- "use strict";
- return "";
- },
-
- /**
- * Given CSS Lint results for a file, return output for this format.
- * @param results {Object} with error and warning messages
- * @param filename {String} relative file path
- * @param options {Object} (Optional) specifies special handling of output
- * @return {String} output for results
- */
- formatResults: function(results, filename, options) {
- "use strict";
- var messages = results.messages,
- output = "";
- options = options || {};
-
- /**
- * Capitalize and return given string.
- * @param str {String} to capitalize
- * @return {String} capitalized
- */
- var capitalize = function(str) {
- return str.charAt(0).toUpperCase() + str.slice(1);
- };
-
- if (messages.length === 0) {
- return options.quiet ? "" : filename + ": Lint Free!";
- }
-
- CSSLint.Util.forEach(messages, function(message) {
- if (message.rollup) {
- output += filename + ": " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
- } else {
- output += filename + ": line " + message.line +
- ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
- }
- });
-
- return output;
- }
-});
-
-CSSLint.addFormatter({
- // format information
- id: "csslint-xml",
- name: "CSSLint XML format",
-
- /**
- * Return opening root XML tag.
- * @return {String} to prepend before all results
- */
- startFormat: function() {
- "use strict";
- return "<?xml version=\"1.0\" encoding=\"utf-8\"?><csslint>";
- },
-
- /**
- * Return closing root XML tag.
- * @return {String} to append after all results
- */
- endFormat: function() {
- "use strict";
- return "</csslint>";
- },
-
- /**
- * Given CSS Lint results for a file, return output for this format.
- * @param results {Object} with error and warning messages
- * @param filename {String} relative file path
- * @param options {Object} (UNUSED for now) specifies special handling of output
- * @return {String} output for results
- */
- formatResults: function(results, filename/*, options*/) {
- "use strict";
- var messages = results.messages,
- output = [];
-
- /**
- * Replace special characters before write to output.
- *
- * Rules:
- * - single quotes is the escape sequence for double-quotes
- * - & is the escape sequence for &
- * - < is the escape sequence for <
- * - > is the escape sequence for >
- *
- * @param {String} message to escape
- * @return escaped message as {String}
- */
- var escapeSpecialCharacters = function(str) {
- if (!str || str.constructor !== String) {
- return "";
- }
- return str.replace(/"/g, "'").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
- };
-
- if (messages.length > 0) {
- output.push("<file name=\""+filename+"\">");
- CSSLint.Util.forEach(messages, function (message) {
- if (message.rollup) {
- output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
- } else {
- output.push("<issue line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
- " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
- }
- });
- output.push("</file>");
- }
-
- return output.join("");
- }
-});
-
-/* globals JSON: true */
-
-CSSLint.addFormatter({
- // format information
- id: "json",
- name: "JSON",
-
- /**
- * Return content to be printed before all file results.
- * @return {String} to prepend before all results
- */
- startFormat: function() {
- "use strict";
- this.json = [];
- return "";
- },
-
- /**
- * Return content to be printed after all file results.
- * @return {String} to append after all results
- */
- endFormat: function() {
- "use strict";
- var ret = "";
- if (this.json.length > 0) {
- if (this.json.length === 1) {
- ret = JSON.stringify(this.json[0]);
- } else {
- ret = JSON.stringify(this.json);
- }
- }
- return ret;
- },
-
- /**
- * Given CSS Lint results for a file, return output for this format.
- * @param results {Object} with error and warning messages
- * @param filename {String} relative file path (Unused)
- * @return {String} output for results
- */
- formatResults: function(results, filename, options) {
- "use strict";
- if (results.messages.length > 0 || !options.quiet) {
- this.json.push({
- filename: filename,
- messages: results.messages,
- stats: results.stats
- });
- }
- return "";
- }
-});
-
-CSSLint.addFormatter({
- // format information
- id: "junit-xml",
- name: "JUNIT XML format",
-
- /**
- * Return opening root XML tag.
- * @return {String} to prepend before all results
- */
- startFormat: function() {
- "use strict";
- return "<?xml version=\"1.0\" encoding=\"utf-8\"?><testsuites>";
- },
-
- /**
- * Return closing root XML tag.
- * @return {String} to append after all results
- */
- endFormat: function() {
- "use strict";
- return "</testsuites>";
- },
-
- /**
- * Given CSS Lint results for a file, return output for this format.
- * @param results {Object} with error and warning messages
- * @param filename {String} relative file path
- * @param options {Object} (UNUSED for now) specifies special handling of output
- * @return {String} output for results
- */
- formatResults: function(results, filename/*, options*/) {
- "use strict";
-
- var messages = results.messages,
- output = [],
- tests = {
- "error": 0,
- "failure": 0
- };
-
- /**
- * Generate a source string for a rule.
- * JUNIT source strings usually resemble Java class names e.g
- * net.csslint.SomeRuleName
- * @param {Object} rule
- * @return rule source as {String}
- */
- var generateSource = function(rule) {
- if (!rule || !("name" in rule)) {
- return "";
- }
- return "net.csslint." + rule.name.replace(/\s/g, "");
- };
-
- /**
- * Replace special characters before write to output.
- *
- * Rules:
- * - single quotes is the escape sequence for double-quotes
- * - < is the escape sequence for <
- * - > is the escape sequence for >
- *
- * @param {String} message to escape
- * @return escaped message as {String}
- */
- var escapeSpecialCharacters = function(str) {
-
- if (!str || str.constructor !== String) {
- return "";
- }
-
- return str.replace(/"/g, "'").replace(/</g, "<").replace(/>/g, ">");
-
- };
-
- if (messages.length > 0) {
-
- messages.forEach(function (message) {
-
- // since junit has no warning class
- // all issues as errors
- var type = message.type === "warning" ? "error" : message.type;
-
- // ignore rollups for now
- if (!message.rollup) {
-
- // build the test case separately, once joined
- // we'll add it to a custom array filtered by type
- output.push("<testcase time=\"0\" name=\"" + generateSource(message.rule) + "\">");
- output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\"><![CDATA[" + message.line + ":" + message.col + ":" + escapeSpecialCharacters(message.evidence) + "]]></" + type + ">");
- output.push("</testcase>");
-
- tests[type] += 1;
-
- }
-
- });
-
- output.unshift("<testsuite time=\"0\" tests=\"" + messages.length + "\" skipped=\"0\" errors=\"" + tests.error + "\" failures=\"" + tests.failure + "\" package=\"net.csslint\" name=\"" + filename + "\">");
- output.push("</testsuite>");
-
- }
-
- return output.join("");
-
- }
-});
-
-CSSLint.addFormatter({
- // format information
- id: "lint-xml",
- name: "Lint XML format",
-
- /**
- * Return opening root XML tag.
- * @return {String} to prepend before all results
- */
- startFormat: function() {
- "use strict";
- return "<?xml version=\"1.0\" encoding=\"utf-8\"?><lint>";
- },
-
- /**
- * Return closing root XML tag.
- * @return {String} to append after all results
- */
- endFormat: function() {
- "use strict";
- return "</lint>";
- },
-
- /**
- * Given CSS Lint results for a file, return output for this format.
- * @param results {Object} with error and warning messages
- * @param filename {String} relative file path
- * @param options {Object} (UNUSED for now) specifies special handling of output
- * @return {String} output for results
- */
- formatResults: function(results, filename/*, options*/) {
- "use strict";
- var messages = results.messages,
- output = [];
-
- /**
- * Replace special characters before write to output.
- *
- * Rules:
- * - single quotes is the escape sequence for double-quotes
- * - & is the escape sequence for &
- * - < is the escape sequence for <
- * - > is the escape sequence for >
- *
- * @param {String} message to escape
- * @return escaped message as {String}
- */
- var escapeSpecialCharacters = function(str) {
- if (!str || str.constructor !== String) {
- return "";
- }
- return str.replace(/"/g, "'").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
- };
-
- if (messages.length > 0) {
-
- output.push("<file name=\""+filename+"\">");
- CSSLint.Util.forEach(messages, function (message) {
- if (message.rollup) {
- output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
- } else {
- var rule = "";
- if (message.rule && message.rule.id) {
- rule = "rule=\"" + escapeSpecialCharacters(message.rule.id) + "\" ";
- }
- output.push("<issue " + rule + "line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
- " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
- }
- });
- output.push("</file>");
- }
-
- return output.join("");
- }
-});
-
-CSSLint.addFormatter({
- // format information
- id: "text",
- name: "Plain Text",
-
- /**
- * Return content to be printed before all file results.
- * @return {String} to prepend before all results
- */
- startFormat: function() {
- "use strict";
- return "";
- },
-
- /**
- * Return content to be printed after all file results.
- * @return {String} to append after all results
- */
- endFormat: function() {
- "use strict";
- return "";
- },
-
- /**
- * Given CSS Lint results for a file, return output for this format.
- * @param results {Object} with error and warning messages
- * @param filename {String} relative file path
- * @param options {Object} (Optional) specifies special handling of output
- * @return {String} output for results
- */
- formatResults: function(results, filename, options) {
- "use strict";
- var messages = results.messages,
- output = "";
- options = options || {};
-
- if (messages.length === 0) {
- return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + ".";
- }
-
- output = "\n\ncsslint: There ";
- if (messages.length === 1) {
- output += "is 1 problem";
- } else {
- output += "are " + messages.length + " problems";
- }
- output += " in " + filename + ".";
-
- var pos = filename.lastIndexOf("/"),
- shortFilename = filename;
-
- if (pos === -1) {
- pos = filename.lastIndexOf("\\");
- }
- if (pos > -1) {
- shortFilename = filename.substring(pos+1);
- }
-
- CSSLint.Util.forEach(messages, function (message, i) {
- output = output + "\n\n" + shortFilename;
- if (message.rollup) {
- output += "\n" + (i+1) + ": " + message.type;
- output += "\n" + message.message;
- } else {
- output += "\n" + (i+1) + ": " + message.type + " at line " + message.line + ", col " + message.col;
- output += "\n" + message.message;
- output += "\n" + message.evidence;
- }
- });
-
- return output;
- }
-});
-
-return CSSLint;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+},{"./css":22,"./util":28}]},{},[]);
+
+return require('parserlib');
+})();
+var clone = (function() {
+'use strict';
+
+var nativeMap;
+try {
+ nativeMap = Map;
+} catch(_) {
+ // maybe a reference error because no `Map`. Give it a dummy value that no
+ // value will ever be an instanceof.
+ nativeMap = function() {};
+}
+
+var nativeSet;
+try {
+ nativeSet = Set;
+} catch(_) {
+ nativeSet = function() {};
+}
+
+var nativePromise;
+try {
+ nativePromise = Promise;
+} catch(_) {
+ nativePromise = function() {};
+}
+
+/**
+ * Clones (copies) an Object using deep copying.
+ *
+ * This function supports circular references by default, but if you are certain
+ * there are no circular references in your object, you can save some CPU time
+ * by calling clone(obj, false).
+ *
+ * Caution: if `circular` is false and `parent` contains circular references,
+ * your program may enter an infinite loop and crash.
+ *
+ * @param `parent` - the object to be cloned
+ * @param `circular` - set to true if the object to be cloned may contain
+ * circular references. (optional - true by default)
+ * @param `depth` - set to a number if the object is only to be cloned to
+ * a particular depth. (optional - defaults to Infinity)
+ * @param `prototype` - sets the prototype to be used when cloning an object.
+ * (optional - defaults to parent prototype).
+ * @param `includeNonEnumerable` - set to true if the non-enumerable properties
+ * should be cloned as well. Non-enumerable properties on the prototype
+ * chain will be ignored. (optional - false by default)
+*/
+function clone(parent, circular, depth, prototype, includeNonEnumerable) {
+ if (typeof circular === 'object') {
+ depth = circular.depth;
+ prototype = circular.prototype;
+ includeNonEnumerable = circular.includeNonEnumerable;
+ circular = circular.circular;
+ }
+ // maintain two arrays for circular references, where corresponding parents
+ // and children have the same index
+ var allParents = [];
+ var allChildren = [];
+
+ var useBuffer = typeof Buffer != 'undefined';
+
+ if (typeof circular == 'undefined')
+ circular = true;
+
+ if (typeof depth == 'undefined')
+ depth = Infinity;
+
+ // recurse this function so we don't reset allParents and allChildren
+ function _clone(parent, depth) {
+ // cloning null always returns null
+ if (parent === null)
+ return null;
+
+ if (depth === 0)
+ return parent;
+
+ var child;
+ var proto;
+ if (typeof parent != 'object') {
+ return parent;
+ }
+
+ if (parent instanceof nativeMap) {
+ child = new nativeMap();
+ } else if (parent instanceof nativeSet) {
+ child = new nativeSet();
+ } else if (parent instanceof nativePromise) {
+ child = new nativePromise(function (resolve, reject) {
+ parent.then(function(value) {
+ resolve(_clone(value, depth - 1));
+ }, function(err) {
+ reject(_clone(err, depth - 1));
+ });
+ });
+ } else if (clone.__isArray(parent)) {
+ child = [];
+ } else if (clone.__isRegExp(parent)) {
+ child = new RegExp(parent.source, __getRegExpFlags(parent));
+ if (parent.lastIndex) child.lastIndex = parent.lastIndex;
+ } else if (clone.__isDate(parent)) {
+ child = new Date(parent.getTime());
+ } else if (useBuffer && Buffer.isBuffer(parent)) {
+ child = new Buffer(parent.length);
+ parent.copy(child);
+ return child;
+ } else if (parent instanceof Error) {
+ child = Object.create(parent);
+ } else {
+ if (typeof prototype == 'undefined') {
+ proto = Object.getPrototypeOf(parent);
+ child = Object.create(proto);
+ }
+ else {
+ child = Object.create(prototype);
+ proto = prototype;
+ }
+ }
+
+ if (circular) {
+ var index = allParents.indexOf(parent);
+
+ if (index != -1) {
+ return allChildren[index];
+ }
+ allParents.push(parent);
+ allChildren.push(child);
+ }
+
+ if (parent instanceof nativeMap) {
+ var keyIterator = parent.keys();
+ while(true) {
+ var next = keyIterator.next();
+ if (next.done) {
+ break;
+ }
+ var keyChild = _clone(next.value, depth - 1);
+ var valueChild = _clone(parent.get(next.value), depth - 1);
+ child.set(keyChild, valueChild);
+ }
+ }
+ if (parent instanceof nativeSet) {
+ var iterator = parent.keys();
+ while(true) {
+ var next = iterator.next();
+ if (next.done) {
+ break;
+ }
+ var entryChild = _clone(next.value, depth - 1);
+ child.add(entryChild);
+ }
+ }
+
+ for (var i in parent) {
+ var attrs;
+ if (proto) {
+ attrs = Object.getOwnPropertyDescriptor(proto, i);
+ }
+
+ if (attrs && attrs.set == null) {
+ continue;
+ }
+ child[i] = _clone(parent[i], depth - 1);
+ }
+
+ if (Object.getOwnPropertySymbols) {
+ var symbols = Object.getOwnPropertySymbols(parent);
+ for (var i = 0; i < symbols.length; i++) {
+ // Don't need to worry about cloning a symbol because it is a primitive,
+ // like a number or string.
+ var symbol = symbols[i];
+ var descriptor = Object.getOwnPropertyDescriptor(parent, symbol);
+ if (descriptor && !descriptor.enumerable && !includeNonEnumerable) {
+ continue;
+ }
+ child[symbol] = _clone(parent[symbol], depth - 1);
+ if (!descriptor.enumerable) {
+ Object.defineProperty(child, symbol, {
+ enumerable: false
+ });
+ }
+ }
+ }
+
+ if (includeNonEnumerable) {
+ var allPropertyNames = Object.getOwnPropertyNames(parent);
+ for (var i = 0; i < allPropertyNames.length; i++) {
+ var propertyName = allPropertyNames[i];
+ var descriptor = Object.getOwnPropertyDescriptor(parent, propertyName);
+ if (descriptor && descriptor.enumerable) {
+ continue;
+ }
+ child[propertyName] = _clone(parent[propertyName], depth - 1);
+ Object.defineProperty(child, propertyName, {
+ enumerable: false
+ });
+ }
+ }
+
+ return child;
+ }
+
+ return _clone(parent, depth);
+}
+
+/**
+ * Simple flat clone using prototype, accepts only objects, usefull for property
+ * override on FLAT configuration object (no nested props).
+ *
+ * USE WITH CAUTION! This may not behave as you wish if you do not know how this
+ * works.
+ */
+clone.clonePrototype = function clonePrototype(parent) {
+ if (parent === null)
+ return null;
+
+ var c = function () {};
+ c.prototype = parent;
+ return new c();
+};
+
+// private utility functions
+
+function __objToStr(o) {
+ return Object.prototype.toString.call(o);
+}
+clone.__objToStr = __objToStr;
+
+function __isDate(o) {
+ return typeof o === 'object' && __objToStr(o) === '[object Date]';
+}
+clone.__isDate = __isDate;
+
+function __isArray(o) {
+ return typeof o === 'object' && __objToStr(o) === '[object Array]';
+}
+clone.__isArray = __isArray;
+
+function __isRegExp(o) {
+ return typeof o === 'object' && __objToStr(o) === '[object RegExp]';
+}
+clone.__isRegExp = __isRegExp;
+
+function __getRegExpFlags(re) {
+ var flags = '';
+ if (re.global) flags += 'g';
+ if (re.ignoreCase) flags += 'i';
+ if (re.multiline) flags += 'm';
+ return flags;
+}
+clone.__getRegExpFlags = __getRegExpFlags;
+
+return clone;
+})();
+
+if (typeof module === 'object' && module.exports) {
+ module.exports = clone;
+}
+
+/**
+ * Main CSSLint object.
+ * @class CSSLint
+ * @static
+ * @extends parserlib.util.EventTarget
+ */
+
+/* global parserlib, clone, Reporter */
+/* exported CSSLint */
+
+var CSSLint = (function() {
+ "use strict";
+
+ var rules = [],
+ formatters = [],
+ embeddedRuleset = /\/\*\s*csslint([^\*]*)\*\//,
+ api = new parserlib.util.EventTarget();
+
+ api.version = "1.0.4";
+
+ //-------------------------------------------------------------------------
+ // Rule Management
+ //-------------------------------------------------------------------------
+
+ /**
+ * Adds a new rule to the engine.
+ * @param {Object} rule The rule to add.
+ * @method addRule
+ */
+ api.addRule = function(rule) {
+ rules.push(rule);
+ rules[rule.id] = rule;
+ };
+
+ /**
+ * Clears all rule from the engine.
+ * @method clearRules
+ */
+ api.clearRules = function() {
+ rules = [];
+ };
+
+ /**
+ * Returns the rule objects.
+ * @return An array of rule objects.
+ * @method getRules
+ */
+ api.getRules = function() {
+ return [].concat(rules).sort(function(a, b) {
+ return a.id > b.id ? 1 : 0;
+ });
+ };
+
+ /**
+ * Returns a ruleset configuration object with all current rules.
+ * @return A ruleset object.
+ * @method getRuleset
+ */
+ api.getRuleset = function() {
+ var ruleset = {},
+ i = 0,
+ len = rules.length;
+
+ while (i < len) {
+ ruleset[rules[i++].id] = 1; // by default, everything is a warning
+ }
+
+ return ruleset;
+ };
+
+ /**
+ * Returns a ruleset object based on embedded rules.
+ * @param {String} text A string of css containing embedded rules.
+ * @param {Object} ruleset A ruleset object to modify.
+ * @return {Object} A ruleset object.
+ * @method getEmbeddedRuleset
+ */
+ function applyEmbeddedRuleset(text, ruleset) {
+ var valueMap,
+ embedded = text && text.match(embeddedRuleset),
+ rules = embedded && embedded[1];
+
+ if (rules) {
+ valueMap = {
+ "true": 2, // true is error
+ "": 1, // blank is warning
+ "false": 0, // false is ignore
+
+ "2": 2, // explicit error
+ "1": 1, // explicit warning
+ "0": 0 // explicit ignore
+ };
+
+ rules.toLowerCase().split(",").forEach(function(rule) {
+ var pair = rule.split(":"),
+ property = pair[0] || "",
+ value = pair[1] || "";
+
+ ruleset[property.trim()] = valueMap[value.trim()];
+ });
+ }
+
+ return ruleset;
+ }
+
+ //-------------------------------------------------------------------------
+ // Formatters
+ //-------------------------------------------------------------------------
+
+ /**
+ * Adds a new formatter to the engine.
+ * @param {Object} formatter The formatter to add.
+ * @method addFormatter
+ */
+ api.addFormatter = function(formatter) {
+ // formatters.push(formatter);
+ formatters[formatter.id] = formatter;
+ };
+
+ /**
+ * Retrieves a formatter for use.
+ * @param {String} formatId The name of the format to retrieve.
+ * @return {Object} The formatter or undefined.
+ * @method getFormatter
+ */
+ api.getFormatter = function(formatId) {
+ return formatters[formatId];
+ };
+
+ /**
+ * Formats the results in a particular format for a single file.
+ * @param {Object} result The results returned from CSSLint.verify().
+ * @param {String} filename The filename for which the results apply.
+ * @param {String} formatId The name of the formatter to use.
+ * @param {Object} options (Optional) for special output handling.
+ * @return {String} A formatted string for the results.
+ * @method format
+ */
+ api.format = function(results, filename, formatId, options) {
+ var formatter = this.getFormatter(formatId),
+ result = null;
+
+ if (formatter) {
+ result = formatter.startFormat();
+ result += formatter.formatResults(results, filename, options || {});
+ result += formatter.endFormat();
+ }
+
+ return result;
+ };
+
+ /**
+ * Indicates if the given format is supported.
+ * @param {String} formatId The ID of the format to check.
+ * @return {Boolean} True if the format exists, false if not.
+ * @method hasFormat
+ */
+ api.hasFormat = function(formatId) {
+ return formatters.hasOwnProperty(formatId);
+ };
+
+ //-------------------------------------------------------------------------
+ // Verification
+ //-------------------------------------------------------------------------
+
+ /**
+ * Starts the verification process for the given CSS text.
+ * @param {String} text The CSS text to verify.
+ * @param {Object} ruleset (Optional) List of rules to apply. If null, then
+ * all rules are used. If a rule has a value of 1 then it's a warning,
+ * a value of 2 means it's an error.
+ * @return {Object} Results of the verification.
+ * @method verify
+ */
+ api.verify = function(text, ruleset) {
+
+ var i = 0,
+ reporter,
+ lines,
+ allow = {},
+ ignore = [],
+ report,
+ parser = new parserlib.css.Parser({
+ starHack: true,
+ ieFilters: true,
+ underscoreHack: true,
+ strict: false
+ });
+
+ // normalize line endings
+ lines = text.replace(/\n\r?/g, "$split$").split("$split$");
+
+ // find 'allow' comments
+ CSSLint.Util.forEach(lines, function (line, lineno) {
+ var allowLine = line && line.match(/\/\*[ \t]*csslint[ \t]+allow:[ \t]*([^\*]*)\*\//i),
+ allowRules = allowLine && allowLine[1],
+ allowRuleset = {};
+
+ if (allowRules) {
+ allowRules.toLowerCase().split(",").forEach(function(allowRule) {
+ allowRuleset[allowRule.trim()] = true;
+ });
+ if (Object.keys(allowRuleset).length > 0) {
+ allow[lineno + 1] = allowRuleset;
+ }
+ }
+ });
+
+ var ignoreStart = null,
+ ignoreEnd = null;
+ CSSLint.Util.forEach(lines, function (line, lineno) {
+ // Keep oldest, "unclosest" ignore:start
+ if (ignoreStart === null && line.match(/\/\*[ \t]*csslint[ \t]+ignore:start[ \t]*\*\//i)) {
+ ignoreStart = lineno;
+ }
+
+ if (line.match(/\/\*[ \t]*csslint[ \t]+ignore:end[ \t]*\*\//i)) {
+ ignoreEnd = lineno;
+ }
+
+ if (ignoreStart !== null && ignoreEnd !== null) {
+ ignore.push([ignoreStart, ignoreEnd]);
+ ignoreStart = ignoreEnd = null;
+ }
+ });
+
+ // Close remaining ignore block, if any
+ if (ignoreStart !== null) {
+ ignore.push([ignoreStart, lines.length]);
+ }
+
+ if (!ruleset) {
+ ruleset = this.getRuleset();
+ }
+
+ if (embeddedRuleset.test(text)) {
+ // defensively copy so that caller's version does not get modified
+ ruleset = clone(ruleset);
+ ruleset = applyEmbeddedRuleset(text, ruleset);
+ }
+
+ reporter = new Reporter(lines, ruleset, allow, ignore);
+
+ ruleset.errors = 2; // always report parsing errors as errors
+ for (i in ruleset) {
+ if (ruleset.hasOwnProperty(i) && ruleset[i]) {
+ if (rules[i]) {
+ rules[i].init(parser, reporter);
+ }
+ }
+ }
+
+
+ // capture most horrible error type
+ try {
+ parser.parse(text);
+ } catch (ex) {
+ reporter.error("Fatal error, cannot continue: " + ex.message, ex.line, ex.col, {});
+ }
+
+ report = {
+ messages : reporter.messages,
+ stats : reporter.stats,
+ ruleset : reporter.ruleset,
+ allow : reporter.allow,
+ ignore : reporter.ignore
+ };
+
+ // sort by line numbers, rollups at the bottom
+ report.messages.sort(function (a, b) {
+ if (a.rollup && !b.rollup) {
+ return 1;
+ } else if (!a.rollup && b.rollup) {
+ return -1;
+ } else {
+ return a.line - b.line;
+ }
+ });
+
+ return report;
+ };
+
+ //-------------------------------------------------------------------------
+ // Publish the API
+ //-------------------------------------------------------------------------
+
+ return api;
+
+})();
+
+/**
+ * An instance of Report is used to report results of the
+ * verification back to the main API.
+ * @class Reporter
+ * @constructor
+ * @param {String[]} lines The text lines of the source.
+ * @param {Object} ruleset The set of rules to work with, including if
+ * they are errors or warnings.
+ * @param {Object} explicitly allowed lines
+ * @param {[][]} ingore list of line ranges to be ignored
+ */
+function Reporter(lines, ruleset, allow, ignore) {
+ "use strict";
+
+ /**
+ * List of messages being reported.
+ * @property messages
+ * @type String[]
+ */
+ this.messages = [];
+
+ /**
+ * List of statistics being reported.
+ * @property stats
+ * @type String[]
+ */
+ this.stats = [];
+
+ /**
+ * Lines of code being reported on. Used to provide contextual information
+ * for messages.
+ * @property lines
+ * @type String[]
+ */
+ this.lines = lines;
+
+ /**
+ * Information about the rules. Used to determine whether an issue is an
+ * error or warning.
+ * @property ruleset
+ * @type Object
+ */
+ this.ruleset = ruleset;
+
+ /**
+ * Lines with specific rule messages to leave out of the report.
+ * @property allow
+ * @type Object
+ */
+ this.allow = allow;
+ if (!this.allow) {
+ this.allow = {};
+ }
+
+ /**
+ * Linesets not to include in the report.
+ * @property ignore
+ * @type [][]
+ */
+ this.ignore = ignore;
+ if (!this.ignore) {
+ this.ignore = [];
+ }
+}
+
+Reporter.prototype = {
+
+ // restore constructor
+ constructor: Reporter,
+
+ /**
+ * Report an error.
+ * @param {String} message The message to store.
+ * @param {int} line The line number.
+ * @param {int} col The column number.
+ * @param {Object} rule The rule this message relates to.
+ * @method error
+ */
+ error: function(message, line, col, rule) {
+ "use strict";
+ this.messages.push({
+ type : "error",
+ line : line,
+ col : col,
+ message : message,
+ evidence: this.lines[line-1],
+ rule : rule || {}
+ });
+ },
+
+ /**
+ * Report an warning.
+ * @param {String} message The message to store.
+ * @param {int} line The line number.
+ * @param {int} col The column number.
+ * @param {Object} rule The rule this message relates to.
+ * @method warn
+ * @deprecated Use report instead.
+ */
+ warn: function(message, line, col, rule) {
+ "use strict";
+ this.report(message, line, col, rule);
+ },
+
+ /**
+ * Report an issue.
+ * @param {String} message The message to store.
+ * @param {int} line The line number.
+ * @param {int} col The column number.
+ * @param {Object} rule The rule this message relates to.
+ * @method report
+ */
+ report: function(message, line, col, rule) {
+ "use strict";
+
+ // Check if rule violation should be allowed
+ if (this.allow.hasOwnProperty(line) && this.allow[line].hasOwnProperty(rule.id)) {
+ return;
+ }
+
+ var ignore = false;
+ CSSLint.Util.forEach(this.ignore, function (range) {
+ if (range[0] <= line && line <= range[1]) {
+ ignore = true;
+ }
+ });
+ if (ignore) {
+ return;
+ }
+
+ this.messages.push({
+ type : this.ruleset[rule.id] === 2 ? "error" : "warning",
+ line : line,
+ col : col,
+ message : message,
+ evidence: this.lines[line-1],
+ rule : rule
+ });
+ },
+
+ /**
+ * Report some informational text.
+ * @param {String} message The message to store.
+ * @param {int} line The line number.
+ * @param {int} col The column number.
+ * @param {Object} rule The rule this message relates to.
+ * @method info
+ */
+ info: function(message, line, col, rule) {
+ "use strict";
+ this.messages.push({
+ type : "info",
+ line : line,
+ col : col,
+ message : message,
+ evidence: this.lines[line-1],
+ rule : rule
+ });
+ },
+
+ /**
+ * Report some rollup error information.
+ * @param {String} message The message to store.
+ * @param {Object} rule The rule this message relates to.
+ * @method rollupError
+ */
+ rollupError: function(message, rule) {
+ "use strict";
+ this.messages.push({
+ type : "error",
+ rollup : true,
+ message : message,
+ rule : rule
+ });
+ },
+
+ /**
+ * Report some rollup warning information.
+ * @param {String} message The message to store.
+ * @param {Object} rule The rule this message relates to.
+ * @method rollupWarn
+ */
+ rollupWarn: function(message, rule) {
+ "use strict";
+ this.messages.push({
+ type : "warning",
+ rollup : true,
+ message : message,
+ rule : rule
+ });
+ },
+
+ /**
+ * Report a statistic.
+ * @param {String} name The name of the stat to store.
+ * @param {Variant} value The value of the stat.
+ * @method stat
+ */
+ stat: function(name, value) {
+ "use strict";
+ this.stats[name] = value;
+ }
+};
+
+// expose for testing purposes
+CSSLint._Reporter = Reporter;
+
+/*
+ * Utility functions that make life easier.
+ */
+CSSLint.Util = {
+ /*
+ * Adds all properties from supplier onto receiver,
+ * overwriting if the same name already exists on
+ * receiver.
+ * @param {Object} The object to receive the properties.
+ * @param {Object} The object to provide the properties.
+ * @return {Object} The receiver
+ */
+ mix: function(receiver, supplier) {
+ "use strict";
+ var prop;
+
+ for (prop in supplier) {
+ if (supplier.hasOwnProperty(prop)) {
+ receiver[prop] = supplier[prop];
+ }
+ }
+
+ return prop;
+ },
+
+ /*
+ * Polyfill for array indexOf() method.
+ * @param {Array} values The array to search.
+ * @param {Variant} value The value to search for.
+ * @return {int} The index of the value if found, -1 if not.
+ */
+ indexOf: function(values, value) {
+ "use strict";
+ if (values.indexOf) {
+ return values.indexOf(value);
+ } else {
+ for (var i=0, len=values.length; i < len; i++) {
+ if (values[i] === value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+ },
+
+ /*
+ * Polyfill for array forEach() method.
+ * @param {Array} values The array to operate on.
+ * @param {Function} func The function to call on each item.
+ * @return {void}
+ */
+ forEach: function(values, func) {
+ "use strict";
+ if (values.forEach) {
+ return values.forEach(func);
+ } else {
+ for (var i=0, len=values.length; i < len; i++) {
+ func(values[i], i, values);
+ }
+ }
+ }
+};
+
+/*
+ * Rule: Don't use adjoining classes (.foo.bar).
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "adjoining-classes",
+ name: "Disallow adjoining classes",
+ desc: "Don't use adjoining classes.",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-adjoining-classes",
+ browsers: "IE6",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this;
+ parser.addListener("startrule", function(event) {
+ var selectors = event.selectors,
+ selector,
+ part,
+ modifier,
+ classCount,
+ i, j, k;
+
+ for (i=0; i < selectors.length; i++) {
+ selector = selectors[i];
+ for (j=0; j < selector.parts.length; j++) {
+ part = selector.parts[j];
+ if (part.type === parser.SELECTOR_PART_TYPE) {
+ classCount = 0;
+ for (k=0; k < part.modifiers.length; k++) {
+ modifier = part.modifiers[k];
+ if (modifier.type === "class") {
+ classCount++;
+ }
+ if (classCount > 1){
+ reporter.report("Adjoining classes: "+selectors[i].text, part.line, part.col, rule);
+ }
+ }
+ }
+ }
+ }
+ });
+ }
+
+});
+
+/*
+ * Rule: Don't use width or height when using padding or border.
+ */
+CSSLint.addRule({
+
+ // rule information
+ id: "box-model",
+ name: "Beware of broken box size",
+ desc: "Don't use width or height when using padding or border.",
+ url: "https://github.com/CSSLint/csslint/wiki/Beware-of-box-model-size",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this,
+ widthProperties = {
+ border: 1,
+ "border-left": 1,
+ "border-right": 1,
+ padding: 1,
+ "padding-left": 1,
+ "padding-right": 1
+ },
+ heightProperties = {
+ border: 1,
+ "border-bottom": 1,
+ "border-top": 1,
+ padding: 1,
+ "padding-bottom": 1,
+ "padding-top": 1
+ },
+ properties,
+ boxSizing = false;
+
+ function startRule() {
+ properties = {};
+ boxSizing = false;
+ }
+
+ function endRule() {
+ var prop, value;
+
+ if (!boxSizing) {
+ if (properties.height) {
+ for (prop in heightProperties) {
+ if (heightProperties.hasOwnProperty(prop) && properties[prop]) {
+ value = properties[prop].value;
+ // special case for padding
+ if (!(prop === "padding" && value.parts.length === 2 && value.parts[0].value === 0)) {
+ reporter.report("Using height with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
+ }
+ }
+ }
+ }
+
+ if (properties.width) {
+ for (prop in widthProperties) {
+ if (widthProperties.hasOwnProperty(prop) && properties[prop]) {
+ value = properties[prop].value;
+
+ if (!(prop === "padding" && value.parts.length === 2 && value.parts[1].value === 0)) {
+ reporter.report("Using width with " + prop + " can sometimes make elements larger than you expect.", properties[prop].line, properties[prop].col, rule);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ parser.addListener("startrule", startRule);
+ parser.addListener("startfontface", startRule);
+ parser.addListener("startpage", startRule);
+ parser.addListener("startpagemargin", startRule);
+ parser.addListener("startkeyframerule", startRule);
+ parser.addListener("startviewport", startRule);
+
+ parser.addListener("property", function(event) {
+ var name = event.property.text.toLowerCase();
+
+ if (heightProperties[name] || widthProperties[name]) {
+ if (!/^0\S*$/.test(event.value) && !(name === "border" && event.value.toString() === "none")) {
+ properties[name] = {
+ line: event.property.line,
+ col: event.property.col,
+ value: event.value
+ };
+ }
+ } else {
+ if (/^(width|height)/i.test(name) && /^(length|percentage)/.test(event.value.parts[0].type)) {
+ properties[name] = 1;
+ } else if (name === "box-sizing") {
+ boxSizing = true;
+ }
+ }
+
+ });
+
+ parser.addListener("endrule", endRule);
+ parser.addListener("endfontface", endRule);
+ parser.addListener("endpage", endRule);
+ parser.addListener("endpagemargin", endRule);
+ parser.addListener("endkeyframerule", endRule);
+ parser.addListener("endviewport", endRule);
+ }
+
+});
+
+/*
+ * Rule: box-sizing doesn't work in IE6 and IE7.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "box-sizing",
+ name: "Disallow use of box-sizing",
+ desc: "The box-sizing properties isn't supported in IE6 and IE7.",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-box-sizing",
+ browsers: "IE6, IE7",
+ tags: ["Compatibility"],
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this;
+
+ parser.addListener("property", function(event) {
+ var name = event.property.text.toLowerCase();
+
+ if (name === "box-sizing") {
+ reporter.report("The box-sizing property isn't supported in IE6 and IE7.", event.line, event.col, rule);
+ }
+ });
+ }
+
+});
+
+/*
+ * Rule: Use the bulletproof @font-face syntax to avoid 404's in old IE
+ * (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax)
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "bulletproof-font-face",
+ name: "Use the bulletproof @font-face syntax",
+ 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).",
+ url: "https://github.com/CSSLint/csslint/wiki/Bulletproof-font-face",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this,
+ fontFaceRule = false,
+ firstSrc = true,
+ ruleFailed = false,
+ line, col;
+
+ // Mark the start of a @font-face declaration so we only test properties inside it
+ parser.addListener("startfontface", function() {
+ fontFaceRule = true;
+ });
+
+ parser.addListener("property", function(event) {
+ // If we aren't inside an @font-face declaration then just return
+ if (!fontFaceRule) {
+ return;
+ }
+
+ var propertyName = event.property.toString().toLowerCase(),
+ value = event.value.toString();
+
+ // Set the line and col numbers for use in the endfontface listener
+ line = event.line;
+ col = event.col;
+
+ // This is the property that we care about, we can ignore the rest
+ if (propertyName === "src") {
+ var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i;
+
+ // We need to handle the advanced syntax with two src properties
+ if (!value.match(regex) && firstSrc) {
+ ruleFailed = true;
+ firstSrc = false;
+ } else if (value.match(regex) && !firstSrc) {
+ ruleFailed = false;
+ }
+ }
+
+
+ });
+
+ // Back to normal rules that we don't need to test
+ parser.addListener("endfontface", function() {
+ fontFaceRule = false;
+
+ if (ruleFailed) {
+ reporter.report("@font-face declaration doesn't follow the fontspring bulletproof syntax.", line, col, rule);
+ }
+ });
+ }
+});
+
+/*
+ * Rule: Include all compatible vendor prefixes to reach a wider
+ * range of users.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "compatible-vendor-prefixes",
+ name: "Require compatible vendor prefixes",
+ desc: "Include all compatible vendor prefixes to reach a wider range of users.",
+ url: "https://github.com/CSSLint/csslint/wiki/Require-compatible-vendor-prefixes",
+ browsers: "All",
+
+ // initialization
+ init: function (parser, reporter) {
+ "use strict";
+ var rule = this,
+ compatiblePrefixes,
+ properties,
+ prop,
+ variations,
+ prefixed,
+ i,
+ len,
+ inKeyFrame = false,
+ arrayPush = Array.prototype.push,
+ applyTo = [];
+
+ // See http://peter.sh/experiments/vendor-prefixed-css-property-overview/ for details
+ compatiblePrefixes = {
+ "animation" : "webkit",
+ "animation-delay" : "webkit",
+ "animation-direction" : "webkit",
+ "animation-duration" : "webkit",
+ "animation-fill-mode" : "webkit",
+ "animation-iteration-count" : "webkit",
+ "animation-name" : "webkit",
+ "animation-play-state" : "webkit",
+ "animation-timing-function" : "webkit",
+ "appearance" : "webkit moz",
+ "border-end" : "webkit moz",
+ "border-end-color" : "webkit moz",
+ "border-end-style" : "webkit moz",
+ "border-end-width" : "webkit moz",
+ "border-image" : "webkit moz o",
+ "border-radius" : "webkit",
+ "border-start" : "webkit moz",
+ "border-start-color" : "webkit moz",
+ "border-start-style" : "webkit moz",
+ "border-start-width" : "webkit moz",
+ "box-align" : "webkit moz ms",
+ "box-direction" : "webkit moz ms",
+ "box-flex" : "webkit moz ms",
+ "box-lines" : "webkit ms",
+ "box-ordinal-group" : "webkit moz ms",
+ "box-orient" : "webkit moz ms",
+ "box-pack" : "webkit moz ms",
+ "box-sizing" : "",
+ "box-shadow" : "",
+ "column-count" : "webkit moz ms",
+ "column-gap" : "webkit moz ms",
+ "column-rule" : "webkit moz ms",
+ "column-rule-color" : "webkit moz ms",
+ "column-rule-style" : "webkit moz ms",
+ "column-rule-width" : "webkit moz ms",
+ "column-width" : "webkit moz ms",
+ "hyphens" : "epub moz",
+ "line-break" : "webkit ms",
+ "margin-end" : "webkit moz",
+ "margin-start" : "webkit moz",
+ "marquee-speed" : "webkit wap",
+ "marquee-style" : "webkit wap",
+ "padding-end" : "webkit moz",
+ "padding-start" : "webkit moz",
+ "tab-size" : "moz o",
+ "text-size-adjust" : "webkit ms",
+ "transform" : "webkit ms",
+ "transform-origin" : "webkit ms",
+ "transition" : "",
+ "transition-delay" : "",
+ "transition-duration" : "",
+ "transition-property" : "",
+ "transition-timing-function" : "",
+ "user-modify" : "webkit moz",
+ "user-select" : "webkit moz ms",
+ "word-break" : "epub ms",
+ "writing-mode" : "epub ms"
+ };
+
+
+ for (prop in compatiblePrefixes) {
+ if (compatiblePrefixes.hasOwnProperty(prop)) {
+ variations = [];
+ prefixed = compatiblePrefixes[prop].split(" ");
+ for (i = 0, len = prefixed.length; i < len; i++) {
+ variations.push("-" + prefixed[i] + "-" + prop);
+ }
+ compatiblePrefixes[prop] = variations;
+ arrayPush.apply(applyTo, variations);
+ }
+ }
+
+ parser.addListener("startrule", function () {
+ properties = [];
+ });
+
+ parser.addListener("startkeyframes", function (event) {
+ inKeyFrame = event.prefix || true;
+ });
+
+ parser.addListener("endkeyframes", function () {
+ inKeyFrame = false;
+ });
+
+ parser.addListener("property", function (event) {
+ var name = event.property;
+ if (CSSLint.Util.indexOf(applyTo, name.text) > -1) {
+
+ // e.g., -moz-transform is okay to be alone in @-moz-keyframes
+ if (!inKeyFrame || typeof inKeyFrame !== "string" ||
+ name.text.indexOf("-" + inKeyFrame + "-") !== 0) {
+ properties.push(name);
+ }
+ }
+ });
+
+ parser.addListener("endrule", function () {
+ if (!properties.length) {
+ return;
+ }
+
+ var propertyGroups = {},
+ i,
+ len,
+ name,
+ prop,
+ variations,
+ value,
+ full,
+ actual,
+ item,
+ propertiesSpecified;
+
+ for (i = 0, len = properties.length; i < len; i++) {
+ name = properties[i];
+
+ for (prop in compatiblePrefixes) {
+ if (compatiblePrefixes.hasOwnProperty(prop)) {
+ variations = compatiblePrefixes[prop];
+ if (CSSLint.Util.indexOf(variations, name.text) > -1) {
+ if (!propertyGroups[prop]) {
+ propertyGroups[prop] = {
+ full: variations.slice(0),
+ actual: [],
+ actualNodes: []
+ };
+ }
+ if (CSSLint.Util.indexOf(propertyGroups[prop].actual, name.text) === -1) {
+ propertyGroups[prop].actual.push(name.text);
+ propertyGroups[prop].actualNodes.push(name);
+ }
+ }
+ }
+ }
+ }
+
+ for (prop in propertyGroups) {
+ if (propertyGroups.hasOwnProperty(prop)) {
+ value = propertyGroups[prop];
+ full = value.full;
+ actual = value.actual;
+
+ if (full.length > actual.length) {
+ for (i = 0, len = full.length; i < len; i++) {
+ item = full[i];
+ if (CSSLint.Util.indexOf(actual, item) === -1) {
+ propertiesSpecified = (actual.length === 1) ? actual[0] : (actual.length === 2) ? actual.join(" and ") : actual.join(", ");
+ reporter.report("The property " + item + " is compatible with " + propertiesSpecified + " and should be included as well.", value.actualNodes[0].line, value.actualNodes[0].col, rule);
+ }
+ }
+
+ }
+ }
+ }
+ });
+ }
+});
+
+/*
+ * Rule: Certain properties don't play well with certain display values.
+ * - float should not be used with inline-block
+ * - height, width, margin-top, margin-bottom, float should not be used with inline
+ * - vertical-align should not be used with block
+ * - margin, float should not be used with table-*
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "display-property-grouping",
+ name: "Require properties appropriate for display",
+ desc: "Certain properties shouldn't be used with certain display property values.",
+ url: "https://github.com/CSSLint/csslint/wiki/Require-properties-appropriate-for-display",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this;
+
+ var propertiesToCheck = {
+ display: 1,
+ "float": "none",
+ height: 1,
+ width: 1,
+ margin: 1,
+ "margin-left": 1,
+ "margin-right": 1,
+ "margin-bottom": 1,
+ "margin-top": 1,
+ padding: 1,
+ "padding-left": 1,
+ "padding-right": 1,
+ "padding-bottom": 1,
+ "padding-top": 1,
+ "vertical-align": 1
+ },
+ properties;
+
+ function reportProperty(name, display, msg) {
+ if (properties[name]) {
+ if (typeof propertiesToCheck[name] !== "string" || properties[name].value.toLowerCase() !== propertiesToCheck[name]) {
+ reporter.report(msg || name + " can't be used with display: " + display + ".", properties[name].line, properties[name].col, rule);
+ }
+ }
+ }
+
+ function startRule() {
+ properties = {};
+ }
+
+ function endRule() {
+
+ var display = properties.display ? properties.display.value : null;
+ if (display) {
+ switch (display) {
+
+ case "inline":
+ // height, width, margin-top, margin-bottom, float should not be used with inline
+ reportProperty("height", display);
+ reportProperty("width", display);
+ reportProperty("margin", display);
+ reportProperty("margin-top", display);
+ reportProperty("margin-bottom", display);
+ reportProperty("float", display, "display:inline has no effect on floated elements (but may be used to fix the IE6 double-margin bug).");
+ break;
+
+ case "block":
+ // vertical-align should not be used with block
+ reportProperty("vertical-align", display);
+ break;
+
+ case "inline-block":
+ // float should not be used with inline-block
+ reportProperty("float", display);
+ break;
+
+ default:
+ // margin, float should not be used with table
+ if (display.indexOf("table-") === 0) {
+ reportProperty("margin", display);
+ reportProperty("margin-left", display);
+ reportProperty("margin-right", display);
+ reportProperty("margin-top", display);
+ reportProperty("margin-bottom", display);
+ reportProperty("float", display);
+ }
+
+ // otherwise do nothing
+ }
+ }
+
+ }
+
+ parser.addListener("startrule", startRule);
+ parser.addListener("startfontface", startRule);
+ parser.addListener("startkeyframerule", startRule);
+ parser.addListener("startpagemargin", startRule);
+ parser.addListener("startpage", startRule);
+ parser.addListener("startviewport", startRule);
+
+ parser.addListener("property", function(event) {
+ var name = event.property.text.toLowerCase();
+
+ if (propertiesToCheck[name]) {
+ properties[name] = {
+ value: event.value.text,
+ line: event.property.line,
+ col: event.property.col
+ };
+ }
+ });
+
+ parser.addListener("endrule", endRule);
+ parser.addListener("endfontface", endRule);
+ parser.addListener("endkeyframerule", endRule);
+ parser.addListener("endpagemargin", endRule);
+ parser.addListener("endpage", endRule);
+ parser.addListener("endviewport", endRule);
+
+ }
+
+});
+
+/*
+ * Rule: Disallow duplicate background-images (using url).
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "duplicate-background-images",
+ name: "Disallow duplicate background images",
+ desc: "Every background-image should be unique. Use a common class for e.g. sprites.",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-background-images",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this,
+ stack = {};
+
+ parser.addListener("property", function(event) {
+ var name = event.property.text,
+ value = event.value,
+ i, len;
+
+ if (name.match(/background/i)) {
+ for (i=0, len=value.parts.length; i < len; i++) {
+ if (value.parts[i].type === "uri") {
+ if (typeof stack[value.parts[i].uri] === "undefined") {
+ stack[value.parts[i].uri] = event;
+ } else {
+ 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);
+ }
+ }
+ }
+ }
+ });
+ }
+});
+
+/*
+ * Rule: Duplicate properties must appear one after the other. If an already-defined
+ * property appears somewhere else in the rule, then it's likely an error.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "duplicate-properties",
+ name: "Disallow duplicate properties",
+ desc: "Duplicate properties must appear one after the other.",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-duplicate-properties",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this,
+ properties,
+ lastProperty;
+
+ function startRule() {
+ properties = {};
+ }
+
+ parser.addListener("startrule", startRule);
+ parser.addListener("startfontface", startRule);
+ parser.addListener("startpage", startRule);
+ parser.addListener("startpagemargin", startRule);
+ parser.addListener("startkeyframerule", startRule);
+ parser.addListener("startviewport", startRule);
+
+ parser.addListener("property", function(event) {
+ var property = event.property,
+ name = property.text.toLowerCase();
+
+ if (properties[name] && (lastProperty !== name || properties[name] === event.value.text)) {
+ reporter.report("Duplicate property '" + event.property + "' found.", event.line, event.col, rule);
+ }
+
+ properties[name] = event.value.text;
+ lastProperty = name;
+
+ });
+
+
+ }
+
+});
+
+/*
+ * Rule: Style rules without any properties defined should be removed.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "empty-rules",
+ name: "Disallow empty rules",
+ desc: "Rules without any properties specified should be removed.",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-empty-rules",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this,
+ count = 0;
+
+ parser.addListener("startrule", function() {
+ count=0;
+ });
+
+ parser.addListener("property", function() {
+ count++;
+ });
+
+ parser.addListener("endrule", function(event) {
+ var selectors = event.selectors;
+ if (count === 0) {
+ reporter.report("Rule is empty.", selectors[0].line, selectors[0].col, rule);
+ }
+ });
+ }
+
+});
+
+/*
+ * Rule: There should be no syntax errors. (Duh.)
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "errors",
+ name: "Parsing Errors",
+ desc: "This rule looks for recoverable syntax errors.",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this;
+
+ parser.addListener("error", function(event) {
+ reporter.error(event.message, event.line, event.col, rule);
+ });
+
+ }
+
+});
+
+CSSLint.addRule({
+
+ // rule information
+ id: "fallback-colors",
+ name: "Require fallback colors",
+ desc: "For older browsers that don't support RGBA, HSL, or HSLA, provide a fallback color.",
+ url: "https://github.com/CSSLint/csslint/wiki/Require-fallback-colors",
+ browsers: "IE6,IE7,IE8",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this,
+ lastProperty,
+ propertiesToCheck = {
+ color: 1,
+ background: 1,
+ "border-color": 1,
+ "border-top-color": 1,
+ "border-right-color": 1,
+ "border-bottom-color": 1,
+ "border-left-color": 1,
+ border: 1,
+ "border-top": 1,
+ "border-right": 1,
+ "border-bottom": 1,
+ "border-left": 1,
+ "background-color": 1
+ };
+
+ function startRule() {
+ lastProperty = null;
+ }
+
+ parser.addListener("startrule", startRule);
+ parser.addListener("startfontface", startRule);
+ parser.addListener("startpage", startRule);
+ parser.addListener("startpagemargin", startRule);
+ parser.addListener("startkeyframerule", startRule);
+ parser.addListener("startviewport", startRule);
+
+ parser.addListener("property", function(event) {
+ var property = event.property,
+ name = property.text.toLowerCase(),
+ parts = event.value.parts,
+ i = 0,
+ colorType = "",
+ len = parts.length;
+
+ if (propertiesToCheck[name]) {
+ while (i < len) {
+ if (parts[i].type === "color") {
+ if ("alpha" in parts[i] || "hue" in parts[i]) {
+
+ if (/([^\)]+)\(/.test(parts[i])) {
+ colorType = RegExp.$1.toUpperCase();
+ }
+
+ if (!lastProperty || (lastProperty.property.text.toLowerCase() !== name || lastProperty.colorType !== "compat")) {
+ reporter.report("Fallback " + name + " (hex or RGB) should precede " + colorType + " " + name + ".", event.line, event.col, rule);
+ }
+ } else {
+ event.colorType = "compat";
+ }
+ }
+
+ i++;
+ }
+ }
+
+ lastProperty = event;
+ });
+
+ }
+
+});
+
+/*
+ * Rule: You shouldn't use more than 10 floats. If you do, there's probably
+ * room for some abstraction.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "floats",
+ name: "Disallow too many floats",
+ desc: "This rule tests if the float property is used too many times",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-too-many-floats",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this;
+ var count = 0;
+
+ // count how many times "float" is used
+ parser.addListener("property", function(event) {
+ if (event.property.text.toLowerCase() === "float" &&
+ event.value.text.toLowerCase() !== "none") {
+ count++;
+ }
+ });
+
+ // report the results
+ parser.addListener("endstylesheet", function() {
+ reporter.stat("floats", count);
+ if (count >= 10) {
+ reporter.rollupWarn("Too many floats (" + count + "), you're probably using them for layout. Consider using a grid system instead.", rule);
+ }
+ });
+ }
+
+});
+
+/*
+ * Rule: Avoid too many @font-face declarations in the same stylesheet.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "font-faces",
+ name: "Don't use too many web fonts",
+ desc: "Too many different web fonts in the same stylesheet.",
+ url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-web-fonts",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this,
+ count = 0;
+
+
+ parser.addListener("startfontface", function() {
+ count++;
+ });
+
+ parser.addListener("endstylesheet", function() {
+ if (count > 5) {
+ reporter.rollupWarn("Too many @font-face declarations (" + count + ").", rule);
+ }
+ });
+ }
+
+});
+
+/*
+ * Rule: You shouldn't need more than 9 font-size declarations.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "font-sizes",
+ name: "Disallow too many font sizes",
+ desc: "Checks the number of font-size declarations.",
+ url: "https://github.com/CSSLint/csslint/wiki/Don%27t-use-too-many-font-size-declarations",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this,
+ count = 0;
+
+ // check for use of "font-size"
+ parser.addListener("property", function(event) {
+ if (event.property.toString() === "font-size") {
+ count++;
+ }
+ });
+
+ // report the results
+ parser.addListener("endstylesheet", function() {
+ reporter.stat("font-sizes", count);
+ if (count >= 10) {
+ reporter.rollupWarn("Too many font-size declarations (" + count + "), abstraction needed.", rule);
+ }
+ });
+ }
+
+});
+
+/*
+ * Rule: When using a vendor-prefixed gradient, make sure to use them all.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "gradients",
+ name: "Require all gradient definitions",
+ desc: "When using a vendor-prefixed gradient, make sure to use them all.",
+ url: "https://github.com/CSSLint/csslint/wiki/Require-all-gradient-definitions",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this,
+ gradients;
+
+ parser.addListener("startrule", function() {
+ gradients = {
+ moz: 0,
+ webkit: 0,
+ oldWebkit: 0,
+ o: 0
+ };
+ });
+
+ parser.addListener("property", function(event) {
+
+ if (/\-(moz|o|webkit)(?:\-(?:linear|radial))\-gradient/i.test(event.value)) {
+ gradients[RegExp.$1] = 1;
+ } else if (/\-webkit\-gradient/i.test(event.value)) {
+ gradients.oldWebkit = 1;
+ }
+
+ });
+
+ parser.addListener("endrule", function(event) {
+ var missing = [];
+
+ if (!gradients.moz) {
+ missing.push("Firefox 3.6+");
+ }
+
+ if (!gradients.webkit) {
+ missing.push("Webkit (Safari 5+, Chrome)");
+ }
+
+ if (!gradients.oldWebkit) {
+ missing.push("Old Webkit (Safari 4+, Chrome)");
+ }
+
+ if (!gradients.o) {
+ missing.push("Opera 11.1+");
+ }
+
+ if (missing.length && missing.length < 4) {
+ reporter.report("Missing vendor-prefixed CSS gradients for " + missing.join(", ") + ".", event.selectors[0].line, event.selectors[0].col, rule);
+ }
+
+ });
+
+ }
+
+});
+
+/*
+ * Rule: Don't use IDs for selectors.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "ids",
+ name: "Disallow IDs in selectors",
+ desc: "Selectors should not contain IDs.",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-IDs-in-selectors",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this;
+ parser.addListener("startrule", function(event) {
+ var selectors = event.selectors,
+ selector,
+ part,
+ modifier,
+ idCount,
+ i, j, k;
+
+ for (i=0; i < selectors.length; i++) {
+ selector = selectors[i];
+ idCount = 0;
+
+ for (j=0; j < selector.parts.length; j++) {
+ part = selector.parts[j];
+ if (part.type === parser.SELECTOR_PART_TYPE) {
+ for (k=0; k < part.modifiers.length; k++) {
+ modifier = part.modifiers[k];
+ if (modifier.type === "id") {
+ idCount++;
+ }
+ }
+ }
+ }
+
+ if (idCount === 1) {
+ reporter.report("Don't use IDs in selectors.", selector.line, selector.col, rule);
+ } else if (idCount > 1) {
+ reporter.report(idCount + " IDs in the selector, really?", selector.line, selector.col, rule);
+ }
+ }
+
+ });
+ }
+
+});
+
+/*
+ * Rule: IE6-9 supports up to 31 stylesheet import.
+ * Reference:
+ * http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/internet-explorer-stylesheet-rule-selector-import-sheet-limit-maximum.aspx
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "import-ie-limit",
+ name: "@import limit on IE6-IE9",
+ desc: "IE6-9 supports up to 31 @import per stylesheet",
+ browsers: "IE6, IE7, IE8, IE9",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this,
+ MAX_IMPORT_COUNT = 31,
+ count = 0;
+
+ function startPage() {
+ count = 0;
+ }
+
+ parser.addListener("startpage", startPage);
+
+ parser.addListener("import", function() {
+ count++;
+ });
+
+ parser.addListener("endstylesheet", function() {
+ if (count > MAX_IMPORT_COUNT) {
+ reporter.rollupError(
+ "Too many @import rules (" + count + "). IE6-9 supports up to 31 import per stylesheet.",
+ rule
+ );
+ }
+ });
+ }
+
+});
+
+/*
+ * Rule: Don't use @import, use <link> instead.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "import",
+ name: "Disallow @import",
+ desc: "Don't use @import, use <link> instead.",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-%40import",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this;
+
+ parser.addListener("import", function(event) {
+ reporter.report("@import prevents parallel downloads, use <link> instead.", event.line, event.col, rule);
+ });
+
+ }
+
+});
+
+/*
+ * Rule: Make sure !important is not overused, this could lead to specificity
+ * war. Display a warning on !important declarations, an error if it's
+ * used more at least 10 times.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "important",
+ name: "Disallow !important",
+ desc: "Be careful when using !important declaration",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-%21important",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this,
+ count = 0;
+
+ // warn that important is used and increment the declaration counter
+ parser.addListener("property", function(event) {
+ if (event.important === true) {
+ count++;
+ reporter.report("Use of !important", event.line, event.col, rule);
+ }
+ });
+
+ // if there are more than 10, show an error
+ parser.addListener("endstylesheet", function() {
+ reporter.stat("important", count);
+ if (count >= 10) {
+ reporter.rollupWarn("Too many !important declarations (" + count + "), try to use less than 10 to avoid specificity issues.", rule);
+ }
+ });
+ }
+
+});
+
+/*
+ * Rule: Properties should be known (listed in CSS3 specification) or
+ * be a vendor-prefixed property.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "known-properties",
+ name: "Require use of known properties",
+ desc: "Properties should be known (listed in CSS3 specification) or be a vendor-prefixed property.",
+ url: "https://github.com/CSSLint/csslint/wiki/Require-use-of-known-properties",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this;
+
+ parser.addListener("property", function(event) {
+
+ // the check is handled entirely by the parser-lib (https://github.com/nzakas/parser-lib)
+ if (event.invalid) {
+ reporter.report(event.invalid.message, event.line, event.col, rule);
+ }
+
+ });
+ }
+
+});
+
+/*
+ * Rule: All properties should be in alphabetical order.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "order-alphabetical",
+ name: "Alphabetical order",
+ desc: "Assure properties are in alphabetical order",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this,
+ properties;
+
+ var startRule = function () {
+ properties = [];
+ };
+
+ var endRule = function(event) {
+ var currentProperties = properties.join(","),
+ expectedProperties = properties.sort().join(",");
+
+ if (currentProperties !== expectedProperties) {
+ reporter.report("Rule doesn't have all its properties in alphabetical order.", event.line, event.col, rule);
+ }
+ };
+
+ parser.addListener("startrule", startRule);
+ parser.addListener("startfontface", startRule);
+ parser.addListener("startpage", startRule);
+ parser.addListener("startpagemargin", startRule);
+ parser.addListener("startkeyframerule", startRule);
+ parser.addListener("startviewport", startRule);
+
+ parser.addListener("property", function(event) {
+ var name = event.property.text,
+ lowerCasePrefixLessName = name.toLowerCase().replace(/^-.*?-/, "");
+
+ properties.push(lowerCasePrefixLessName);
+ });
+
+ parser.addListener("endrule", endRule);
+ parser.addListener("endfontface", endRule);
+ parser.addListener("endpage", endRule);
+ parser.addListener("endpagemargin", endRule);
+ parser.addListener("endkeyframerule", endRule);
+ parser.addListener("endviewport", endRule);
+ }
+
+});
+
+/*
+ * Rule: outline: none or outline: 0 should only be used in a :focus rule
+ * and only if there are other properties in the same rule.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "outline-none",
+ name: "Disallow outline: none",
+ desc: "Use of outline: none or outline: 0 should be limited to :focus rules.",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-outline%3Anone",
+ browsers: "All",
+ tags: ["Accessibility"],
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this,
+ lastRule;
+
+ function startRule(event) {
+ if (event.selectors) {
+ lastRule = {
+ line: event.line,
+ col: event.col,
+ selectors: event.selectors,
+ propCount: 0,
+ outline: false
+ };
+ } else {
+ lastRule = null;
+ }
+ }
+
+ function endRule() {
+ if (lastRule) {
+ if (lastRule.outline) {
+ if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") === -1) {
+ reporter.report("Outlines should only be modified using :focus.", lastRule.line, lastRule.col, rule);
+ } else if (lastRule.propCount === 1) {
+ reporter.report("Outlines shouldn't be hidden unless other visual changes are made.", lastRule.line, lastRule.col, rule);
+ }
+ }
+ }
+ }
+
+ parser.addListener("startrule", startRule);
+ parser.addListener("startfontface", startRule);
+ parser.addListener("startpage", startRule);
+ parser.addListener("startpagemargin", startRule);
+ parser.addListener("startkeyframerule", startRule);
+ parser.addListener("startviewport", startRule);
+
+ parser.addListener("property", function(event) {
+ var name = event.property.text.toLowerCase(),
+ value = event.value;
+
+ if (lastRule) {
+ lastRule.propCount++;
+ if (name === "outline" && (value.toString() === "none" || value.toString() === "0")) {
+ lastRule.outline = true;
+ }
+ }
+
+ });
+
+ parser.addListener("endrule", endRule);
+ parser.addListener("endfontface", endRule);
+ parser.addListener("endpage", endRule);
+ parser.addListener("endpagemargin", endRule);
+ parser.addListener("endkeyframerule", endRule);
+ parser.addListener("endviewport", endRule);
+
+ }
+
+});
+
+/*
+ * Rule: Don't use classes or IDs with elements (a.foo or a#foo).
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "overqualified-elements",
+ name: "Disallow overqualified elements",
+ desc: "Don't use classes or IDs with elements (a.foo or a#foo).",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-overqualified-elements",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this,
+ classes = {};
+
+ parser.addListener("startrule", function(event) {
+ var selectors = event.selectors,
+ selector,
+ part,
+ modifier,
+ i, j, k;
+
+ for (i=0; i < selectors.length; i++) {
+ selector = selectors[i];
+
+ for (j=0; j < selector.parts.length; j++) {
+ part = selector.parts[j];
+ if (part.type === parser.SELECTOR_PART_TYPE) {
+ for (k=0; k < part.modifiers.length; k++) {
+ modifier = part.modifiers[k];
+ if (part.elementName && modifier.type === "id") {
+ reporter.report("Element (" + part + ") is overqualified, just use " + modifier + " without element name.", part.line, part.col, rule);
+ } else if (modifier.type === "class") {
+
+ if (!classes[modifier]) {
+ classes[modifier] = [];
+ }
+ classes[modifier].push({
+ modifier: modifier,
+ part: part
+ });
+ }
+ }
+ }
+ }
+ }
+ });
+
+ parser.addListener("endstylesheet", function() {
+
+ var prop;
+ for (prop in classes) {
+ if (classes.hasOwnProperty(prop)) {
+
+ // one use means that this is overqualified
+ if (classes[prop].length === 1 && classes[prop][0].part.elementName) {
+ 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);
+ }
+ }
+ }
+ });
+ }
+
+});
+
+/*
+ * Rule: Headings (h1-h6) should not be qualified (namespaced).
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "qualified-headings",
+ name: "Disallow qualified headings",
+ desc: "Headings should not be qualified (namespaced).",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-qualified-headings",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this;
+
+ parser.addListener("startrule", function(event) {
+ var selectors = event.selectors,
+ selector,
+ part,
+ i, j;
+
+ for (i=0; i < selectors.length; i++) {
+ selector = selectors[i];
+
+ for (j=0; j < selector.parts.length; j++) {
+ part = selector.parts[j];
+ if (part.type === parser.SELECTOR_PART_TYPE) {
+ if (part.elementName && /h[1-6]/.test(part.elementName.toString()) && j > 0) {
+ reporter.report("Heading (" + part.elementName + ") should not be qualified.", part.line, part.col, rule);
+ }
+ }
+ }
+ }
+ });
+ }
+
+});
+
+/*
+ * Rule: Selectors that look like regular expressions are slow and should be avoided.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "regex-selectors",
+ name: "Disallow selectors that look like regexs",
+ desc: "Selectors that look like regular expressions are slow and should be avoided.",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-selectors-that-look-like-regular-expressions",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this;
+
+ parser.addListener("startrule", function(event) {
+ var selectors = event.selectors,
+ selector,
+ part,
+ modifier,
+ i, j, k;
+
+ for (i=0; i < selectors.length; i++) {
+ selector = selectors[i];
+ for (j=0; j < selector.parts.length; j++) {
+ part = selector.parts[j];
+ if (part.type === parser.SELECTOR_PART_TYPE) {
+ for (k=0; k < part.modifiers.length; k++) {
+ modifier = part.modifiers[k];
+ if (modifier.type === "attribute") {
+ if (/([~\|\^\$\*]=)/.test(modifier)) {
+ reporter.report("Attribute selectors with " + RegExp.$1 + " are slow!", modifier.line, modifier.col, rule);
+ }
+ }
+
+ }
+ }
+ }
+ }
+ });
+ }
+
+});
+
+/*
+ * Rule: Total number of rules should not exceed x.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "rules-count",
+ name: "Rules Count",
+ desc: "Track how many rules there are.",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var count = 0;
+
+ // count each rule
+ parser.addListener("startrule", function() {
+ count++;
+ });
+
+ parser.addListener("endstylesheet", function() {
+ reporter.stat("rule-count", count);
+ });
+ }
+
+});
+
+/*
+ * Rule: Warn people with approaching the IE 4095 limit
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "selector-max-approaching",
+ name: "Warn when approaching the 4095 selector limit for IE",
+ desc: "Will warn when selector count is >= 3800 selectors.",
+ browsers: "IE",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this, count = 0;
+
+ parser.addListener("startrule", function(event) {
+ count += event.selectors.length;
+ });
+
+ parser.addListener("endstylesheet", function() {
+ if (count >= 3800) {
+ reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule);
+ }
+ });
+ }
+
+});
+
+/*
+ * Rule: Warn people past the IE 4095 limit
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "selector-max",
+ name: "Error when past the 4095 selector limit for IE",
+ desc: "Will error when selector count is > 4095.",
+ browsers: "IE",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this, count = 0;
+
+ parser.addListener("startrule", function(event) {
+ count += event.selectors.length;
+ });
+
+ parser.addListener("endstylesheet", function() {
+ if (count > 4095) {
+ reporter.report("You have " + count + " selectors. Internet Explorer supports a maximum of 4095 selectors per stylesheet. Consider refactoring.", 0, 0, rule);
+ }
+ });
+ }
+
+});
+
+/*
+ * Rule: Avoid new-line characters in selectors.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "selector-newline",
+ name: "Disallow new-line characters in selectors",
+ desc: "New-line characters in selectors are usually a forgotten comma and not a descendant combinator.",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this;
+
+ function startRule(event) {
+ var i, len, selector, p, n, pLen, part, part2, type, currentLine, nextLine,
+ selectors = event.selectors;
+
+ for (i = 0, len = selectors.length; i < len; i++) {
+ selector = selectors[i];
+ for (p = 0, pLen = selector.parts.length; p < pLen; p++) {
+ for (n = p + 1; n < pLen; n++) {
+ part = selector.parts[p];
+ part2 = selector.parts[n];
+ type = part.type;
+ currentLine = part.line;
+ nextLine = part2.line;
+
+ if (type === "descendant" && nextLine > currentLine) {
+ reporter.report("newline character found in selector (forgot a comma?)", currentLine, selectors[i].parts[0].col, rule);
+ }
+ }
+ }
+
+ }
+ }
+
+ parser.addListener("startrule", startRule);
+
+ }
+});
+
+/*
+ * Rule: Use shorthand properties where possible.
+ *
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "shorthand",
+ name: "Require shorthand properties",
+ desc: "Use shorthand properties where possible.",
+ url: "https://github.com/CSSLint/csslint/wiki/Require-shorthand-properties",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this,
+ prop, i, len,
+ propertiesToCheck = {},
+ properties,
+ mapping = {
+ "margin": [
+ "margin-top",
+ "margin-bottom",
+ "margin-left",
+ "margin-right"
+ ],
+ "padding": [
+ "padding-top",
+ "padding-bottom",
+ "padding-left",
+ "padding-right"
+ ]
+ };
+
+ // initialize propertiesToCheck
+ for (prop in mapping) {
+ if (mapping.hasOwnProperty(prop)) {
+ for (i=0, len=mapping[prop].length; i < len; i++) {
+ propertiesToCheck[mapping[prop][i]] = prop;
+ }
+ }
+ }
+
+ function startRule() {
+ properties = {};
+ }
+
+ // event handler for end of rules
+ function endRule(event) {
+
+ var prop, i, len, total;
+
+ // check which properties this rule has
+ for (prop in mapping) {
+ if (mapping.hasOwnProperty(prop)) {
+ total=0;
+
+ for (i=0, len=mapping[prop].length; i < len; i++) {
+ total += properties[mapping[prop][i]] ? 1 : 0;
+ }
+
+ if (total === mapping[prop].length) {
+ reporter.report("The properties " + mapping[prop].join(", ") + " can be replaced by " + prop + ".", event.line, event.col, rule);
+ }
+ }
+ }
+ }
+
+ parser.addListener("startrule", startRule);
+ parser.addListener("startfontface", startRule);
+
+ // check for use of "font-size"
+ parser.addListener("property", function(event) {
+ var name = event.property.toString().toLowerCase();
+
+ if (propertiesToCheck[name]) {
+ properties[name] = 1;
+ }
+ });
+
+ parser.addListener("endrule", endRule);
+ parser.addListener("endfontface", endRule);
+
+ }
+
+});
+
+/*
+ * Rule: Don't use properties with a star prefix.
+ *
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "star-property-hack",
+ name: "Disallow properties with a star prefix",
+ desc: "Checks for the star property hack (targets IE6/7)",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-star-hack",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this;
+
+ // check if property name starts with "*"
+ parser.addListener("property", function(event) {
+ var property = event.property;
+
+ if (property.hack === "*") {
+ reporter.report("Property with star prefix found.", event.property.line, event.property.col, rule);
+ }
+ });
+ }
+});
+
+/*
+ * Rule: Don't use text-indent for image replacement if you need to support rtl.
+ *
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "text-indent",
+ name: "Disallow negative text-indent",
+ desc: "Checks for text indent less than -99px",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-negative-text-indent",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this,
+ textIndent,
+ direction;
+
+
+ function startRule() {
+ textIndent = false;
+ direction = "inherit";
+ }
+
+ // event handler for end of rules
+ function endRule() {
+ if (textIndent && direction !== "ltr") {
+ 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);
+ }
+ }
+
+ parser.addListener("startrule", startRule);
+ parser.addListener("startfontface", startRule);
+
+ // check for use of "font-size"
+ parser.addListener("property", function(event) {
+ var name = event.property.toString().toLowerCase(),
+ value = event.value;
+
+ if (name === "text-indent" && value.parts[0].value < -99) {
+ textIndent = event.property;
+ } else if (name === "direction" && value.toString() === "ltr") {
+ direction = "ltr";
+ }
+ });
+
+ parser.addListener("endrule", endRule);
+ parser.addListener("endfontface", endRule);
+
+ }
+
+});
+
+/*
+ * Rule: Don't use properties with a underscore prefix.
+ *
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "underscore-property-hack",
+ name: "Disallow properties with an underscore prefix",
+ desc: "Checks for the underscore property hack (targets IE6)",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-underscore-hack",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this;
+
+ // check if property name starts with "_"
+ parser.addListener("property", function(event) {
+ var property = event.property;
+
+ if (property.hack === "_") {
+ reporter.report("Property with underscore prefix found.", event.property.line, event.property.col, rule);
+ }
+ });
+ }
+});
+
+/*
+ * Rule: Headings (h1-h6) should be defined only once.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "unique-headings",
+ name: "Headings should only be defined once",
+ desc: "Headings should be defined only once.",
+ url: "https://github.com/CSSLint/csslint/wiki/Headings-should-only-be-defined-once",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this;
+
+ var headings = {
+ h1: 0,
+ h2: 0,
+ h3: 0,
+ h4: 0,
+ h5: 0,
+ h6: 0
+ };
+
+ parser.addListener("startrule", function(event) {
+ var selectors = event.selectors,
+ selector,
+ part,
+ pseudo,
+ i, j;
+
+ for (i=0; i < selectors.length; i++) {
+ selector = selectors[i];
+ part = selector.parts[selector.parts.length-1];
+
+ if (part.elementName && /(h[1-6])/i.test(part.elementName.toString())) {
+
+ for (j=0; j < part.modifiers.length; j++) {
+ if (part.modifiers[j].type === "pseudo") {
+ pseudo = true;
+ break;
+ }
+ }
+
+ if (!pseudo) {
+ headings[RegExp.$1]++;
+ if (headings[RegExp.$1] > 1) {
+ reporter.report("Heading (" + part.elementName + ") has already been defined.", part.line, part.col, rule);
+ }
+ }
+ }
+ }
+ });
+
+ parser.addListener("endstylesheet", function() {
+ var prop,
+ messages = [];
+
+ for (prop in headings) {
+ if (headings.hasOwnProperty(prop)) {
+ if (headings[prop] > 1) {
+ messages.push(headings[prop] + " " + prop + "s");
+ }
+ }
+ }
+
+ if (messages.length) {
+ reporter.rollupWarn("You have " + messages.join(", ") + " defined in this stylesheet.", rule);
+ }
+ });
+ }
+
+});
+
+/*
+ * Rule: Don't use universal selector because it's slow.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "universal-selector",
+ name: "Disallow universal selector",
+ desc: "The universal selector (*) is known to be slow.",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-universal-selector",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this;
+
+ parser.addListener("startrule", function(event) {
+ var selectors = event.selectors,
+ selector,
+ part,
+ i;
+
+ for (i=0; i < selectors.length; i++) {
+ selector = selectors[i];
+
+ part = selector.parts[selector.parts.length-1];
+ if (part.elementName === "*") {
+ reporter.report(rule.desc, part.line, part.col, rule);
+ }
+ }
+ });
+ }
+
+});
+
+/*
+ * Rule: Don't use unqualified attribute selectors because they're just like universal selectors.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "unqualified-attributes",
+ name: "Disallow unqualified attribute selectors",
+ desc: "Unqualified attribute selectors are known to be slow.",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-unqualified-attribute-selectors",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+
+ var rule = this;
+
+ parser.addListener("startrule", function(event) {
+
+ var selectors = event.selectors,
+ selectorContainsClassOrId = false,
+ selector,
+ part,
+ modifier,
+ i, k;
+
+ for (i=0; i < selectors.length; i++) {
+ selector = selectors[i];
+
+ part = selector.parts[selector.parts.length-1];
+ if (part.type === parser.SELECTOR_PART_TYPE) {
+ for (k=0; k < part.modifiers.length; k++) {
+ modifier = part.modifiers[k];
+
+ if (modifier.type === "class" || modifier.type === "id") {
+ selectorContainsClassOrId = true;
+ break;
+ }
+ }
+
+ if (!selectorContainsClassOrId) {
+ for (k=0; k < part.modifiers.length; k++) {
+ modifier = part.modifiers[k];
+ if (modifier.type === "attribute" && (!part.elementName || part.elementName === "*")) {
+ reporter.report(rule.desc, part.line, part.col, rule);
+ }
+ }
+ }
+ }
+
+ }
+ });
+ }
+
+});
+
+/*
+ * Rule: When using a vendor-prefixed property, make sure to
+ * include the standard one.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "vendor-prefix",
+ name: "Require standard property with vendor prefix",
+ desc: "When using a vendor-prefixed property, make sure to include the standard one.",
+ url: "https://github.com/CSSLint/csslint/wiki/Require-standard-property-with-vendor-prefix",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this,
+ properties,
+ num,
+ propertiesToCheck = {
+ "-webkit-border-radius": "border-radius",
+ "-webkit-border-top-left-radius": "border-top-left-radius",
+ "-webkit-border-top-right-radius": "border-top-right-radius",
+ "-webkit-border-bottom-left-radius": "border-bottom-left-radius",
+ "-webkit-border-bottom-right-radius": "border-bottom-right-radius",
+
+ "-o-border-radius": "border-radius",
+ "-o-border-top-left-radius": "border-top-left-radius",
+ "-o-border-top-right-radius": "border-top-right-radius",
+ "-o-border-bottom-left-radius": "border-bottom-left-radius",
+ "-o-border-bottom-right-radius": "border-bottom-right-radius",
+
+ "-moz-border-radius": "border-radius",
+ "-moz-border-radius-topleft": "border-top-left-radius",
+ "-moz-border-radius-topright": "border-top-right-radius",
+ "-moz-border-radius-bottomleft": "border-bottom-left-radius",
+ "-moz-border-radius-bottomright": "border-bottom-right-radius",
+
+ "-moz-column-count": "column-count",
+ "-webkit-column-count": "column-count",
+
+ "-moz-column-gap": "column-gap",
+ "-webkit-column-gap": "column-gap",
+
+ "-moz-column-rule": "column-rule",
+ "-webkit-column-rule": "column-rule",
+
+ "-moz-column-rule-style": "column-rule-style",
+ "-webkit-column-rule-style": "column-rule-style",
+
+ "-moz-column-rule-color": "column-rule-color",
+ "-webkit-column-rule-color": "column-rule-color",
+
+ "-moz-column-rule-width": "column-rule-width",
+ "-webkit-column-rule-width": "column-rule-width",
+
+ "-moz-column-width": "column-width",
+ "-webkit-column-width": "column-width",
+
+ "-webkit-column-span": "column-span",
+ "-webkit-columns": "columns",
+
+ "-moz-box-shadow": "box-shadow",
+ "-webkit-box-shadow": "box-shadow",
+
+ "-moz-transform": "transform",
+ "-webkit-transform": "transform",
+ "-o-transform": "transform",
+ "-ms-transform": "transform",
+
+ "-moz-transform-origin": "transform-origin",
+ "-webkit-transform-origin": "transform-origin",
+ "-o-transform-origin": "transform-origin",
+ "-ms-transform-origin": "transform-origin",
+
+ "-moz-box-sizing": "box-sizing",
+ "-webkit-box-sizing": "box-sizing"
+ };
+
+ // event handler for beginning of rules
+ function startRule() {
+ properties = {};
+ num = 1;
+ }
+
+ // event handler for end of rules
+ function endRule() {
+ var prop,
+ i,
+ len,
+ needed,
+ actual,
+ needsStandard = [];
+
+ for (prop in properties) {
+ if (propertiesToCheck[prop]) {
+ needsStandard.push({
+ actual: prop,
+ needed: propertiesToCheck[prop]
+ });
+ }
+ }
+
+ for (i=0, len=needsStandard.length; i < len; i++) {
+ needed = needsStandard[i].needed;
+ actual = needsStandard[i].actual;
+
+ if (!properties[needed]) {
+ reporter.report("Missing standard property '" + needed + "' to go along with '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
+ } else {
+ // make sure standard property is last
+ if (properties[needed][0].pos < properties[actual][0].pos) {
+ reporter.report("Standard property '" + needed + "' should come after vendor-prefixed property '" + actual + "'.", properties[actual][0].name.line, properties[actual][0].name.col, rule);
+ }
+ }
+ }
+
+ }
+
+ parser.addListener("startrule", startRule);
+ parser.addListener("startfontface", startRule);
+ parser.addListener("startpage", startRule);
+ parser.addListener("startpagemargin", startRule);
+ parser.addListener("startkeyframerule", startRule);
+ parser.addListener("startviewport", startRule);
+
+ parser.addListener("property", function(event) {
+ var name = event.property.text.toLowerCase();
+
+ if (!properties[name]) {
+ properties[name] = [];
+ }
+
+ properties[name].push({
+ name: event.property,
+ value: event.value,
+ pos: num++
+ });
+ });
+
+ parser.addListener("endrule", endRule);
+ parser.addListener("endfontface", endRule);
+ parser.addListener("endpage", endRule);
+ parser.addListener("endpagemargin", endRule);
+ parser.addListener("endkeyframerule", endRule);
+ parser.addListener("endviewport", endRule);
+ }
+
+});
+
+/*
+ * Rule: You don't need to specify units when a value is 0.
+ */
+
+CSSLint.addRule({
+
+ // rule information
+ id: "zero-units",
+ name: "Disallow units for 0 values",
+ desc: "You don't need to specify units when a value is 0.",
+ url: "https://github.com/CSSLint/csslint/wiki/Disallow-units-for-zero-values",
+ browsers: "All",
+
+ // initialization
+ init: function(parser, reporter) {
+ "use strict";
+ var rule = this;
+
+ // count how many times "float" is used
+ parser.addListener("property", function(event) {
+ var parts = event.value.parts,
+ i = 0,
+ len = parts.length;
+
+ while (i < len) {
+ if ((parts[i].units || parts[i].type === "percentage") && parts[i].value === 0 && parts[i].type !== "time") {
+ reporter.report("Values of 0 shouldn't have units specified.", parts[i].line, parts[i].col, rule);
+ }
+ i++;
+ }
+
+ });
+
+ }
+
+});
+
+(function() {
+ "use strict";
+
+ /**
+ * Replace special characters before write to output.
+ *
+ * Rules:
+ * - single quotes is the escape sequence for double-quotes
+ * - & is the escape sequence for &
+ * - < is the escape sequence for <
+ * - > is the escape sequence for >
+ *
+ * @param {String} message to escape
+ * @return escaped message as {String}
+ */
+ var xmlEscape = function(str) {
+ if (!str || str.constructor !== String) {
+ return "";
+ }
+
+ return str.replace(/["&><]/g, function(match) {
+ switch (match) {
+ case "\"":
+ return """;
+ case "&":
+ return "&";
+ case "<":
+ return "<";
+ case ">":
+ return ">";
+ }
+ });
+ };
+
+ CSSLint.addFormatter({
+ // format information
+ id: "checkstyle-xml",
+ name: "Checkstyle XML format",
+
+ /**
+ * Return opening root XML tag.
+ * @return {String} to prepend before all results
+ */
+ startFormat: function() {
+ return "<?xml version=\"1.0\" encoding=\"utf-8\"?><checkstyle>";
+ },
+
+ /**
+ * Return closing root XML tag.
+ * @return {String} to append after all results
+ */
+ endFormat: function() {
+ return "</checkstyle>";
+ },
+
+ /**
+ * Returns message when there is a file read error.
+ * @param {String} filename The name of the file that caused the error.
+ * @param {String} message The error message
+ * @return {String} The error message.
+ */
+ readError: function(filename, message) {
+ return "<file name=\"" + xmlEscape(filename) + "\"><error line=\"0\" column=\"0\" severty=\"error\" message=\"" + xmlEscape(message) + "\"></error></file>";
+ },
+
+ /**
+ * Given CSS Lint results for a file, return output for this format.
+ * @param results {Object} with error and warning messages
+ * @param filename {String} relative file path
+ * @param options {Object} (UNUSED for now) specifies special handling of output
+ * @return {String} output for results
+ */
+ formatResults: function(results, filename/*, options*/) {
+ var messages = results.messages,
+ output = [];
+
+ /**
+ * Generate a source string for a rule.
+ * Checkstyle source strings usually resemble Java class names e.g
+ * net.csslint.SomeRuleName
+ * @param {Object} rule
+ * @return rule source as {String}
+ */
+ var generateSource = function(rule) {
+ if (!rule || !("name" in rule)) {
+ return "";
+ }
+ return "net.csslint." + rule.name.replace(/\s/g, "");
+ };
+
+
+ if (messages.length > 0) {
+ output.push("<file name=\""+filename+"\">");
+ CSSLint.Util.forEach(messages, function (message) {
+ // ignore rollups for now
+ if (!message.rollup) {
+ output.push("<error line=\"" + message.line + "\" column=\"" + message.col + "\" severity=\"" + message.type + "\"" +
+ " message=\"" + xmlEscape(message.message) + "\" source=\"" + generateSource(message.rule) +"\"/>");
+ }
+ });
+ output.push("</file>");
+ }
+
+ return output.join("");
+ }
+ });
+
+}());
+
+CSSLint.addFormatter({
+ // format information
+ id: "compact",
+ name: "Compact, 'porcelain' format",
+
+ /**
+ * Return content to be printed before all file results.
+ * @return {String} to prepend before all results
+ */
+ startFormat: function() {
+ "use strict";
+ return "";
+ },
+
+ /**
+ * Return content to be printed after all file results.
+ * @return {String} to append after all results
+ */
+ endFormat: function() {
+ "use strict";
+ return "";
+ },
+
+ /**
+ * Given CSS Lint results for a file, return output for this format.
+ * @param results {Object} with error and warning messages
+ * @param filename {String} relative file path
+ * @param options {Object} (Optional) specifies special handling of output
+ * @return {String} output for results
+ */
+ formatResults: function(results, filename, options) {
+ "use strict";
+ var messages = results.messages,
+ output = "";
+ options = options || {};
+
+ /**
+ * Capitalize and return given string.
+ * @param str {String} to capitalize
+ * @return {String} capitalized
+ */
+ var capitalize = function(str) {
+ return str.charAt(0).toUpperCase() + str.slice(1);
+ };
+
+ if (messages.length === 0) {
+ return options.quiet ? "" : filename + ": Lint Free!";
+ }
+
+ CSSLint.Util.forEach(messages, function(message) {
+ if (message.rollup) {
+ output += filename + ": " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
+ } else {
+ output += filename + ": line " + message.line +
+ ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n";
+ }
+ });
+
+ return output;
+ }
+});
+
+CSSLint.addFormatter({
+ // format information
+ id: "csslint-xml",
+ name: "CSSLint XML format",
+
+ /**
+ * Return opening root XML tag.
+ * @return {String} to prepend before all results
+ */
+ startFormat: function() {
+ "use strict";
+ return "<?xml version=\"1.0\" encoding=\"utf-8\"?><csslint>";
+ },
+
+ /**
+ * Return closing root XML tag.
+ * @return {String} to append after all results
+ */
+ endFormat: function() {
+ "use strict";
+ return "</csslint>";
+ },
+
+ /**
+ * Given CSS Lint results for a file, return output for this format.
+ * @param results {Object} with error and warning messages
+ * @param filename {String} relative file path
+ * @param options {Object} (UNUSED for now) specifies special handling of output
+ * @return {String} output for results
+ */
+ formatResults: function(results, filename/*, options*/) {
+ "use strict";
+ var messages = results.messages,
+ output = [];
+
+ /**
+ * Replace special characters before write to output.
+ *
+ * Rules:
+ * - single quotes is the escape sequence for double-quotes
+ * - & is the escape sequence for &
+ * - < is the escape sequence for <
+ * - > is the escape sequence for >
+ *
+ * @param {String} message to escape
+ * @return escaped message as {String}
+ */
+ var escapeSpecialCharacters = function(str) {
+ if (!str || str.constructor !== String) {
+ return "";
+ }
+ return str.replace(/"/g, "'").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
+ };
+
+ if (messages.length > 0) {
+ output.push("<file name=\""+filename+"\">");
+ CSSLint.Util.forEach(messages, function (message) {
+ if (message.rollup) {
+ output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
+ } else {
+ output.push("<issue line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
+ " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
+ }
+ });
+ output.push("</file>");
+ }
+
+ return output.join("");
+ }
+});
+
+/* globals JSON: true */
+
+CSSLint.addFormatter({
+ // format information
+ id: "json",
+ name: "JSON",
+
+ /**
+ * Return content to be printed before all file results.
+ * @return {String} to prepend before all results
+ */
+ startFormat: function() {
+ "use strict";
+ this.json = [];
+ return "";
+ },
+
+ /**
+ * Return content to be printed after all file results.
+ * @return {String} to append after all results
+ */
+ endFormat: function() {
+ "use strict";
+ var ret = "";
+ if (this.json.length > 0) {
+ if (this.json.length === 1) {
+ ret = JSON.stringify(this.json[0]);
+ } else {
+ ret = JSON.stringify(this.json);
+ }
+ }
+ return ret;
+ },
+
+ /**
+ * Given CSS Lint results for a file, return output for this format.
+ * @param results {Object} with error and warning messages
+ * @param filename {String} relative file path (Unused)
+ * @return {String} output for results
+ */
+ formatResults: function(results, filename, options) {
+ "use strict";
+ if (results.messages.length > 0 || !options.quiet) {
+ this.json.push({
+ filename: filename,
+ messages: results.messages,
+ stats: results.stats
+ });
+ }
+ return "";
+ }
+});
+
+CSSLint.addFormatter({
+ // format information
+ id: "junit-xml",
+ name: "JUNIT XML format",
+
+ /**
+ * Return opening root XML tag.
+ * @return {String} to prepend before all results
+ */
+ startFormat: function() {
+ "use strict";
+ return "<?xml version=\"1.0\" encoding=\"utf-8\"?><testsuites>";
+ },
+
+ /**
+ * Return closing root XML tag.
+ * @return {String} to append after all results
+ */
+ endFormat: function() {
+ "use strict";
+ return "</testsuites>";
+ },
+
+ /**
+ * Given CSS Lint results for a file, return output for this format.
+ * @param results {Object} with error and warning messages
+ * @param filename {String} relative file path
+ * @param options {Object} (UNUSED for now) specifies special handling of output
+ * @return {String} output for results
+ */
+ formatResults: function(results, filename/*, options*/) {
+ "use strict";
+
+ var messages = results.messages,
+ output = [],
+ tests = {
+ "error": 0,
+ "failure": 0
+ };
+
+ /**
+ * Generate a source string for a rule.
+ * JUNIT source strings usually resemble Java class names e.g
+ * net.csslint.SomeRuleName
+ * @param {Object} rule
+ * @return rule source as {String}
+ */
+ var generateSource = function(rule) {
+ if (!rule || !("name" in rule)) {
+ return "";
+ }
+ return "net.csslint." + rule.name.replace(/\s/g, "");
+ };
+
+ /**
+ * Replace special characters before write to output.
+ *
+ * Rules:
+ * - single quotes is the escape sequence for double-quotes
+ * - < is the escape sequence for <
+ * - > is the escape sequence for >
+ *
+ * @param {String} message to escape
+ * @return escaped message as {String}
+ */
+ var escapeSpecialCharacters = function(str) {
+
+ if (!str || str.constructor !== String) {
+ return "";
+ }
+
+ return str.replace(/"/g, "'").replace(/</g, "<").replace(/>/g, ">");
+
+ };
+
+ if (messages.length > 0) {
+
+ messages.forEach(function (message) {
+
+ // since junit has no warning class
+ // all issues as errors
+ var type = message.type === "warning" ? "error" : message.type;
+
+ // ignore rollups for now
+ if (!message.rollup) {
+
+ // build the test case separately, once joined
+ // we'll add it to a custom array filtered by type
+ output.push("<testcase time=\"0\" name=\"" + generateSource(message.rule) + "\">");
+ output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\"><![CDATA[" + message.line + ":" + message.col + ":" + escapeSpecialCharacters(message.evidence) + "]]></" + type + ">");
+ output.push("</testcase>");
+
+ tests[type] += 1;
+
+ }
+
+ });
+
+ output.unshift("<testsuite time=\"0\" tests=\"" + messages.length + "\" skipped=\"0\" errors=\"" + tests.error + "\" failures=\"" + tests.failure + "\" package=\"net.csslint\" name=\"" + filename + "\">");
+ output.push("</testsuite>");
+
+ }
+
+ return output.join("");
+
+ }
+});
+
+CSSLint.addFormatter({
+ // format information
+ id: "lint-xml",
+ name: "Lint XML format",
+
+ /**
+ * Return opening root XML tag.
+ * @return {String} to prepend before all results
+ */
+ startFormat: function() {
+ "use strict";
+ return "<?xml version=\"1.0\" encoding=\"utf-8\"?><lint>";
+ },
+
+ /**
+ * Return closing root XML tag.
+ * @return {String} to append after all results
+ */
+ endFormat: function() {
+ "use strict";
+ return "</lint>";
+ },
+
+ /**
+ * Given CSS Lint results for a file, return output for this format.
+ * @param results {Object} with error and warning messages
+ * @param filename {String} relative file path
+ * @param options {Object} (UNUSED for now) specifies special handling of output
+ * @return {String} output for results
+ */
+ formatResults: function(results, filename/*, options*/) {
+ "use strict";
+ var messages = results.messages,
+ output = [];
+
+ /**
+ * Replace special characters before write to output.
+ *
+ * Rules:
+ * - single quotes is the escape sequence for double-quotes
+ * - & is the escape sequence for &
+ * - < is the escape sequence for <
+ * - > is the escape sequence for >
+ *
+ * @param {String} message to escape
+ * @return escaped message as {String}
+ */
+ var escapeSpecialCharacters = function(str) {
+ if (!str || str.constructor !== String) {
+ return "";
+ }
+ return str.replace(/"/g, "'").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
+ };
+
+ if (messages.length > 0) {
+
+ output.push("<file name=\""+filename+"\">");
+ CSSLint.Util.forEach(messages, function (message) {
+ if (message.rollup) {
+ output.push("<issue severity=\"" + message.type + "\" reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
+ } else {
+ var rule = "";
+ if (message.rule && message.rule.id) {
+ rule = "rule=\"" + escapeSpecialCharacters(message.rule.id) + "\" ";
+ }
+ output.push("<issue " + rule + "line=\"" + message.line + "\" char=\"" + message.col + "\" severity=\"" + message.type + "\"" +
+ " reason=\"" + escapeSpecialCharacters(message.message) + "\" evidence=\"" + escapeSpecialCharacters(message.evidence) + "\"/>");
+ }
+ });
+ output.push("</file>");
+ }
+
+ return output.join("");
+ }
+});
+
+CSSLint.addFormatter({
+ // format information
+ id: "text",
+ name: "Plain Text",
+
+ /**
+ * Return content to be printed before all file results.
+ * @return {String} to prepend before all results
+ */
+ startFormat: function() {
+ "use strict";
+ return "";
+ },
+
+ /**
+ * Return content to be printed after all file results.
+ * @return {String} to append after all results
+ */
+ endFormat: function() {
+ "use strict";
+ return "";
+ },
+
+ /**
+ * Given CSS Lint results for a file, return output for this format.
+ * @param results {Object} with error and warning messages
+ * @param filename {String} relative file path
+ * @param options {Object} (Optional) specifies special handling of output
+ * @return {String} output for results
+ */
+ formatResults: function(results, filename, options) {
+ "use strict";
+ var messages = results.messages,
+ output = "";
+ options = options || {};
+
+ if (messages.length === 0) {
+ return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + ".";
+ }
+
+ output = "\n\ncsslint: There ";
+ if (messages.length === 1) {
+ output += "is 1 problem";
+ } else {
+ output += "are " + messages.length + " problems";
+ }
+ output += " in " + filename + ".";
+
+ var pos = filename.lastIndexOf("/"),
+ shortFilename = filename;
+
+ if (pos === -1) {
+ pos = filename.lastIndexOf("\\");
+ }
+ if (pos > -1) {
+ shortFilename = filename.substring(pos+1);
+ }
+
+ CSSLint.Util.forEach(messages, function (message, i) {
+ output = output + "\n\n" + shortFilename;
+ if (message.rollup) {
+ output += "\n" + (i+1) + ": " + message.type;
+ output += "\n" + message.message;
+ } else {
+ output += "\n" + (i+1) + ": " + message.type + " at line " + message.line + ", col " + message.col;
+ output += "\n" + message.message;
+ output += "\n" + message.evidence;
+ }
+ });
+
+ return output;
+ }
+});
+
+return CSSLint;
</ins><span class="cx" style="display: block; padding: 0 10px"> })();
</span><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of file
</span></span></pre>
</div>
</div>
</body>
</html>