From 76ef3a08d97d98352bd0901c8fecc729c9966af4 Mon Sep 17 00:00:00 2001 From: XhmikosR Date: Tue, 8 Apr 2014 08:30:41 +0300 Subject: [PATCH 01/10] Re-organize dist directories. There's now only a "dist" directory. --- .gitignore | 2 - Gruntfile.js | 39 +- demos/CSSLintDemo.htm | 2 +- {release/npm => dist}/cli.js | 49 +- {release => dist}/csslint-node.js | 324 ++- {release => dist}/csslint-rhino.js | 472 +++-- {release => dist}/csslint-tests.js | 269 ++- {release => dist}/csslint-worker.js | 439 +++-- {release => dist}/csslint-wsh.js | 472 +++-- {release => dist}/csslint.js | 433 ++-- package.json | 13 +- release/npm/lib/csslint-node.js | 2851 --------------------------- release/npm/package.json | 52 - src/cli/node.js | 2 +- tasks/test_rhino.js | 2 +- tasks/yuitest.js | 2 +- tests/testrunner.htm | 4 +- 17 files changed, 1644 insertions(+), 3783 deletions(-) rename {release/npm => dist}/cli.js (90%) rename {release => dist}/csslint-node.js (94%) rename {release => dist}/csslint-rhino.js (96%) rename {release => dist}/csslint-tests.js (95%) rename {release => dist}/csslint-worker.js (96%) rename {release => dist}/csslint-wsh.js (96%) rename {release => dist}/csslint.js (96%) delete mode 100644 release/npm/lib/csslint-node.js delete mode 100644 release/npm/package.json diff --git a/.gitignore b/.gitignore index 59ec0cb3..94b752cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ -/build/ /node_modules/ /npm-debug.log -/release/npm/README.md csslint.pnproj # Diff files diff --git a/Gruntfile.js b/Gruntfile.js index 3a5efa19..6d40c9e4 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -38,7 +38,7 @@ module.exports = function(grunt) { "*/\n" + "/* Build: v<%= pkg.version %> <%= grunt.template.today('dd-mmmm-yyyy hh:MM:ss') %> */" }, - build_dir: "build", + build_dir: "dist", //Parser lib copy for versions that can't use requirejs parserlib: "node_modules/parserlib/lib/node-parserlib.js", //clone copy for versions that can't use requirejs @@ -58,8 +58,7 @@ module.exports = function(grunt) { ], // Task configuration. clean: { - build: ["<%= build_dir %>"], - release: ["release"] + dist: "<%= build_dir %>" }, changelog: { dest: "CHANGELOG" @@ -94,8 +93,7 @@ module.exports = function(grunt) { footer: "\nexports.CSSLint = CSSLint;" }, files: { - "<%= build_dir %>/<%= pkg.name %>-node.js": ["<%= csslint_files %>"], - "<%= build_dir %>/npm/lib/<%= pkg.name %>-node.js": ["<%= csslint_files %>"] + "<%= build_dir %>/<%= pkg.name %>-node.js": ["<%= csslint_files %>"] } }, node_cli: { @@ -106,7 +104,7 @@ module.exports = function(grunt) { "src/cli/common.js", "src/cli/node.js" ], - dest: "<%= build_dir %>/npm/cli.js" + dest: "<%= build_dir %>/cli.js" }, tests: { src: [ @@ -136,21 +134,8 @@ module.exports = function(grunt) { dest: "<%= build_dir %>/<%= pkg.name %>-wsh.js" } }, - copy: { - build: { - expand: true, - cwd: "<%= build_dir %>/", - src: "**/*", - dest: "release/" - }, - npm: { - expand: true, - src: ["README.md", "package.json"], - dest: "release/npm/" - } - }, includereplace: { - release: { + dist: { options: { // Global variables available in all files globals: { @@ -163,7 +148,7 @@ module.exports = function(grunt) { expand: true, cwd: "<%= build_dir %>/", src: "**/*", - dest: "release/" + dest: "<%= build_dir %>/" }] } }, @@ -223,14 +208,16 @@ module.exports = function(grunt) { grunt.loadTasks("tasks"); // Default task. - grunt.registerTask("default", ["test"]); + grunt.registerTask("default", ["build", "test"]); + + grunt.registerTask("build", ["clean", "concat", "includereplace"]); - // Alias for + //Alias for grunt.registerTask("lint", ["jshint"]); // Testing - grunt.registerTask("test", ["clean:build", "jshint", "concat", "yuitest"]); - grunt.registerTask("rhino", ["clean:build", "jshint", "concat", "test_rhino"]); + grunt.registerTask("test", ["build", "jshint", "yuitest"]); + grunt.registerTask("rhino", ["build", "jshint", "test_rhino"]); - grunt.registerTask("release", ["test", "clean:release", "copy", "includereplace:release", "changelog"]); + grunt.registerTask("release", ["default", "changelog"]); }; diff --git a/demos/CSSLintDemo.htm b/demos/CSSLintDemo.htm index 89b5bd34..90cf2cf6 100644 --- a/demos/CSSLintDemo.htm +++ b/demos/CSSLintDemo.htm @@ -8,7 +8,7 @@ .error { color: #D9534F; font-weight: bold; } .warning { color: #F0AD4E; } - + diff --git a/release/npm/cli.js b/dist/cli.js similarity index 90% rename from release/npm/cli.js rename to dist/cli.js index f78d8dba..bdc5394d 100644 --- a/release/npm/cli.js +++ b/dist/cli.js @@ -1,10 +1,10 @@ #!/usr/bin/env node /*! CSSLint -Copyright (c) 2013 Nicole Sullivan and Nicholas C. Zakas. All rights reserved. +Copyright (c) 2014 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 +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 @@ -13,7 +13,7 @@ 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 +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 @@ -22,11 +22,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* Build: v0.10.0 15-August-2013 01:07:22 *//* +/* Build: v0.10.0 11-April-2014 12:11:52 *//* * Encapsulates all of the CLI functionality. The api argument simply * provides environment-specific functionality. */ -/*global CSSLint*/ + +/* exported cli */ + function cli(api){ var globalOptions = { @@ -195,8 +197,8 @@ function cli(api){ */ function outputHelp(){ var lenToPad = 40, - toPrint = '', - formatString = ''; + toPrint = "", + formatString = ""; api.print([ "\nUsage: csslint-rhino.js [options]* [file|dir]*", @@ -209,14 +211,14 @@ function cli(api){ // Print the option name and the format if present toPrint += " --" + optionName; if (globalOptions[optionName].format !== "") { - formatString = '=' + globalOptions[optionName].format; + formatString = "=" + globalOptions[optionName].format; toPrint += formatString; } else { - formatString = ''; + formatString = ""; } // Pad out with the appropriate number of spaces - toPrint += new Array(lenToPad - (optionName.length + formatString.length)).join(' '); + toPrint += new Array(lenToPad - (optionName.length + formatString.length)).join(" "); // Print the description toPrint += globalOptions[optionName].description + "\n"; @@ -306,9 +308,9 @@ function cli(api){ } function validateOptions(options) { - for (var option_key in options) { - if (!globalOptions.hasOwnProperty(option_key) && option_key !== 'files') { - api.print(option_key + ' is not a valid option. Exiting...'); + for (var optionKey in options) { + if (!globalOptions.hasOwnProperty(optionKey) && optionKey !== "files") { + api.print(optionKey + " is not a valid option. Exiting..."); outputHelp(); api.quit(0); } @@ -316,8 +318,20 @@ function cli(api){ } function readConfigFile(options) { - var data = api.readFile(api.getFullPath(".csslintrc")); + var data = api.readFile(api.getFullPath(".csslintrc")), + json; if (data) { + if (data.charAt(0) === "{") { + try { + json = JSON.parse(data); + data = ""; + for (var optionName in json) { + if (json.hasOwnProperty(optionName)) { + data += "--" + optionName + "=" + json[optionName].join(); + } + } + } catch(e) {} + } options = processArguments(data.split(/[\s\n\r]+/m), options); } @@ -365,12 +379,13 @@ function cli(api){ * CSSLint Node.js Command Line Interface */ -/*jshint node:true*/ -/*global cli*/ +/* jshint node:true */ +/* global cli */ +/* exported CSSLint */ var fs = require("fs"), path = require("path"), - CSSLint = require("./lib/csslint-node").CSSLint; + CSSLint = require("./csslint-node").CSSLint; cli({ args: process.argv.slice(2), diff --git a/release/csslint-node.js b/dist/csslint-node.js similarity index 94% rename from release/csslint-node.js rename to dist/csslint-node.js index f800f974..861dabbd 100644 --- a/release/csslint-node.js +++ b/dist/csslint-node.js @@ -1,9 +1,9 @@ /*! CSSLint -Copyright (c) 2013 Nicole Sullivan and Nicholas C. Zakas. All rights reserved. +Copyright (c) 2014 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 +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 @@ -12,7 +12,7 @@ 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 +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 @@ -21,15 +21,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* Build: v0.10.0 15-August-2013 01:07:22 */ -var parserlib = require("parserlib"); +/* Build: v0.10.0 11-April-2014 12:11:52 */ +var parserlib = require('parserlib'); /** * Main CSSLint object. * @class CSSLint * @static * @extends parserlib.util.EventTarget */ -/*global parserlib, Reporter*/ + +/* global parserlib, Reporter */ +/* exported CSSLint */ + var CSSLint = (function(){ var rules = [], @@ -195,8 +198,7 @@ var CSSLint = (function(){ */ api.verify = function(text, ruleset){ - var i = 0, - len = rules.length, + var i = 0, reporter, lines, report, @@ -204,7 +206,7 @@ var CSSLint = (function(){ underscoreHack: true, strict: false }); // normalize line endings - lines = text.replace(/\n\r?/g, "$split$").split('$split$'); + lines = text.replace(/\n\r?/g, "$split$").split("$split$"); if (!ruleset){ ruleset = this.getRuleset(); @@ -261,7 +263,6 @@ var CSSLint = (function(){ })(); -/*global CSSLint*/ /** * An instance of Report is used to report results of the * verification back to the main API. @@ -423,8 +424,6 @@ Reporter.prototype = { //expose for testing purposes CSSLint._Reporter = Reporter; -/*global CSSLint*/ - /* * Utility functions that make life easier. */ @@ -484,10 +483,11 @@ CSSLint.Util = { } } }; -/*global CSSLint*/ + /* * Rule: Don't use adjoining classes (.foo.bar). */ + CSSLint.addRule({ //rule information @@ -529,7 +529,6 @@ CSSLint.addRule({ } }); -/*global CSSLint*/ /* * Rule: Don't use width or height when using padding or border. @@ -630,11 +629,11 @@ CSSLint.addRule({ } }); -/*global CSSLint*/ /* * Rule: box-sizing doesn't work in IE6 and IE7. */ + CSSLint.addRule({ //rule information @@ -658,11 +657,12 @@ CSSLint.addRule({ } }); + /* * 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) */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -674,14 +674,13 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ var rule = this, - count = 0, 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(event){ + parser.addListener("startfontface", function(){ fontFaceRule = true; }); @@ -699,7 +698,7 @@ CSSLint.addRule({ col = event.col; // This is the property that we care about, we can ignore the rest - if (propertyName === 'src') { + if (propertyName === "src") { var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i; // We need to handle the advanced syntax with two src properties @@ -715,7 +714,7 @@ CSSLint.addRule({ }); // Back to normal rules that we don't need to test - parser.addListener("endfontface", function(event){ + parser.addListener("endfontface", function(){ fontFaceRule = false; if (ruleFailed) { @@ -724,11 +723,12 @@ CSSLint.addRule({ }); } }); + /* * Rule: Include all compatible vendor prefixes to reach a wider * range of users. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -816,9 +816,9 @@ CSSLint.addRule({ for (prop in compatiblePrefixes) { if (compatiblePrefixes.hasOwnProperty(prop)) { variations = []; - prefixed = compatiblePrefixes[prop].split(' '); + prefixed = compatiblePrefixes[prop].split(" "); for (i = 0, len = prefixed.length; i < len; i++) { - variations.push('-' + prefixed[i] + '-' + prop); + variations.push("-" + prefixed[i] + "-" + prop); } compatiblePrefixes[prop] = variations; arrayPush.apply(applyTo, variations); @@ -833,7 +833,7 @@ CSSLint.addRule({ inKeyFrame = event.prefix || true; }); - parser.addListener("endkeyframes", function (event) { + parser.addListener("endkeyframes", function () { inKeyFrame = false; }); @@ -849,7 +849,7 @@ CSSLint.addRule({ } }); - parser.addListener("endrule", function (event) { + parser.addListener("endrule", function () { if (!properties.length) { return; } @@ -910,6 +910,7 @@ CSSLint.addRule({ }); } }); + /* * Rule: Certain properties don't play well with certain display values. * - float should not be used with inline-block @@ -917,7 +918,7 @@ CSSLint.addRule({ * - vertical-align should not be used with block * - margin, float should not be used with table-* */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1027,10 +1028,11 @@ CSSLint.addRule({ } }); + /* * Rule: Disallow duplicate background-images (using url). */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1051,8 +1053,8 @@ CSSLint.addRule({ 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') { + if (value.parts[i].type == "uri") { + if (typeof stack[value.parts[i].uri] === "undefined") { stack[value.parts[i].uri] = event; } else { @@ -1064,11 +1066,12 @@ CSSLint.addRule({ }); } }); + /* * 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. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1083,7 +1086,7 @@ CSSLint.addRule({ properties, lastProperty; - function startRule(event){ + function startRule(){ properties = {}; } @@ -1110,10 +1113,11 @@ CSSLint.addRule({ } }); + /* * Rule: Style rules without any properties defined should be removed. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1144,10 +1148,11 @@ CSSLint.addRule({ } }); + /* * Rule: There should be no syntax errors. (Duh.) */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1168,7 +1173,6 @@ CSSLint.addRule({ }); -/*global CSSLint*/ CSSLint.addRule({ //rule information @@ -1198,7 +1202,7 @@ CSSLint.addRule({ }, properties; - function startRule(event){ + function startRule(){ properties = {}; lastProperty = null; } @@ -1244,11 +1248,12 @@ CSSLint.addRule({ } }); + /* * Rule: You shouldn't use more than 10 floats. If you do, there's probably * room for some abstraction. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1280,10 +1285,11 @@ CSSLint.addRule({ } }); + /* * Rule: Avoid too many @font-face declarations in the same stylesheet. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1310,11 +1316,11 @@ CSSLint.addRule({ } }); + /* * Rule: You shouldn't need more than 9 font-size declarations. */ -/*global CSSLint*/ CSSLint.addRule({ //rule information @@ -1345,10 +1351,11 @@ CSSLint.addRule({ } }); + /* * Rule: When using a vendor-prefixed gradient, make sure to use them all. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1413,7 +1420,7 @@ CSSLint.addRule({ /* * Rule: Don't use IDs for selectors. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1460,10 +1467,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use @import, use instead. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1483,12 +1491,13 @@ CSSLint.addRule({ } }); + /* * 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. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1520,11 +1529,12 @@ CSSLint.addRule({ } }); + /* * Rule: Properties should be known (listed in CSS3 specification) or * be a vendor-prefixed property. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1538,7 +1548,6 @@ CSSLint.addRule({ var rule = this; parser.addListener("property", function(event){ - var name = event.property.text.toLowerCase(); // the check is handled entirely by the parser-lib (https://github.com/nzakas/parser-lib) if (event.invalid) { @@ -1549,11 +1558,58 @@ CSSLint.addRule({ } }); + +/* + * Rule: All properties should be in alphabetical order.. + */ +/*global CSSLint*/ +CSSLint.addRule({ + + //rule information + id: "order-alphabetical", + name: "Alphabetical order", + desc: "Assure properties are in alphabetical order", + browsers: "All", + + //initialization + init: function(parser, reporter){ + var rule = this, + properties; + + var startRule = function () { + properties = []; + }; + + parser.addListener("startrule", startRule); + parser.addListener("startfontface", startRule); + parser.addListener("startpage", startRule); + parser.addListener("startpagemargin", startRule); + parser.addListener("startkeyframerule", startRule); + + parser.addListener("property", function(event){ + var name = event.property.text, + lowerCasePrefixLessName = name.toLowerCase().replace(/^-.*?-/, ""); + + properties.push(lowerCasePrefixLessName); + }); + + parser.addListener("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 ordered.", event.line, event.col, rule); + } + }); + } + +}); + /* * 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. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1582,7 +1638,7 @@ CSSLint.addRule({ } } - function endRule(event){ + function endRule(){ if (lastRule){ if (lastRule.outline){ if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") == -1){ @@ -1622,10 +1678,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use classes or IDs with elements (a.foo or a#foo). */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1685,10 +1742,11 @@ CSSLint.addRule({ } }); + /* * Rule: Headings (h1-h6) should not be qualified (namespaced). */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1723,10 +1781,11 @@ CSSLint.addRule({ } }); + /* * Rule: Selectors that look like regular expressions are slow and should be avoided. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1767,10 +1826,11 @@ CSSLint.addRule({ } }); + /* * Rule: Total number of rules should not exceed x. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1781,8 +1841,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ - var rule = this, - count = 0; + var count = 0; //count each rule parser.addListener("startrule", function(){ @@ -1795,10 +1854,11 @@ CSSLint.addRule({ } }); + /* * Rule: Warn people with approaching the IE 4095 limit */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1811,7 +1871,7 @@ CSSLint.addRule({ init: function(parser, reporter) { var rule = this, count = 0; - parser.addListener('startrule', function(event) { + parser.addListener("startrule", function(event) { count += event.selectors.length; }); @@ -1827,7 +1887,7 @@ CSSLint.addRule({ /* * Rule: Warn people past the IE 4095 limit */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1840,7 +1900,7 @@ CSSLint.addRule({ init: function(parser, reporter){ var rule = this, count = 0; - parser.addListener('startrule',function(event) { + parser.addListener("startrule", function(event) { count += event.selectors.length; }); @@ -1852,11 +1912,47 @@ CSSLint.addRule({ } }); + +/* + * 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) { + var rule = this; + + parser.addListener("startrule", function(event) { + var i, len, selector, p, pLen, part, previousLine, currentLine, + 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++) { + part = selector.parts[p]; + currentLine = part.line; + if (currentLine === previousLine) { + reporter.report("newline character found in selector (forgot a comma?)", currentLine, selectors[i].parts[0].col, rule); + } + previousLine = currentLine; + } + } + }); + } +}); + /* * Rule: Use shorthand properties where possible. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1895,7 +1991,7 @@ CSSLint.addRule({ } } - function startRule(event){ + function startRule(){ properties = {}; } @@ -1925,8 +2021,7 @@ CSSLint.addRule({ //check for use of "font-size" parser.addListener("property", function(event){ - var name = event.property.toString().toLowerCase(), - value = event.value.parts[0].value; + var name = event.property.toString().toLowerCase(); if (propertiesToCheck[name]){ properties[name] = 1; @@ -1939,11 +2034,12 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use properties with a star prefix. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1966,11 +2062,12 @@ CSSLint.addRule({ }); } }); + /* * Rule: Don't use text-indent for image replacement if you need to support rtl. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1986,13 +2083,13 @@ CSSLint.addRule({ direction; - function startRule(event){ + function startRule(){ textIndent = false; direction = "inherit"; } //event handler for end of rules - function endRule(event){ + 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); } @@ -2019,11 +2116,12 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use properties with a underscore prefix. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -2046,10 +2144,11 @@ CSSLint.addRule({ }); } }); + /* * Rule: Headings (h1-h6) should be defined only once. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -2062,7 +2161,7 @@ CSSLint.addRule({ init: function(parser, reporter){ var rule = this; - var headings = { + var headings = { h1: 0, h2: 0, h3: 0, @@ -2101,7 +2200,7 @@ CSSLint.addRule({ } }); - parser.addListener("endstylesheet", function(event){ + parser.addListener("endstylesheet", function(){ var prop, messages = []; @@ -2120,10 +2219,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use universal selector because it's slow. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -2140,8 +2240,7 @@ CSSLint.addRule({ var selectors = event.selectors, selector, part, - modifier, - i, j, k; + i; for (i=0; i < selectors.length; i++){ selector = selectors[i]; @@ -2155,10 +2254,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use unqualified attribute selectors because they're just like universal selectors. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -2177,7 +2277,7 @@ CSSLint.addRule({ selector, part, modifier, - i, j, k; + i, k; for (i=0; i < selectors.length; i++){ selector = selectors[i]; @@ -2197,11 +2297,12 @@ CSSLint.addRule({ } }); + /* * Rule: When using a vendor-prefixed property, make sure to * include the standard one. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -2272,11 +2373,7 @@ CSSLint.addRule({ "-ms-transform-origin" : "transform-origin", "-moz-box-sizing" : "box-sizing", - "-webkit-box-sizing" : "box-sizing", - - "-moz-user-select" : "user-select", - "-khtml-user-select" : "user-select", - "-webkit-user-select" : "user-select" + "-webkit-box-sizing" : "box-sizing" }; //event handler for beginning of rules @@ -2286,10 +2383,10 @@ CSSLint.addRule({ } //event handler for end of rules - function endRule(event){ + function endRule(){ var prop, - i, len, - standard, + i, + len, needed, actual, needsStandard = []; @@ -2340,10 +2437,11 @@ CSSLint.addRule({ } }); + /* * Rule: You don't need to specify units when a value is 0. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -2374,7 +2472,7 @@ CSSLint.addRule({ } }); -/*global CSSLint*/ + (function() { /** @@ -2446,7 +2544,7 @@ CSSLint.addRule({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = []; @@ -2458,20 +2556,20 @@ CSSLint.addRule({ * @return rule source as {String} */ var generateSource = function(rule) { - if (!rule || !('name' in rule)) { + if (!rule || !("name" in rule)) { return ""; } - return 'net.csslint.' + rule.name.replace(/\s/g,''); + return "net.csslint." + rule.name.replace(/\s/g,""); }; if (messages.length > 0) { output.push(""); - CSSLint.Util.forEach(messages, function (message, i) { + CSSLint.Util.forEach(messages, function (message) { //ignore rollups for now if (!message.rollup) { - output.push(""); } }); @@ -2483,7 +2581,7 @@ CSSLint.addRule({ }); }()); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "compact", @@ -2530,19 +2628,19 @@ CSSLint.addFormatter({ return options.quiet ? "" : filename + ": Lint Free!"; } - CSSLint.Util.forEach(messages, function(message, i) { + CSSLint.Util.forEach(messages, function(message) { if (message.rollup) { output += filename + ": " + capitalize(message.type) + " - " + message.message + "\n"; } else { output += filename + ": " + "line " + message.line + - ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + "\n"; + ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n"; } }); return output; } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "csslint-xml", @@ -2571,7 +2669,7 @@ CSSLint.addFormatter({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = []; @@ -2596,7 +2694,7 @@ CSSLint.addFormatter({ if (messages.length > 0) { output.push(""); - CSSLint.Util.forEach(messages, function (message, i) { + CSSLint.Util.forEach(messages, function (message) { if (message.rollup) { output.push(""); } else { @@ -2610,7 +2708,7 @@ CSSLint.addFormatter({ return output.join(""); } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "junit-xml", @@ -2639,13 +2737,13 @@ CSSLint.addFormatter({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = [], tests = { - 'error': 0, - 'failure': 0 + "error": 0, + "failure": 0 }; /** @@ -2656,10 +2754,10 @@ CSSLint.addFormatter({ * @return rule source as {String} */ var generateSource = function(rule) { - if (!rule || !('name' in rule)) { + if (!rule || !("name" in rule)) { return ""; } - return 'net.csslint.' + rule.name.replace(/\s/g,''); + return "net.csslint." + rule.name.replace(/\s/g,""); }; /** @@ -2685,11 +2783,11 @@ CSSLint.addFormatter({ if (messages.length > 0) { - messages.forEach(function (message, i) { + messages.forEach(function (message) { // since junit has no warning class // all issues as errors - var type = message.type === 'warning' ? 'error' : message.type; + var type = message.type === "warning" ? "error" : message.type; //ignore rollups for now if (!message.rollup) { @@ -2697,7 +2795,7 @@ CSSLint.addFormatter({ // build the test case seperately, once joined // we'll add it to a custom array filtered by type output.push(""); - output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\">"); + output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\">"); output.push(""); tests[type] += 1; @@ -2715,7 +2813,7 @@ CSSLint.addFormatter({ } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "lint-xml", @@ -2744,7 +2842,7 @@ CSSLint.addFormatter({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = []; @@ -2770,7 +2868,7 @@ CSSLint.addFormatter({ if (messages.length > 0) { output.push(""); - CSSLint.Util.forEach(messages, function (message, i) { + CSSLint.Util.forEach(messages, function (message) { if (message.rollup) { output.push(""); } else { @@ -2784,7 +2882,7 @@ CSSLint.addFormatter({ return output.join(""); } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "text", @@ -2822,7 +2920,14 @@ CSSLint.addFormatter({ return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + "."; } - output = "\n\ncsslint: There are " + messages.length + " problems 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; @@ -2848,4 +2953,5 @@ CSSLint.addFormatter({ return output; } }); + exports.CSSLint = CSSLint; \ No newline at end of file diff --git a/release/csslint-rhino.js b/dist/csslint-rhino.js similarity index 96% rename from release/csslint-rhino.js rename to dist/csslint-rhino.js index 587d438b..ab083c5d 100644 --- a/release/csslint-rhino.js +++ b/dist/csslint-rhino.js @@ -1,9 +1,9 @@ /*! CSSLint -Copyright (c) 2013 Nicole Sullivan and Nicholas C. Zakas. All rights reserved. +Copyright (c) 2014 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 +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 @@ -12,7 +12,7 @@ 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 +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 @@ -21,7 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* Build: v0.10.0 15-August-2013 01:07:22 */ +/* Build: v0.10.0 11-April-2014 12:11:52 */ var exports = exports || {}; var CSSLint = (function(){ /*! @@ -47,7 +47,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* Version v0.2.3, Build time: 19-June-2013 11:16:15 */ +/* Version v0.2.4, Build time: 7-January-2014 07:32:49 */ var parserlib = {}; (function(){ @@ -957,7 +957,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* Version v0.2.3, Build time: 19-June-2013 11:16:15 */ +/* Version v0.2.4, Build time: 7-January-2014 07:32:49 */ (function(){ var EventTarget = parserlib.util.EventTarget, TokenStreamBase = parserlib.util.TokenStreamBase, @@ -992,6 +992,7 @@ var Colors = { darkcyan :"#008b8b", darkgoldenrod :"#b8860b", darkgray :"#a9a9a9", + darkgrey :"#a9a9a9", darkgreen :"#006400", darkkhaki :"#bdb76b", darkmagenta :"#8b008b", @@ -1003,11 +1004,13 @@ var Colors = { darkseagreen :"#8fbc8f", darkslateblue :"#483d8b", darkslategray :"#2f4f4f", + darkslategrey :"#2f4f4f", darkturquoise :"#00ced1", darkviolet :"#9400d3", deeppink :"#ff1493", deepskyblue :"#00bfff", dimgray :"#696969", + dimgrey :"#696969", dodgerblue :"#1e90ff", firebrick :"#b22222", floralwhite :"#fffaf0", @@ -1018,6 +1021,7 @@ var Colors = { gold :"#ffd700", goldenrod :"#daa520", gray :"#808080", + grey :"#808080", green :"#008000", greenyellow :"#adff2f", honeydew :"#f0fff0", @@ -1035,12 +1039,14 @@ var Colors = { lightcyan :"#e0ffff", lightgoldenrodyellow :"#fafad2", lightgray :"#d3d3d3", + lightgrey :"#d3d3d3", lightgreen :"#90ee90", lightpink :"#ffb6c1", lightsalmon :"#ffa07a", lightseagreen :"#20b2aa", lightskyblue :"#87cefa", lightslategray :"#778899", + lightslategrey :"#778899", lightsteelblue :"#b0c4de", lightyellow :"#ffffe0", lime :"#00ff00", @@ -1093,6 +1099,7 @@ var Colors = { skyblue :"#87ceeb", slateblue :"#6a5acd", slategray :"#708090", + slategrey :"#708090", snow :"#fffafa", springgreen :"#00ff7f", steelblue :"#4682b4", @@ -1118,6 +1125,7 @@ var Colors = { buttontext :"Text on push buttons.", captiontext :"Text in caption, size box, and scrollbar arrow box.", graytext :"Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.", + greytext :"Greyed (disabled) text. This color is set to #000 if the current display driver does not support a solid grey color.", highlight :"Item(s) selected in a control.", highlighttext :"Text of item(s) selected in a control.", inactiveborder :"Inactive window border.", @@ -2812,7 +2820,7 @@ Parser.prototype = function(){ var tokenStream = this._tokenStream, values = [], - //valueParts = [], + //valueParts = [], value = null, operator = null; @@ -2829,9 +2837,9 @@ Parser.prototype = function(){ values.push(operator); } /*else { //if there's not an operator, you have a full value - values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col)); - valueParts = []; - }*/ + values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col)); + valueParts = []; + }*/ value = this._term(); @@ -2843,7 +2851,7 @@ Parser.prototype = function(){ } while(true); } - //cleanup + //cleanup /*if (valueParts.length){ values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col)); }*/ @@ -3534,6 +3542,12 @@ nth var Properties = { //A + "align-items" : "flex-start | flex-end | center | baseline | stretch", + "align-content" : "flex-start | flex-end | center | space-between | space-around | stretch", + "align-self" : "auto | flex-start | flex-end | center | baseline | stretch", + "-webkit-align-items" : "flex-start | flex-end | center | baseline | stretch", + "-webkit-align-content" : "flex-start | flex-end | center | space-between | space-around | stretch", + "-webkit-align-self" : "auto | flex-start | flex-end | center | baseline | stretch", "alignment-adjust" : "auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | | ", "alignment-baseline" : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical", "animation" : 1, @@ -3729,15 +3743,24 @@ var Properties = { "border-top-width" : "", "border-width" : { multi: "", max: 4 }, "bottom" : " | inherit", - "box-align" : "start | end | center | baseline | stretch", //http://www.w3.org/TR/2009/WD-css3-flexbox-20090723/ - "box-decoration-break" : "slice |clone", - "box-direction" : "normal | reverse | inherit", - "box-flex" : "", - "box-flex-group" : "", - "box-lines" : "single | multiple", - "box-ordinal-group" : "", - "box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit", - "box-pack" : "start | end | center | justify", + "-moz-box-align" : "start | end | center | baseline | stretch", + "-moz-box-decoration-break" : "slice |clone", + "-moz-box-direction" : "normal | reverse | inherit", + "-moz-box-flex" : "", + "-moz-box-flex-group" : "", + "-moz-box-lines" : "single | multiple", + "-moz-box-ordinal-group" : "", + "-moz-box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit", + "-moz-box-pack" : "start | end | center | justify", + "-webkit-box-align" : "start | end | center | baseline | stretch", + "-webkit-box-decoration-break" : "slice |clone", + "-webkit-box-direction" : "normal | reverse | inherit", + "-webkit-box-flex" : "", + "-webkit-box-flex-group" : "", + "-webkit-box-lines" : "single | multiple", + "-webkit-box-ordinal-group" : "", + "-webkit-box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit", + "-webkit-box-pack" : "start | end | center | justify", "box-shadow" : function (expression) { var result = false, part; @@ -3783,7 +3806,7 @@ var Properties = { //D "direction" : "ltr | rtl | inherit", - "display" : "inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | box | inline-box | grid | inline-grid | none | inherit | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker | -webkit-box | -webkit-inline-box", + "display" : "inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | grid | inline-grid | none | inherit | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker | -webkit-box | -webkit-inline-box | -ms-flexbox | -ms-inline-flexbox | flex | -webkit-flex | inline-flex | -webkit-inline-flex", "dominant-baseline" : 1, "drop-initial-after-adjust" : "central | middle | after-edge | text-after-edge | ideographic | alphabetic | mathematical | | ", "drop-initial-after-align" : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical", @@ -3800,6 +3823,26 @@ var Properties = { "filter" : 1, "fit" : "fill | hidden | meet | slice", "fit-position" : 1, + "flex" : "none | [ ? || ", + "flex-basis" : "", + "flex-direction" : "row | row-reverse | column | column-reverse", + "flex-flow" : " || ", + "flex-grow" : "", + "flex-shrink" : "", + "flex-wrap" : "nowrap | wrap | wrap-reverse", + "-webkit-flex" : "none | [ ? || ", + "-webkit-flex-basis" : "", + "-webkit-flex-direction" : "row | row-reverse | column | column-reverse", + "-webkit-flex-flow" : " || ", + "-webkit-flex-grow" : "", + "-webkit-flex-shrink" : "", + "-webkit-flex-wrap" : "nowrap | wrap | wrap-reverse", + "-ms-flex" : "[[ ? ] || [ || || auto ] ] | none", + "-ms-flex-align" : "start | end | center | stretch | baseline", + "-ms-flex-direction" : "row | column | row-reverse | column-reverse | inherit", + "-ms-flex-order" : "", + "-ms-flex-pack" : "start | end | center | justify", + "-ms-flex-wrap" : "nowrap | wrap | wrap-reverse", "float" : "left | right | none | inherit", "float-offset" : 1, "font" : 1, @@ -3843,6 +3886,10 @@ var Properties = { "image-resolution" : 1, "inline-box-align" : "initial | last | ", + //J + "justify-content" : "flex-start | flex-end | center | space-between | space-around", + "-webkit-justify-content" : "flex-start | flex-end | center | space-between | space-around", + //L "left" : " | inherit", "letter-spacing" : " | normal | inherit", @@ -3886,6 +3933,8 @@ var Properties = { //O "opacity" : " | inherit", + "order" : "", + "-webkit-order" : "", "orphans" : " | inherit", "outline" : 1, "outline-color" : " | invert | inherit", @@ -3894,6 +3943,7 @@ var Properties = { "outline-width" : " | inherit", "overflow" : "visible | hidden | scroll | auto | inherit", "overflow-style" : 1, + "overflow-wrap" : "normal | break-word", "overflow-x" : 1, "overflow-y" : 1, @@ -3971,6 +4021,8 @@ var Properties = { "text-transform" : "capitalize | uppercase | lowercase | none | inherit", "text-wrap" : "normal | none | avoid", "top" : " | inherit", + "-ms-touch-action" : "auto | none | pan-x | pan-y", + "touch-action" : "auto | none | pan-x | pan-y", "transform" : 1, "transform-origin" : 1, "transform-style" : 1, @@ -3981,7 +4033,7 @@ var Properties = { "transition-timing-function" : 1, //U - "unicode-bidi" : "normal | embed | bidi-override | inherit", + "unicode-bidi" : "normal | embed | isolate | bidi-override | isolate-override | plaintext | inherit", "user-modify" : "read-only | read-write | write-only | inherit", "user-select" : "none | text | toggle | element | elements | all | inherit", @@ -4005,7 +4057,8 @@ var Properties = { "width" : " | | auto | inherit" , "word-break" : "normal | keep-all | break-all", "word-spacing" : " | normal | inherit", - "word-wrap" : 1, + "word-wrap" : "normal | break-word", + "writing-mode" : "horizontal-tb | vertical-rl | vertical-lr | lr-tb | rl-tb | tb-rl | bt-rl | tb-lr | bt-lr | lr-bt | rl-bt | lr | rl | tb | inherit", //Z "z-index" : " | auto | inherit", @@ -4276,9 +4329,6 @@ function PropertyValuePart(text, line, col){ } - } else if (/^([+\-]?[\d\.]+)%$/i.test(text)){ //percentage - this.type = "percentage"; - this.value = +RegExp.$1; } else if (/^([+\-]?[\d\.]+)%$/i.test(text)){ //percentage this.type = "percentage"; this.value = +RegExp.$1; @@ -4375,6 +4425,7 @@ PropertyValuePart.prototype.constructor = PropertyValuePart; PropertyValuePart.fromToken = function(token){ return new PropertyValuePart(token.value, token.startLine, token.startCol); }; + var Pseudos = { ":first-letter": 1, ":first-line": 1, @@ -4668,12 +4719,12 @@ function isIdentStart(c){ } function mix(receiver, supplier){ - for (var prop in supplier){ - if (supplier.hasOwnProperty(prop)){ - receiver[prop] = supplier[prop]; - } - } - return receiver; + for (var prop in supplier){ + if (supplier.hasOwnProperty(prop)){ + receiver[prop] = supplier[prop]; + } + } + return receiver; } //----------------------------------------------------------------------------- @@ -4689,7 +4740,7 @@ function mix(receiver, supplier){ * @namespace parserlib.css */ function TokenStream(input){ - TokenStreamBase.call(this, input, Tokens); + TokenStreamBase.call(this, input, Tokens); } TokenStream.prototype = mix(new TokenStreamBase(), { @@ -5662,7 +5713,7 @@ var Tokens = [ { name: "FONT_FACE_SYM", text: "@font-face"}, { name: "CHARSET_SYM", text: "@charset"}, { name: "NAMESPACE_SYM", text: "@namespace"}, - { name: "VIEWPORT_SYM", text: "@viewport"}, + { name: "VIEWPORT_SYM", text: ["@viewport", "@-ms-viewport"]}, { name: "UNKNOWN_SYM" }, //{ name: "ATKEYWORD"}, @@ -6436,7 +6487,10 @@ exports[prop] = parserlib[prop]; * @static * @extends parserlib.util.EventTarget */ -/*global parserlib, Reporter*/ + +/* global parserlib, Reporter */ +/* exported CSSLint */ + var CSSLint = (function(){ var rules = [], @@ -6602,8 +6656,7 @@ var CSSLint = (function(){ */ api.verify = function(text, ruleset){ - var i = 0, - len = rules.length, + var i = 0, reporter, lines, report, @@ -6611,7 +6664,7 @@ var CSSLint = (function(){ underscoreHack: true, strict: false }); // normalize line endings - lines = text.replace(/\n\r?/g, "$split$").split('$split$'); + lines = text.replace(/\n\r?/g, "$split$").split("$split$"); if (!ruleset){ ruleset = this.getRuleset(); @@ -6668,7 +6721,6 @@ var CSSLint = (function(){ })(); -/*global CSSLint*/ /** * An instance of Report is used to report results of the * verification back to the main API. @@ -6830,8 +6882,6 @@ Reporter.prototype = { //expose for testing purposes CSSLint._Reporter = Reporter; -/*global CSSLint*/ - /* * Utility functions that make life easier. */ @@ -6891,10 +6941,11 @@ CSSLint.Util = { } } }; -/*global CSSLint*/ + /* * Rule: Don't use adjoining classes (.foo.bar). */ + CSSLint.addRule({ //rule information @@ -6936,7 +6987,6 @@ CSSLint.addRule({ } }); -/*global CSSLint*/ /* * Rule: Don't use width or height when using padding or border. @@ -7037,11 +7087,11 @@ CSSLint.addRule({ } }); -/*global CSSLint*/ /* * Rule: box-sizing doesn't work in IE6 and IE7. */ + CSSLint.addRule({ //rule information @@ -7065,11 +7115,12 @@ CSSLint.addRule({ } }); + /* * 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) */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7081,14 +7132,13 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ var rule = this, - count = 0, 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(event){ + parser.addListener("startfontface", function(){ fontFaceRule = true; }); @@ -7106,7 +7156,7 @@ CSSLint.addRule({ col = event.col; // This is the property that we care about, we can ignore the rest - if (propertyName === 'src') { + if (propertyName === "src") { var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i; // We need to handle the advanced syntax with two src properties @@ -7122,7 +7172,7 @@ CSSLint.addRule({ }); // Back to normal rules that we don't need to test - parser.addListener("endfontface", function(event){ + parser.addListener("endfontface", function(){ fontFaceRule = false; if (ruleFailed) { @@ -7131,11 +7181,12 @@ CSSLint.addRule({ }); } }); + /* * Rule: Include all compatible vendor prefixes to reach a wider * range of users. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7223,9 +7274,9 @@ CSSLint.addRule({ for (prop in compatiblePrefixes) { if (compatiblePrefixes.hasOwnProperty(prop)) { variations = []; - prefixed = compatiblePrefixes[prop].split(' '); + prefixed = compatiblePrefixes[prop].split(" "); for (i = 0, len = prefixed.length; i < len; i++) { - variations.push('-' + prefixed[i] + '-' + prop); + variations.push("-" + prefixed[i] + "-" + prop); } compatiblePrefixes[prop] = variations; arrayPush.apply(applyTo, variations); @@ -7240,7 +7291,7 @@ CSSLint.addRule({ inKeyFrame = event.prefix || true; }); - parser.addListener("endkeyframes", function (event) { + parser.addListener("endkeyframes", function () { inKeyFrame = false; }); @@ -7256,7 +7307,7 @@ CSSLint.addRule({ } }); - parser.addListener("endrule", function (event) { + parser.addListener("endrule", function () { if (!properties.length) { return; } @@ -7317,6 +7368,7 @@ CSSLint.addRule({ }); } }); + /* * Rule: Certain properties don't play well with certain display values. * - float should not be used with inline-block @@ -7324,7 +7376,7 @@ CSSLint.addRule({ * - vertical-align should not be used with block * - margin, float should not be used with table-* */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7434,10 +7486,11 @@ CSSLint.addRule({ } }); + /* * Rule: Disallow duplicate background-images (using url). */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7458,8 +7511,8 @@ CSSLint.addRule({ 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') { + if (value.parts[i].type == "uri") { + if (typeof stack[value.parts[i].uri] === "undefined") { stack[value.parts[i].uri] = event; } else { @@ -7471,11 +7524,12 @@ CSSLint.addRule({ }); } }); + /* * 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. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7490,7 +7544,7 @@ CSSLint.addRule({ properties, lastProperty; - function startRule(event){ + function startRule(){ properties = {}; } @@ -7517,10 +7571,11 @@ CSSLint.addRule({ } }); + /* * Rule: Style rules without any properties defined should be removed. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7551,10 +7606,11 @@ CSSLint.addRule({ } }); + /* * Rule: There should be no syntax errors. (Duh.) */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7575,7 +7631,6 @@ CSSLint.addRule({ }); -/*global CSSLint*/ CSSLint.addRule({ //rule information @@ -7605,7 +7660,7 @@ CSSLint.addRule({ }, properties; - function startRule(event){ + function startRule(){ properties = {}; lastProperty = null; } @@ -7651,11 +7706,12 @@ CSSLint.addRule({ } }); + /* * Rule: You shouldn't use more than 10 floats. If you do, there's probably * room for some abstraction. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7687,10 +7743,11 @@ CSSLint.addRule({ } }); + /* * Rule: Avoid too many @font-face declarations in the same stylesheet. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7717,11 +7774,11 @@ CSSLint.addRule({ } }); + /* * Rule: You shouldn't need more than 9 font-size declarations. */ -/*global CSSLint*/ CSSLint.addRule({ //rule information @@ -7752,10 +7809,11 @@ CSSLint.addRule({ } }); + /* * Rule: When using a vendor-prefixed gradient, make sure to use them all. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7820,7 +7878,7 @@ CSSLint.addRule({ /* * Rule: Don't use IDs for selectors. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7867,10 +7925,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use @import, use instead. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7890,12 +7949,13 @@ CSSLint.addRule({ } }); + /* * 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. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7927,11 +7987,12 @@ CSSLint.addRule({ } }); + /* * Rule: Properties should be known (listed in CSS3 specification) or * be a vendor-prefixed property. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7945,7 +8006,6 @@ CSSLint.addRule({ var rule = this; parser.addListener("property", function(event){ - var name = event.property.text.toLowerCase(); // the check is handled entirely by the parser-lib (https://github.com/nzakas/parser-lib) if (event.invalid) { @@ -7956,11 +8016,58 @@ CSSLint.addRule({ } }); + +/* + * Rule: All properties should be in alphabetical order.. + */ +/*global CSSLint*/ +CSSLint.addRule({ + + //rule information + id: "order-alphabetical", + name: "Alphabetical order", + desc: "Assure properties are in alphabetical order", + browsers: "All", + + //initialization + init: function(parser, reporter){ + var rule = this, + properties; + + var startRule = function () { + properties = []; + }; + + parser.addListener("startrule", startRule); + parser.addListener("startfontface", startRule); + parser.addListener("startpage", startRule); + parser.addListener("startpagemargin", startRule); + parser.addListener("startkeyframerule", startRule); + + parser.addListener("property", function(event){ + var name = event.property.text, + lowerCasePrefixLessName = name.toLowerCase().replace(/^-.*?-/, ""); + + properties.push(lowerCasePrefixLessName); + }); + + parser.addListener("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 ordered.", event.line, event.col, rule); + } + }); + } + +}); + /* * 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. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7989,7 +8096,7 @@ CSSLint.addRule({ } } - function endRule(event){ + function endRule(){ if (lastRule){ if (lastRule.outline){ if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") == -1){ @@ -8029,10 +8136,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use classes or IDs with elements (a.foo or a#foo). */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8092,10 +8200,11 @@ CSSLint.addRule({ } }); + /* * Rule: Headings (h1-h6) should not be qualified (namespaced). */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8130,10 +8239,11 @@ CSSLint.addRule({ } }); + /* * Rule: Selectors that look like regular expressions are slow and should be avoided. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8174,10 +8284,11 @@ CSSLint.addRule({ } }); + /* * Rule: Total number of rules should not exceed x. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8188,8 +8299,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ - var rule = this, - count = 0; + var count = 0; //count each rule parser.addListener("startrule", function(){ @@ -8202,10 +8312,11 @@ CSSLint.addRule({ } }); + /* * Rule: Warn people with approaching the IE 4095 limit */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8218,7 +8329,7 @@ CSSLint.addRule({ init: function(parser, reporter) { var rule = this, count = 0; - parser.addListener('startrule', function(event) { + parser.addListener("startrule", function(event) { count += event.selectors.length; }); @@ -8234,7 +8345,7 @@ CSSLint.addRule({ /* * Rule: Warn people past the IE 4095 limit */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8247,7 +8358,7 @@ CSSLint.addRule({ init: function(parser, reporter){ var rule = this, count = 0; - parser.addListener('startrule',function(event) { + parser.addListener("startrule", function(event) { count += event.selectors.length; }); @@ -8259,11 +8370,47 @@ CSSLint.addRule({ } }); + +/* + * 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) { + var rule = this; + + parser.addListener("startrule", function(event) { + var i, len, selector, p, pLen, part, previousLine, currentLine, + 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++) { + part = selector.parts[p]; + currentLine = part.line; + if (currentLine === previousLine) { + reporter.report("newline character found in selector (forgot a comma?)", currentLine, selectors[i].parts[0].col, rule); + } + previousLine = currentLine; + } + } + }); + } +}); + /* * Rule: Use shorthand properties where possible. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8302,7 +8449,7 @@ CSSLint.addRule({ } } - function startRule(event){ + function startRule(){ properties = {}; } @@ -8332,8 +8479,7 @@ CSSLint.addRule({ //check for use of "font-size" parser.addListener("property", function(event){ - var name = event.property.toString().toLowerCase(), - value = event.value.parts[0].value; + var name = event.property.toString().toLowerCase(); if (propertiesToCheck[name]){ properties[name] = 1; @@ -8346,11 +8492,12 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use properties with a star prefix. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8373,11 +8520,12 @@ CSSLint.addRule({ }); } }); + /* * Rule: Don't use text-indent for image replacement if you need to support rtl. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8393,13 +8541,13 @@ CSSLint.addRule({ direction; - function startRule(event){ + function startRule(){ textIndent = false; direction = "inherit"; } //event handler for end of rules - function endRule(event){ + 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); } @@ -8426,11 +8574,12 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use properties with a underscore prefix. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8453,10 +8602,11 @@ CSSLint.addRule({ }); } }); + /* * Rule: Headings (h1-h6) should be defined only once. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8469,7 +8619,7 @@ CSSLint.addRule({ init: function(parser, reporter){ var rule = this; - var headings = { + var headings = { h1: 0, h2: 0, h3: 0, @@ -8508,7 +8658,7 @@ CSSLint.addRule({ } }); - parser.addListener("endstylesheet", function(event){ + parser.addListener("endstylesheet", function(){ var prop, messages = []; @@ -8527,10 +8677,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use universal selector because it's slow. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8547,8 +8698,7 @@ CSSLint.addRule({ var selectors = event.selectors, selector, part, - modifier, - i, j, k; + i; for (i=0; i < selectors.length; i++){ selector = selectors[i]; @@ -8562,10 +8712,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use unqualified attribute selectors because they're just like universal selectors. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8584,7 +8735,7 @@ CSSLint.addRule({ selector, part, modifier, - i, j, k; + i, k; for (i=0; i < selectors.length; i++){ selector = selectors[i]; @@ -8604,11 +8755,12 @@ CSSLint.addRule({ } }); + /* * Rule: When using a vendor-prefixed property, make sure to * include the standard one. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8679,11 +8831,7 @@ CSSLint.addRule({ "-ms-transform-origin" : "transform-origin", "-moz-box-sizing" : "box-sizing", - "-webkit-box-sizing" : "box-sizing", - - "-moz-user-select" : "user-select", - "-khtml-user-select" : "user-select", - "-webkit-user-select" : "user-select" + "-webkit-box-sizing" : "box-sizing" }; //event handler for beginning of rules @@ -8693,10 +8841,10 @@ CSSLint.addRule({ } //event handler for end of rules - function endRule(event){ + function endRule(){ var prop, - i, len, - standard, + i, + len, needed, actual, needsStandard = []; @@ -8747,10 +8895,11 @@ CSSLint.addRule({ } }); + /* * Rule: You don't need to specify units when a value is 0. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8781,7 +8930,7 @@ CSSLint.addRule({ } }); -/*global CSSLint*/ + (function() { /** @@ -8853,7 +9002,7 @@ CSSLint.addRule({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = []; @@ -8865,20 +9014,20 @@ CSSLint.addRule({ * @return rule source as {String} */ var generateSource = function(rule) { - if (!rule || !('name' in rule)) { + if (!rule || !("name" in rule)) { return ""; } - return 'net.csslint.' + rule.name.replace(/\s/g,''); + return "net.csslint." + rule.name.replace(/\s/g,""); }; if (messages.length > 0) { output.push(""); - CSSLint.Util.forEach(messages, function (message, i) { + CSSLint.Util.forEach(messages, function (message) { //ignore rollups for now if (!message.rollup) { - output.push(""); } }); @@ -8890,7 +9039,7 @@ CSSLint.addRule({ }); }()); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "compact", @@ -8937,19 +9086,19 @@ CSSLint.addFormatter({ return options.quiet ? "" : filename + ": Lint Free!"; } - CSSLint.Util.forEach(messages, function(message, i) { + CSSLint.Util.forEach(messages, function(message) { if (message.rollup) { output += filename + ": " + capitalize(message.type) + " - " + message.message + "\n"; } else { output += filename + ": " + "line " + message.line + - ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + "\n"; + ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n"; } }); return output; } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "csslint-xml", @@ -8978,7 +9127,7 @@ CSSLint.addFormatter({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = []; @@ -9003,7 +9152,7 @@ CSSLint.addFormatter({ if (messages.length > 0) { output.push(""); - CSSLint.Util.forEach(messages, function (message, i) { + CSSLint.Util.forEach(messages, function (message) { if (message.rollup) { output.push(""); } else { @@ -9017,7 +9166,7 @@ CSSLint.addFormatter({ return output.join(""); } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "junit-xml", @@ -9046,13 +9195,13 @@ CSSLint.addFormatter({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = [], tests = { - 'error': 0, - 'failure': 0 + "error": 0, + "failure": 0 }; /** @@ -9063,10 +9212,10 @@ CSSLint.addFormatter({ * @return rule source as {String} */ var generateSource = function(rule) { - if (!rule || !('name' in rule)) { + if (!rule || !("name" in rule)) { return ""; } - return 'net.csslint.' + rule.name.replace(/\s/g,''); + return "net.csslint." + rule.name.replace(/\s/g,""); }; /** @@ -9092,11 +9241,11 @@ CSSLint.addFormatter({ if (messages.length > 0) { - messages.forEach(function (message, i) { + messages.forEach(function (message) { // since junit has no warning class // all issues as errors - var type = message.type === 'warning' ? 'error' : message.type; + var type = message.type === "warning" ? "error" : message.type; //ignore rollups for now if (!message.rollup) { @@ -9104,7 +9253,7 @@ CSSLint.addFormatter({ // build the test case seperately, once joined // we'll add it to a custom array filtered by type output.push(""); - output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\">"); + output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\">"); output.push(""); tests[type] += 1; @@ -9122,7 +9271,7 @@ CSSLint.addFormatter({ } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "lint-xml", @@ -9151,7 +9300,7 @@ CSSLint.addFormatter({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = []; @@ -9177,7 +9326,7 @@ CSSLint.addFormatter({ if (messages.length > 0) { output.push(""); - CSSLint.Util.forEach(messages, function (message, i) { + CSSLint.Util.forEach(messages, function (message) { if (message.rollup) { output.push(""); } else { @@ -9191,7 +9340,7 @@ CSSLint.addFormatter({ return output.join(""); } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "text", @@ -9229,7 +9378,14 @@ CSSLint.addFormatter({ return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + "."; } - output = "\n\ncsslint: There are " + messages.length + " problems 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; @@ -9255,13 +9411,16 @@ CSSLint.addFormatter({ return output; } }); + return CSSLint; })(); /* * Encapsulates all of the CLI functionality. The api argument simply * provides environment-specific functionality. */ -/*global CSSLint*/ + +/* exported cli */ + function cli(api){ var globalOptions = { @@ -9430,8 +9589,8 @@ function cli(api){ */ function outputHelp(){ var lenToPad = 40, - toPrint = '', - formatString = ''; + toPrint = "", + formatString = ""; api.print([ "\nUsage: csslint-rhino.js [options]* [file|dir]*", @@ -9444,14 +9603,14 @@ function cli(api){ // Print the option name and the format if present toPrint += " --" + optionName; if (globalOptions[optionName].format !== "") { - formatString = '=' + globalOptions[optionName].format; + formatString = "=" + globalOptions[optionName].format; toPrint += formatString; } else { - formatString = ''; + formatString = ""; } // Pad out with the appropriate number of spaces - toPrint += new Array(lenToPad - (optionName.length + formatString.length)).join(' '); + toPrint += new Array(lenToPad - (optionName.length + formatString.length)).join(" "); // Print the description toPrint += globalOptions[optionName].description + "\n"; @@ -9541,9 +9700,9 @@ function cli(api){ } function validateOptions(options) { - for (var option_key in options) { - if (!globalOptions.hasOwnProperty(option_key) && option_key !== 'files') { - api.print(option_key + ' is not a valid option. Exiting...'); + for (var optionKey in options) { + if (!globalOptions.hasOwnProperty(optionKey) && optionKey !== "files") { + api.print(optionKey + " is not a valid option. Exiting..."); outputHelp(); api.quit(0); } @@ -9551,8 +9710,20 @@ function cli(api){ } function readConfigFile(options) { - var data = api.readFile(api.getFullPath(".csslintrc")); + var data = api.readFile(api.getFullPath(".csslintrc")), + json; if (data) { + if (data.charAt(0) === "{") { + try { + json = JSON.parse(data); + data = ""; + for (var optionName in json) { + if (json.hasOwnProperty(optionName)) { + data += "--" + optionName + "=" + json[optionName].join(); + } + } + } catch(e) {} + } options = processArguments(data.split(/[\s\n\r]+/m), options); } @@ -9599,8 +9770,9 @@ function cli(api){ /* * CSSLint Rhino Command Line Interface */ -/*jshint rhino:true*/ -/*global cli, File*/ + +/* jshint rhino:true */ +/* global cli, File */ importPackage(java.io); diff --git a/release/csslint-tests.js b/dist/csslint-tests.js similarity index 95% rename from release/csslint-tests.js rename to dist/csslint-tests.js index ead4839e..4f8a8aa1 100644 --- a/release/csslint-tests.js +++ b/dist/csslint-tests.js @@ -1,6 +1,4 @@ (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -19,13 +17,13 @@ "Embedded ruleset should be honored": function(){ var result = CSSLint.verify("/*csslint bogus, adjoining-classes:true, box-sizing:false */\n.foo.bar{}", { - 'text-indent': 1, - 'box-sizing': 1 + "text-indent": 1, + "box-sizing": 1 }); - Assert.areEqual(2, result.ruleset['adjoining-classes']); - Assert.areEqual(1, result.ruleset['text-indent']); - Assert.areEqual(0, result.ruleset['box-sizing']); + Assert.areEqual(2, result.ruleset["adjoining-classes"]); + Assert.areEqual(1, result.ruleset["text-indent"]); + Assert.areEqual(0, result.ruleset["box-sizing"]); } })); @@ -33,8 +31,6 @@ })(); (function(){ - - /*global YUITest, CSSLint, Reporter*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -70,8 +66,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -86,9 +80,9 @@ "File with problems should list them": function(){ var result = { messages: [ - { type: "warning", line: 1, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: { name: "A Rule"} }, - { type: "error", line: 2, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: { name: "Some Other Rule"} } - ], stats: [] }, + { type: "warning", line: 1, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: { name: "A Rule"} }, + { type: "error", line: 2, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: { name: "Some Other Rule"} } + ], stats: [] }, file = "", error1 = "", error2 = "", @@ -98,14 +92,14 @@ }, "Formatter should escape special characters": function() { - var specialCharsSting = 'sneaky, "sneaky", , sneak & sneaky', + var specialCharsSting = "sneaky, 'sneaky', , sneak & sneaky", result = { messages: [ - { type: "warning", line: 1, col: 1, message: specialCharsSting, evidence: "ALSO BOGUS", rule: [] }, - { type: "error", line: 2, col: 1, message: specialCharsSting, evidence: "ALSO BOGUS", rule: [] } - ], stats: [] }, + { type: "warning", line: 1, col: 1, message: specialCharsSting, evidence: "ALSO BOGUS", rule: [] }, + { type: "error", line: 2, col: 1, message: specialCharsSting, evidence: "ALSO BOGUS", rule: [] } + ], stats: [] }, file = "", - error1 = "", - error2 = "", + error1 = "", + error2 = "", expected = "" + file + error1 + error2 + "", actual = CSSLint.format(result, "FILE", "checkstyle-xml"); Assert.areEqual(expected, actual); @@ -115,8 +109,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -136,11 +128,11 @@ "File with problems should list them": function() { var result = { messages: [ - { type: 'error', line: 2, col: 1, message: 'BOGUS ERROR', evidence: 'BOGUS', rule: [] }, - { type: 'warning', line: 1, col: 1, message: 'BOGUS WARNING', evidence: 'BOGUS', rule: [] } - ], stats: [] }, - err = "path/to/FILE: line 2, col 1, Error - BOGUS ERROR\n", - warning = "path/to/FILE: line 1, col 1, Warning - BOGUS WARNING\n", + { type: "error", line: 2, col: 1, message: "BOGUS ERROR", evidence: "BOGUS", rule: { id: "BOGUS_RULE_ID" } }, + { type: "warning", line: 1, col: 1, message: "BOGUS WARNING", evidence: "BOGUS", rule: { id: "BOGUS_RULE_ID" } } + ], stats: [] }, + err = "path/to/FILE: line 2, col 1, Error - BOGUS ERROR (BOGUS_RULE_ID)\n", + warning = "path/to/FILE: line 1, col 1, Warning - BOGUS WARNING (BOGUS_RULE_ID)\n", expected = err + warning, actual = CSSLint.getFormatter("compact").formatResults(result, "path/to/FILE", {fullPath: "/absolute/path/to/FILE"}); Assert.areEqual(expected, actual); @@ -148,11 +140,11 @@ "Should output relative file paths": function() { var result = { messages: [ - { type: 'error', line: 2, col: 1, message: 'BOGUS ERROR', evidence: 'BOGUS', rule: [] }, - { type: 'warning', line: 1, col: 1, message: 'BOGUS WARNING', evidence: 'BOGUS', rule: [] } - ], stats: [] }, - err = "path/to/FILE: line 2, col 1, Error - BOGUS ERROR\n", - warning = "path/to/FILE: line 1, col 1, Warning - BOGUS WARNING\n", + { type: "error", line: 2, col: 1, message: "BOGUS ERROR", evidence: "BOGUS", rule: { id: "BOGUS_RULE_ID" } }, + { type: "warning", line: 1, col: 1, message: "BOGUS WARNING", evidence: "BOGUS", rule: { id: "BOGUS_RULE_ID" } } + ], stats: [] }, + err = "path/to/FILE: line 2, col 1, Error - BOGUS ERROR (BOGUS_RULE_ID)\n", + warning = "path/to/FILE: line 1, col 1, Warning - BOGUS WARNING (BOGUS_RULE_ID)\n", expected = err + warning, actual = CSSLint.getFormatter("compact").formatResults(result, "path/to/FILE", {fullPath: "/absolute/path/to/FILE"}); Assert.areEqual(expected, actual); @@ -163,8 +155,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -178,9 +168,9 @@ "File with problems should list them": function(){ var result = { messages: [ - { type: "warning", line: 1, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: [] }, - { type: "error", line: 2, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: [] } - ], stats: [] }, + { type: "warning", line: 1, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: [] }, + { type: "error", line: 2, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: [] } + ], stats: [] }, file = "", error1 = "", error2 = "", @@ -190,11 +180,11 @@ }, "Formatter should escape double quotes": function() { - var doubleQuotedEvidence = 'sneaky, "sneaky", , sneak & sneaky', + var doubleQuotedEvidence = "sneaky, \"sneaky\", , sneak & sneaky", result = { messages: [ - { type: "warning", line: 1, col: 1, message: "BOGUS", evidence: doubleQuotedEvidence, rule: [] }, - { type: "error", line: 2, col: 1, message: "BOGUS", evidence: doubleQuotedEvidence, rule: [] } - ], stats: [] }, + { type: "warning", line: 1, col: 1, message: "BOGUS", evidence: doubleQuotedEvidence, rule: [] }, + { type: "error", line: 2, col: 1, message: "BOGUS", evidence: doubleQuotedEvidence, rule: [] } + ], stats: [] }, file = "", error1 = "", error2 = "", @@ -206,8 +196,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -225,9 +213,9 @@ "File with problems should list them": function(){ var result = { messages: [ - { type: "warning", line: 1, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: { name: "A Rule"} }, - { type: "error", line: 2, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: { name: "Some Other Rule"} } - ], stats: [] }, + { type: "warning", line: 1, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: { name: "A Rule"} }, + { type: "error", line: 2, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: { name: "Some Other Rule"} } + ], stats: [] }, file = "", error1 = "", @@ -241,11 +229,11 @@ "Formatter should escape special characters": function() { - var specialCharsSting = 'sneaky, "sneaky", ', + var specialCharsSting = "sneaky, 'sneaky', ", result = { messages: [ - { type: "warning", line: 1, col: 1, message: specialCharsSting, evidence: "ALSO BOGUS", rule: [] }, - { type: "error", line: 2, col: 1, message: specialCharsSting, evidence: "ALSO BOGUS", rule: [] } - ], stats: [] }, + { type: "warning", line: 1, col: 1, message: specialCharsSting, evidence: "ALSO BOGUS", rule: [] }, + { type: "error", line: 2, col: 1, message: specialCharsSting, evidence: "ALSO BOGUS", rule: [] } + ], stats: [] }, file = "", error1 = "", @@ -261,8 +249,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -277,9 +263,9 @@ "File with problems should list them": function(){ var result = { messages: [ - { type: "warning", line: 1, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: [] }, - { type: "error", line: 2, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: [] } - ], stats: [] }, + { type: "warning", line: 1, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: [] }, + { type: "error", line: 2, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: [] } + ], stats: [] }, file = "", error1 = "", error2 = "", @@ -289,11 +275,11 @@ }, "Formatter should escape double quotes": function() { - var doubleQuotedEvidence = 'sneaky, "sneaky", , sneak & sneaky', + var doubleQuotedEvidence = "sneaky, \"sneaky\", , sneak & sneaky", result = { messages: [ - { type: "warning", line: 1, col: 1, message: "BOGUS", evidence: doubleQuotedEvidence, rule: [] }, - { type: "error", line: 2, col: 1, message: "BOGUS", evidence: doubleQuotedEvidence, rule: [] } - ], stats: [] }, + { type: "warning", line: 1, col: 1, message: "BOGUS", evidence: doubleQuotedEvidence, rule: [] }, + { type: "error", line: 2, col: 1, message: "BOGUS", evidence: doubleQuotedEvidence, rule: [] } + ], stats: [] }, file = "", error1 = "", error2 = "", @@ -305,8 +291,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -319,6 +303,16 @@ Assert.areEqual("\n\ncsslint: No errors in path/to/FILE.", actual); }, + "File with one problem should use proper grammar": function() { + var result = { messages: [ + { type: "warning", line: 1, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: [] } + ], stats: [] }, + error1 = "\n1: warning at line 1, col 1\nBOGUS\nALSO BOGUS", + expected = "\n\ncsslint: There is 1 problem in path/to/FILE.\n\nFILE" + error1, + actual = CSSLint.getFormatter("text").formatResults(result, "path/to/FILE", {fullPath: "/absolute/path/to/FILE"}); + Assert.areEqual(expected, actual); + }, + "Should have no output when quiet option is specified and no errors": function() { var result = { messages: [], stats: [] }, actual = CSSLint.getFormatter("text").formatResults(result, "path/to/FILE", {fullPath: "/absolute/path/to/FILE", quiet: "true"}); @@ -327,9 +321,9 @@ "File with problems should list them": function() { var result = { messages: [ - { type: 'warning', line: 1, col: 1, message: 'BOGUS', evidence: 'ALSO BOGUS', rule: [] }, - { type: 'error', line: 2, col: 1, message: 'BOGUS', evidence: 'ALSO BOGUS', rule: [] } - ], stats: [] }, + { type: "warning", line: 1, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: [] }, + { type: "error", line: 2, col: 1, message: "BOGUS", evidence: "ALSO BOGUS", rule: [] } + ], stats: [] }, error1 = "\n1: warning at line 1, col 1\nBOGUS\nALSO BOGUS", error2 = "\n2: error at line 2, col 1\nBOGUS\nALSO BOGUS", expected = "\n\ncsslint: There are 2 problems in path/to/FILE.\n\nFILE" + error1 + "\n\nFILE" + error2, @@ -342,8 +336,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -374,8 +366,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -394,17 +384,17 @@ Assert.areEqual(0, result.messages.length); }, - "Using width:auto with padding should not result in a warning": function(){ + "Using width:auto with padding should not result in a warning": function(){ var result = CSSLint.verify(".foo { width: auto; padding: 10px; }", { "box-model": 1 }); Assert.areEqual(0, result.messages.length); }, - "Using width:available with padding should not result in a warning": function(){ + "Using width:available with padding should not result in a warning": function(){ var result = CSSLint.verify(".foo { width: available; padding: 10px; }", { "box-model": 1 }); Assert.areEqual(0, result.messages.length); }, - "Using height:auto with padding should not result in a warning": function(){ + "Using height:auto with padding should not result in a warning": function(){ var result = CSSLint.verify(".foo { height: auto; padding: 10px; }", { "box-model": 1 }); Assert.areEqual(0, result.messages.length); }, @@ -601,8 +591,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -625,8 +613,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -731,8 +717,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -784,8 +768,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -998,8 +980,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -1024,8 +1004,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -1079,8 +1057,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -1098,8 +1074,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -1116,8 +1090,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2014,8 +1986,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2050,8 +2020,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2079,8 +2047,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2110,8 +2076,6 @@ })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2163,8 +2127,6 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2189,8 +2151,6 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2208,8 +2168,6 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2236,8 +2194,6 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2281,8 +2237,41 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })(); (function(){ + var Assert = YUITest.Assert; + + YUITest.TestRunner.add(new YUITest.TestCase({ + + name: "Alphabetical order Errors", + + "Rules with properties not in alphabetical order should result in a warning": function(){ + var result = CSSLint.verify("li { z-index: 2; color: red; }", { "order-alphabetical": 1 }); + Assert.areEqual(1, result.messages.length); + Assert.areEqual("warning", result.messages[0].type); + Assert.areEqual("Rule doesn't have all its properties in alphabetical ordered.", result.messages[0].message); + }, + + "Rules with prefixed properties not in alphabetical order (without the prefix) should result in a warning": function(){ + var result = CSSLint.verify("li { -moz-transition: none; -webkit-box-shadow: none; }", { "order-alphabetical": 1 }); + Assert.areEqual(1, result.messages.length); + Assert.areEqual("warning", result.messages[0].type); + Assert.areEqual("Rule doesn't have all its properties in alphabetical ordered.", result.messages[0].message); + }, + + "Rules with properties in alphabetical order should not result in a warning": function(){ + var result = CSSLint.verify("li { box-shadow: none; color: red; transition: none; }", { "order-alphabetical": 1 }); + Assert.areEqual(0, result.messages.length); + }, + + "Rules with prefixed properties in alphabetical order should not result in a warning": function(){ + var result = CSSLint.verify("li { -webkit-box-shadow: none; color: red; -moz-transition: none; }", { "order-alphabetical": 1 }); + Assert.areEqual(0, result.messages.length); + } + + })); - /*global YUITest, CSSLint*/ +})(); + +(function(){ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2332,8 +2321,6 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2374,8 +2361,6 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2394,8 +2379,6 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2447,8 +2430,6 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert, i, j, css1 = "", css2 = "", css3 = "", css4 = ""; // create css1, which has only 4095 rules and 4095 selectors @@ -2515,8 +2496,6 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert, i, j, css1 = "", css2 = "", css3 = "", css4 = ""; // create css1, which has only 4095 rules and 4095 selectors @@ -2573,9 +2552,41 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })); })(); -(function(){ +(function () { + + var ruleId = "selector-newline", expectWarning, expectPass; + + expectWarning = function (ruleset, expectedMessage) { + var result, enabledRules = {}; + enabledRules[ruleId] = 1; + result = CSSLint.verify(ruleset, enabledRules); + YUITest.Assert.areEqual(1, result.messages.length); + YUITest.Assert.areEqual("warning", result.messages[0].type); + YUITest.Assert.areEqual(expectedMessage, result.messages[0].message); + }; + + expectPass = function (ruleset) { + var result, enabledRules = {}; + enabledRules[ruleId] = 1; + result = CSSLint.verify(ruleset, enabledRules); + YUITest.Assert.areEqual(0, result.messages.length); + }; + + YUITest.TestRunner.add(new YUITest.TestCase({ + + name: ruleId + " Rule Errors", - /*global YUITest, CSSLint*/ + "a newline in a selector should result in a warning": function () { + expectWarning(".foo\n.bar{}", "newline character found in selector (forgot a comma?)"); + }, + "a newline between selectors should not result in a warning": function () { + expectPass(".foo,\n.bar{}"); + } + })); + +}()); + +(function(){ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2611,8 +2622,6 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2636,8 +2645,6 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2692,8 +2699,6 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2717,8 +2722,6 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2765,8 +2768,6 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2797,8 +2798,6 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2835,8 +2834,6 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ @@ -2895,11 +2892,9 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); Assert.areEqual("Missing standard property 'box-shadow' to go along with '-moz-box-shadow'.", result.messages[0].message); }, - "Using -moz-user-select should result in a warning.": function(){ + "Using -moz-user-select should not result in a warning.": function(){ var result = CSSLint.verify("h1 { -moz-user-select:none; }", { "vendor-prefix": 1 }); - Assert.areEqual(1, result.messages.length); - Assert.areEqual("warning", result.messages[0].type); - Assert.areEqual("Missing standard property 'user-select' to go along with '-moz-user-select'.", result.messages[0].message); + Assert.areEqual(0, result.messages.length); }, "Using @font-face should not result in an error (#90)": function(){ @@ -2912,8 +2907,6 @@ background: -o-linear-gradient(top, #1e5799 ,#2989d8 ,#207cca ,#7db9e8 ); })(); (function(){ - - /*global YUITest, CSSLint*/ var Assert = YUITest.Assert; YUITest.TestRunner.add(new YUITest.TestCase({ diff --git a/release/csslint-worker.js b/dist/csslint-worker.js similarity index 96% rename from release/csslint-worker.js rename to dist/csslint-worker.js index 3a61465d..2e737e48 100644 --- a/release/csslint-worker.js +++ b/dist/csslint-worker.js @@ -1,9 +1,9 @@ /*! CSSLint -Copyright (c) 2013 Nicole Sullivan and Nicholas C. Zakas. All rights reserved. +Copyright (c) 2014 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 +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 @@ -12,7 +12,7 @@ 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 +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 @@ -21,7 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* Build: v0.10.0 15-August-2013 01:07:22 */ +/* Build: v0.10.0 11-April-2014 12:11:52 */ var exports = exports || {}; /*! Parser-Lib @@ -46,7 +46,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* Version v0.2.3, Build time: 19-June-2013 11:16:15 */ +/* Version v0.2.4, Build time: 7-January-2014 07:32:49 */ var parserlib = {}; (function(){ @@ -956,7 +956,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* Version v0.2.3, Build time: 19-June-2013 11:16:15 */ +/* Version v0.2.4, Build time: 7-January-2014 07:32:49 */ (function(){ var EventTarget = parserlib.util.EventTarget, TokenStreamBase = parserlib.util.TokenStreamBase, @@ -991,6 +991,7 @@ var Colors = { darkcyan :"#008b8b", darkgoldenrod :"#b8860b", darkgray :"#a9a9a9", + darkgrey :"#a9a9a9", darkgreen :"#006400", darkkhaki :"#bdb76b", darkmagenta :"#8b008b", @@ -1002,11 +1003,13 @@ var Colors = { darkseagreen :"#8fbc8f", darkslateblue :"#483d8b", darkslategray :"#2f4f4f", + darkslategrey :"#2f4f4f", darkturquoise :"#00ced1", darkviolet :"#9400d3", deeppink :"#ff1493", deepskyblue :"#00bfff", dimgray :"#696969", + dimgrey :"#696969", dodgerblue :"#1e90ff", firebrick :"#b22222", floralwhite :"#fffaf0", @@ -1017,6 +1020,7 @@ var Colors = { gold :"#ffd700", goldenrod :"#daa520", gray :"#808080", + grey :"#808080", green :"#008000", greenyellow :"#adff2f", honeydew :"#f0fff0", @@ -1034,12 +1038,14 @@ var Colors = { lightcyan :"#e0ffff", lightgoldenrodyellow :"#fafad2", lightgray :"#d3d3d3", + lightgrey :"#d3d3d3", lightgreen :"#90ee90", lightpink :"#ffb6c1", lightsalmon :"#ffa07a", lightseagreen :"#20b2aa", lightskyblue :"#87cefa", lightslategray :"#778899", + lightslategrey :"#778899", lightsteelblue :"#b0c4de", lightyellow :"#ffffe0", lime :"#00ff00", @@ -1092,6 +1098,7 @@ var Colors = { skyblue :"#87ceeb", slateblue :"#6a5acd", slategray :"#708090", + slategrey :"#708090", snow :"#fffafa", springgreen :"#00ff7f", steelblue :"#4682b4", @@ -1117,6 +1124,7 @@ var Colors = { buttontext :"Text on push buttons.", captiontext :"Text in caption, size box, and scrollbar arrow box.", graytext :"Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.", + greytext :"Greyed (disabled) text. This color is set to #000 if the current display driver does not support a solid grey color.", highlight :"Item(s) selected in a control.", highlighttext :"Text of item(s) selected in a control.", inactiveborder :"Inactive window border.", @@ -2811,7 +2819,7 @@ Parser.prototype = function(){ var tokenStream = this._tokenStream, values = [], - //valueParts = [], + //valueParts = [], value = null, operator = null; @@ -2828,9 +2836,9 @@ Parser.prototype = function(){ values.push(operator); } /*else { //if there's not an operator, you have a full value - values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col)); - valueParts = []; - }*/ + values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col)); + valueParts = []; + }*/ value = this._term(); @@ -2842,7 +2850,7 @@ Parser.prototype = function(){ } while(true); } - //cleanup + //cleanup /*if (valueParts.length){ values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col)); }*/ @@ -3533,6 +3541,12 @@ nth var Properties = { //A + "align-items" : "flex-start | flex-end | center | baseline | stretch", + "align-content" : "flex-start | flex-end | center | space-between | space-around | stretch", + "align-self" : "auto | flex-start | flex-end | center | baseline | stretch", + "-webkit-align-items" : "flex-start | flex-end | center | baseline | stretch", + "-webkit-align-content" : "flex-start | flex-end | center | space-between | space-around | stretch", + "-webkit-align-self" : "auto | flex-start | flex-end | center | baseline | stretch", "alignment-adjust" : "auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | | ", "alignment-baseline" : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical", "animation" : 1, @@ -3728,15 +3742,24 @@ var Properties = { "border-top-width" : "", "border-width" : { multi: "", max: 4 }, "bottom" : " | inherit", - "box-align" : "start | end | center | baseline | stretch", //http://www.w3.org/TR/2009/WD-css3-flexbox-20090723/ - "box-decoration-break" : "slice |clone", - "box-direction" : "normal | reverse | inherit", - "box-flex" : "", - "box-flex-group" : "", - "box-lines" : "single | multiple", - "box-ordinal-group" : "", - "box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit", - "box-pack" : "start | end | center | justify", + "-moz-box-align" : "start | end | center | baseline | stretch", + "-moz-box-decoration-break" : "slice |clone", + "-moz-box-direction" : "normal | reverse | inherit", + "-moz-box-flex" : "", + "-moz-box-flex-group" : "", + "-moz-box-lines" : "single | multiple", + "-moz-box-ordinal-group" : "", + "-moz-box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit", + "-moz-box-pack" : "start | end | center | justify", + "-webkit-box-align" : "start | end | center | baseline | stretch", + "-webkit-box-decoration-break" : "slice |clone", + "-webkit-box-direction" : "normal | reverse | inherit", + "-webkit-box-flex" : "", + "-webkit-box-flex-group" : "", + "-webkit-box-lines" : "single | multiple", + "-webkit-box-ordinal-group" : "", + "-webkit-box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit", + "-webkit-box-pack" : "start | end | center | justify", "box-shadow" : function (expression) { var result = false, part; @@ -3782,7 +3805,7 @@ var Properties = { //D "direction" : "ltr | rtl | inherit", - "display" : "inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | box | inline-box | grid | inline-grid | none | inherit | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker | -webkit-box | -webkit-inline-box", + "display" : "inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | grid | inline-grid | none | inherit | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker | -webkit-box | -webkit-inline-box | -ms-flexbox | -ms-inline-flexbox | flex | -webkit-flex | inline-flex | -webkit-inline-flex", "dominant-baseline" : 1, "drop-initial-after-adjust" : "central | middle | after-edge | text-after-edge | ideographic | alphabetic | mathematical | | ", "drop-initial-after-align" : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical", @@ -3799,6 +3822,26 @@ var Properties = { "filter" : 1, "fit" : "fill | hidden | meet | slice", "fit-position" : 1, + "flex" : "none | [ ? || ", + "flex-basis" : "", + "flex-direction" : "row | row-reverse | column | column-reverse", + "flex-flow" : " || ", + "flex-grow" : "", + "flex-shrink" : "", + "flex-wrap" : "nowrap | wrap | wrap-reverse", + "-webkit-flex" : "none | [ ? || ", + "-webkit-flex-basis" : "", + "-webkit-flex-direction" : "row | row-reverse | column | column-reverse", + "-webkit-flex-flow" : " || ", + "-webkit-flex-grow" : "", + "-webkit-flex-shrink" : "", + "-webkit-flex-wrap" : "nowrap | wrap | wrap-reverse", + "-ms-flex" : "[[ ? ] || [ || || auto ] ] | none", + "-ms-flex-align" : "start | end | center | stretch | baseline", + "-ms-flex-direction" : "row | column | row-reverse | column-reverse | inherit", + "-ms-flex-order" : "", + "-ms-flex-pack" : "start | end | center | justify", + "-ms-flex-wrap" : "nowrap | wrap | wrap-reverse", "float" : "left | right | none | inherit", "float-offset" : 1, "font" : 1, @@ -3842,6 +3885,10 @@ var Properties = { "image-resolution" : 1, "inline-box-align" : "initial | last | ", + //J + "justify-content" : "flex-start | flex-end | center | space-between | space-around", + "-webkit-justify-content" : "flex-start | flex-end | center | space-between | space-around", + //L "left" : " | inherit", "letter-spacing" : " | normal | inherit", @@ -3885,6 +3932,8 @@ var Properties = { //O "opacity" : " | inherit", + "order" : "", + "-webkit-order" : "", "orphans" : " | inherit", "outline" : 1, "outline-color" : " | invert | inherit", @@ -3893,6 +3942,7 @@ var Properties = { "outline-width" : " | inherit", "overflow" : "visible | hidden | scroll | auto | inherit", "overflow-style" : 1, + "overflow-wrap" : "normal | break-word", "overflow-x" : 1, "overflow-y" : 1, @@ -3970,6 +4020,8 @@ var Properties = { "text-transform" : "capitalize | uppercase | lowercase | none | inherit", "text-wrap" : "normal | none | avoid", "top" : " | inherit", + "-ms-touch-action" : "auto | none | pan-x | pan-y", + "touch-action" : "auto | none | pan-x | pan-y", "transform" : 1, "transform-origin" : 1, "transform-style" : 1, @@ -3980,7 +4032,7 @@ var Properties = { "transition-timing-function" : 1, //U - "unicode-bidi" : "normal | embed | bidi-override | inherit", + "unicode-bidi" : "normal | embed | isolate | bidi-override | isolate-override | plaintext | inherit", "user-modify" : "read-only | read-write | write-only | inherit", "user-select" : "none | text | toggle | element | elements | all | inherit", @@ -4004,7 +4056,8 @@ var Properties = { "width" : " | | auto | inherit" , "word-break" : "normal | keep-all | break-all", "word-spacing" : " | normal | inherit", - "word-wrap" : 1, + "word-wrap" : "normal | break-word", + "writing-mode" : "horizontal-tb | vertical-rl | vertical-lr | lr-tb | rl-tb | tb-rl | bt-rl | tb-lr | bt-lr | lr-bt | rl-bt | lr | rl | tb | inherit", //Z "z-index" : " | auto | inherit", @@ -4275,9 +4328,6 @@ function PropertyValuePart(text, line, col){ } - } else if (/^([+\-]?[\d\.]+)%$/i.test(text)){ //percentage - this.type = "percentage"; - this.value = +RegExp.$1; } else if (/^([+\-]?[\d\.]+)%$/i.test(text)){ //percentage this.type = "percentage"; this.value = +RegExp.$1; @@ -4374,6 +4424,7 @@ PropertyValuePart.prototype.constructor = PropertyValuePart; PropertyValuePart.fromToken = function(token){ return new PropertyValuePart(token.value, token.startLine, token.startCol); }; + var Pseudos = { ":first-letter": 1, ":first-line": 1, @@ -4667,12 +4718,12 @@ function isIdentStart(c){ } function mix(receiver, supplier){ - for (var prop in supplier){ - if (supplier.hasOwnProperty(prop)){ - receiver[prop] = supplier[prop]; - } - } - return receiver; + for (var prop in supplier){ + if (supplier.hasOwnProperty(prop)){ + receiver[prop] = supplier[prop]; + } + } + return receiver; } //----------------------------------------------------------------------------- @@ -4688,7 +4739,7 @@ function mix(receiver, supplier){ * @namespace parserlib.css */ function TokenStream(input){ - TokenStreamBase.call(this, input, Tokens); + TokenStreamBase.call(this, input, Tokens); } TokenStream.prototype = mix(new TokenStreamBase(), { @@ -5661,7 +5712,7 @@ var Tokens = [ { name: "FONT_FACE_SYM", text: "@font-face"}, { name: "CHARSET_SYM", text: "@charset"}, { name: "NAMESPACE_SYM", text: "@namespace"}, - { name: "VIEWPORT_SYM", text: "@viewport"}, + { name: "VIEWPORT_SYM", text: ["@viewport", "@-ms-viewport"]}, { name: "UNKNOWN_SYM" }, //{ name: "ATKEYWORD"}, @@ -6435,7 +6486,10 @@ exports[prop] = parserlib[prop]; * @static * @extends parserlib.util.EventTarget */ -/*global parserlib, Reporter*/ + +/* global parserlib, Reporter */ +/* exported CSSLint */ + var CSSLint = (function(){ var rules = [], @@ -6601,8 +6655,7 @@ var CSSLint = (function(){ */ api.verify = function(text, ruleset){ - var i = 0, - len = rules.length, + var i = 0, reporter, lines, report, @@ -6610,7 +6663,7 @@ var CSSLint = (function(){ underscoreHack: true, strict: false }); // normalize line endings - lines = text.replace(/\n\r?/g, "$split$").split('$split$'); + lines = text.replace(/\n\r?/g, "$split$").split("$split$"); if (!ruleset){ ruleset = this.getRuleset(); @@ -6667,7 +6720,6 @@ var CSSLint = (function(){ })(); -/*global CSSLint*/ /** * An instance of Report is used to report results of the * verification back to the main API. @@ -6829,8 +6881,6 @@ Reporter.prototype = { //expose for testing purposes CSSLint._Reporter = Reporter; -/*global CSSLint*/ - /* * Utility functions that make life easier. */ @@ -6890,10 +6940,11 @@ CSSLint.Util = { } } }; -/*global CSSLint*/ + /* * Rule: Don't use adjoining classes (.foo.bar). */ + CSSLint.addRule({ //rule information @@ -6935,7 +6986,6 @@ CSSLint.addRule({ } }); -/*global CSSLint*/ /* * Rule: Don't use width or height when using padding or border. @@ -7036,11 +7086,11 @@ CSSLint.addRule({ } }); -/*global CSSLint*/ /* * Rule: box-sizing doesn't work in IE6 and IE7. */ + CSSLint.addRule({ //rule information @@ -7064,11 +7114,12 @@ CSSLint.addRule({ } }); + /* * 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) */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7080,14 +7131,13 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ var rule = this, - count = 0, 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(event){ + parser.addListener("startfontface", function(){ fontFaceRule = true; }); @@ -7105,7 +7155,7 @@ CSSLint.addRule({ col = event.col; // This is the property that we care about, we can ignore the rest - if (propertyName === 'src') { + if (propertyName === "src") { var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i; // We need to handle the advanced syntax with two src properties @@ -7121,7 +7171,7 @@ CSSLint.addRule({ }); // Back to normal rules that we don't need to test - parser.addListener("endfontface", function(event){ + parser.addListener("endfontface", function(){ fontFaceRule = false; if (ruleFailed) { @@ -7130,11 +7180,12 @@ CSSLint.addRule({ }); } }); + /* * Rule: Include all compatible vendor prefixes to reach a wider * range of users. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7222,9 +7273,9 @@ CSSLint.addRule({ for (prop in compatiblePrefixes) { if (compatiblePrefixes.hasOwnProperty(prop)) { variations = []; - prefixed = compatiblePrefixes[prop].split(' '); + prefixed = compatiblePrefixes[prop].split(" "); for (i = 0, len = prefixed.length; i < len; i++) { - variations.push('-' + prefixed[i] + '-' + prop); + variations.push("-" + prefixed[i] + "-" + prop); } compatiblePrefixes[prop] = variations; arrayPush.apply(applyTo, variations); @@ -7239,7 +7290,7 @@ CSSLint.addRule({ inKeyFrame = event.prefix || true; }); - parser.addListener("endkeyframes", function (event) { + parser.addListener("endkeyframes", function () { inKeyFrame = false; }); @@ -7255,7 +7306,7 @@ CSSLint.addRule({ } }); - parser.addListener("endrule", function (event) { + parser.addListener("endrule", function () { if (!properties.length) { return; } @@ -7316,6 +7367,7 @@ CSSLint.addRule({ }); } }); + /* * Rule: Certain properties don't play well with certain display values. * - float should not be used with inline-block @@ -7323,7 +7375,7 @@ CSSLint.addRule({ * - vertical-align should not be used with block * - margin, float should not be used with table-* */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7433,10 +7485,11 @@ CSSLint.addRule({ } }); + /* * Rule: Disallow duplicate background-images (using url). */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7457,8 +7510,8 @@ CSSLint.addRule({ 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') { + if (value.parts[i].type == "uri") { + if (typeof stack[value.parts[i].uri] === "undefined") { stack[value.parts[i].uri] = event; } else { @@ -7470,11 +7523,12 @@ CSSLint.addRule({ }); } }); + /* * 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. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7489,7 +7543,7 @@ CSSLint.addRule({ properties, lastProperty; - function startRule(event){ + function startRule(){ properties = {}; } @@ -7516,10 +7570,11 @@ CSSLint.addRule({ } }); + /* * Rule: Style rules without any properties defined should be removed. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7550,10 +7605,11 @@ CSSLint.addRule({ } }); + /* * Rule: There should be no syntax errors. (Duh.) */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7574,7 +7630,6 @@ CSSLint.addRule({ }); -/*global CSSLint*/ CSSLint.addRule({ //rule information @@ -7604,7 +7659,7 @@ CSSLint.addRule({ }, properties; - function startRule(event){ + function startRule(){ properties = {}; lastProperty = null; } @@ -7650,11 +7705,12 @@ CSSLint.addRule({ } }); + /* * Rule: You shouldn't use more than 10 floats. If you do, there's probably * room for some abstraction. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7686,10 +7742,11 @@ CSSLint.addRule({ } }); + /* * Rule: Avoid too many @font-face declarations in the same stylesheet. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7716,11 +7773,11 @@ CSSLint.addRule({ } }); + /* * Rule: You shouldn't need more than 9 font-size declarations. */ -/*global CSSLint*/ CSSLint.addRule({ //rule information @@ -7751,10 +7808,11 @@ CSSLint.addRule({ } }); + /* * Rule: When using a vendor-prefixed gradient, make sure to use them all. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7819,7 +7877,7 @@ CSSLint.addRule({ /* * Rule: Don't use IDs for selectors. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7866,10 +7924,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use @import, use instead. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7889,12 +7948,13 @@ CSSLint.addRule({ } }); + /* * 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. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7926,11 +7986,12 @@ CSSLint.addRule({ } }); + /* * Rule: Properties should be known (listed in CSS3 specification) or * be a vendor-prefixed property. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7944,7 +8005,6 @@ CSSLint.addRule({ var rule = this; parser.addListener("property", function(event){ - var name = event.property.text.toLowerCase(); // the check is handled entirely by the parser-lib (https://github.com/nzakas/parser-lib) if (event.invalid) { @@ -7955,11 +8015,58 @@ CSSLint.addRule({ } }); + +/* + * Rule: All properties should be in alphabetical order.. + */ +/*global CSSLint*/ +CSSLint.addRule({ + + //rule information + id: "order-alphabetical", + name: "Alphabetical order", + desc: "Assure properties are in alphabetical order", + browsers: "All", + + //initialization + init: function(parser, reporter){ + var rule = this, + properties; + + var startRule = function () { + properties = []; + }; + + parser.addListener("startrule", startRule); + parser.addListener("startfontface", startRule); + parser.addListener("startpage", startRule); + parser.addListener("startpagemargin", startRule); + parser.addListener("startkeyframerule", startRule); + + parser.addListener("property", function(event){ + var name = event.property.text, + lowerCasePrefixLessName = name.toLowerCase().replace(/^-.*?-/, ""); + + properties.push(lowerCasePrefixLessName); + }); + + parser.addListener("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 ordered.", event.line, event.col, rule); + } + }); + } + +}); + /* * 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. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7988,7 +8095,7 @@ CSSLint.addRule({ } } - function endRule(event){ + function endRule(){ if (lastRule){ if (lastRule.outline){ if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") == -1){ @@ -8028,10 +8135,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use classes or IDs with elements (a.foo or a#foo). */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8091,10 +8199,11 @@ CSSLint.addRule({ } }); + /* * Rule: Headings (h1-h6) should not be qualified (namespaced). */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8129,10 +8238,11 @@ CSSLint.addRule({ } }); + /* * Rule: Selectors that look like regular expressions are slow and should be avoided. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8173,10 +8283,11 @@ CSSLint.addRule({ } }); + /* * Rule: Total number of rules should not exceed x. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8187,8 +8298,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ - var rule = this, - count = 0; + var count = 0; //count each rule parser.addListener("startrule", function(){ @@ -8201,10 +8311,11 @@ CSSLint.addRule({ } }); + /* * Rule: Warn people with approaching the IE 4095 limit */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8217,7 +8328,7 @@ CSSLint.addRule({ init: function(parser, reporter) { var rule = this, count = 0; - parser.addListener('startrule', function(event) { + parser.addListener("startrule", function(event) { count += event.selectors.length; }); @@ -8233,7 +8344,7 @@ CSSLint.addRule({ /* * Rule: Warn people past the IE 4095 limit */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8246,7 +8357,7 @@ CSSLint.addRule({ init: function(parser, reporter){ var rule = this, count = 0; - parser.addListener('startrule',function(event) { + parser.addListener("startrule", function(event) { count += event.selectors.length; }); @@ -8258,11 +8369,47 @@ CSSLint.addRule({ } }); + +/* + * 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) { + var rule = this; + + parser.addListener("startrule", function(event) { + var i, len, selector, p, pLen, part, previousLine, currentLine, + 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++) { + part = selector.parts[p]; + currentLine = part.line; + if (currentLine === previousLine) { + reporter.report("newline character found in selector (forgot a comma?)", currentLine, selectors[i].parts[0].col, rule); + } + previousLine = currentLine; + } + } + }); + } +}); + /* * Rule: Use shorthand properties where possible. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8301,7 +8448,7 @@ CSSLint.addRule({ } } - function startRule(event){ + function startRule(){ properties = {}; } @@ -8331,8 +8478,7 @@ CSSLint.addRule({ //check for use of "font-size" parser.addListener("property", function(event){ - var name = event.property.toString().toLowerCase(), - value = event.value.parts[0].value; + var name = event.property.toString().toLowerCase(); if (propertiesToCheck[name]){ properties[name] = 1; @@ -8345,11 +8491,12 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use properties with a star prefix. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8372,11 +8519,12 @@ CSSLint.addRule({ }); } }); + /* * Rule: Don't use text-indent for image replacement if you need to support rtl. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8392,13 +8540,13 @@ CSSLint.addRule({ direction; - function startRule(event){ + function startRule(){ textIndent = false; direction = "inherit"; } //event handler for end of rules - function endRule(event){ + 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); } @@ -8425,11 +8573,12 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use properties with a underscore prefix. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8452,10 +8601,11 @@ CSSLint.addRule({ }); } }); + /* * Rule: Headings (h1-h6) should be defined only once. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8468,7 +8618,7 @@ CSSLint.addRule({ init: function(parser, reporter){ var rule = this; - var headings = { + var headings = { h1: 0, h2: 0, h3: 0, @@ -8507,7 +8657,7 @@ CSSLint.addRule({ } }); - parser.addListener("endstylesheet", function(event){ + parser.addListener("endstylesheet", function(){ var prop, messages = []; @@ -8526,10 +8676,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use universal selector because it's slow. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8546,8 +8697,7 @@ CSSLint.addRule({ var selectors = event.selectors, selector, part, - modifier, - i, j, k; + i; for (i=0; i < selectors.length; i++){ selector = selectors[i]; @@ -8561,10 +8711,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use unqualified attribute selectors because they're just like universal selectors. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8583,7 +8734,7 @@ CSSLint.addRule({ selector, part, modifier, - i, j, k; + i, k; for (i=0; i < selectors.length; i++){ selector = selectors[i]; @@ -8603,11 +8754,12 @@ CSSLint.addRule({ } }); + /* * Rule: When using a vendor-prefixed property, make sure to * include the standard one. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8678,11 +8830,7 @@ CSSLint.addRule({ "-ms-transform-origin" : "transform-origin", "-moz-box-sizing" : "box-sizing", - "-webkit-box-sizing" : "box-sizing", - - "-moz-user-select" : "user-select", - "-khtml-user-select" : "user-select", - "-webkit-user-select" : "user-select" + "-webkit-box-sizing" : "box-sizing" }; //event handler for beginning of rules @@ -8692,10 +8840,10 @@ CSSLint.addRule({ } //event handler for end of rules - function endRule(event){ + function endRule(){ var prop, - i, len, - standard, + i, + len, needed, actual, needsStandard = []; @@ -8746,10 +8894,11 @@ CSSLint.addRule({ } }); + /* * Rule: You don't need to specify units when a value is 0. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8780,7 +8929,7 @@ CSSLint.addRule({ } }); -/*global CSSLint*/ + (function() { /** @@ -8852,7 +9001,7 @@ CSSLint.addRule({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = []; @@ -8864,20 +9013,20 @@ CSSLint.addRule({ * @return rule source as {String} */ var generateSource = function(rule) { - if (!rule || !('name' in rule)) { + if (!rule || !("name" in rule)) { return ""; } - return 'net.csslint.' + rule.name.replace(/\s/g,''); + return "net.csslint." + rule.name.replace(/\s/g,""); }; if (messages.length > 0) { output.push(""); - CSSLint.Util.forEach(messages, function (message, i) { + CSSLint.Util.forEach(messages, function (message) { //ignore rollups for now if (!message.rollup) { - output.push(""); } }); @@ -8889,7 +9038,7 @@ CSSLint.addRule({ }); }()); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "compact", @@ -8936,19 +9085,19 @@ CSSLint.addFormatter({ return options.quiet ? "" : filename + ": Lint Free!"; } - CSSLint.Util.forEach(messages, function(message, i) { + CSSLint.Util.forEach(messages, function(message) { if (message.rollup) { output += filename + ": " + capitalize(message.type) + " - " + message.message + "\n"; } else { output += filename + ": " + "line " + message.line + - ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + "\n"; + ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n"; } }); return output; } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "csslint-xml", @@ -8977,7 +9126,7 @@ CSSLint.addFormatter({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = []; @@ -9002,7 +9151,7 @@ CSSLint.addFormatter({ if (messages.length > 0) { output.push(""); - CSSLint.Util.forEach(messages, function (message, i) { + CSSLint.Util.forEach(messages, function (message) { if (message.rollup) { output.push(""); } else { @@ -9016,7 +9165,7 @@ CSSLint.addFormatter({ return output.join(""); } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "junit-xml", @@ -9045,13 +9194,13 @@ CSSLint.addFormatter({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = [], tests = { - 'error': 0, - 'failure': 0 + "error": 0, + "failure": 0 }; /** @@ -9062,10 +9211,10 @@ CSSLint.addFormatter({ * @return rule source as {String} */ var generateSource = function(rule) { - if (!rule || !('name' in rule)) { + if (!rule || !("name" in rule)) { return ""; } - return 'net.csslint.' + rule.name.replace(/\s/g,''); + return "net.csslint." + rule.name.replace(/\s/g,""); }; /** @@ -9091,11 +9240,11 @@ CSSLint.addFormatter({ if (messages.length > 0) { - messages.forEach(function (message, i) { + messages.forEach(function (message) { // since junit has no warning class // all issues as errors - var type = message.type === 'warning' ? 'error' : message.type; + var type = message.type === "warning" ? "error" : message.type; //ignore rollups for now if (!message.rollup) { @@ -9103,7 +9252,7 @@ CSSLint.addFormatter({ // build the test case seperately, once joined // we'll add it to a custom array filtered by type output.push(""); - output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\">"); + output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\">"); output.push(""); tests[type] += 1; @@ -9121,7 +9270,7 @@ CSSLint.addFormatter({ } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "lint-xml", @@ -9150,7 +9299,7 @@ CSSLint.addFormatter({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = []; @@ -9176,7 +9325,7 @@ CSSLint.addFormatter({ if (messages.length > 0) { output.push(""); - CSSLint.Util.forEach(messages, function (message, i) { + CSSLint.Util.forEach(messages, function (message) { if (message.rollup) { output.push(""); } else { @@ -9190,7 +9339,7 @@ CSSLint.addFormatter({ return output.join(""); } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "text", @@ -9228,7 +9377,14 @@ CSSLint.addFormatter({ return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + "."; } - output = "\n\ncsslint: There are " + messages.length + " problems 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; @@ -9254,14 +9410,15 @@ CSSLint.addFormatter({ return output; } }); + /* * Web worker for CSSLint */ -/*global self, CSSLint, JSON*/ +/*global self, JSON*/ //message indicates to start linting self.onmessage = function(event){ - var data = event.data, + var data = event.data, message, text, ruleset, @@ -9279,4 +9436,4 @@ self.onmessage = function(event){ //Not all browsers support structured clone, so JSON stringify results self.postMessage(JSON.stringify(results)); -}; \ No newline at end of file +}; diff --git a/release/csslint-wsh.js b/dist/csslint-wsh.js similarity index 96% rename from release/csslint-wsh.js rename to dist/csslint-wsh.js index 10e140ec..5d0ba728 100644 --- a/release/csslint-wsh.js +++ b/dist/csslint-wsh.js @@ -1,9 +1,9 @@ /*! CSSLint -Copyright (c) 2013 Nicole Sullivan and Nicholas C. Zakas. All rights reserved. +Copyright (c) 2014 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 +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 @@ -12,7 +12,7 @@ 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 +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 @@ -21,7 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* Build: v0.10.0 15-August-2013 01:07:22 */ +/* Build: v0.10.0 11-April-2014 12:11:52 */ var exports = exports || {}; var CSSLint = (function(){ /*! @@ -47,7 +47,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* Version v0.2.3, Build time: 19-June-2013 11:16:15 */ +/* Version v0.2.4, Build time: 7-January-2014 07:32:49 */ var parserlib = {}; (function(){ @@ -957,7 +957,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* Version v0.2.3, Build time: 19-June-2013 11:16:15 */ +/* Version v0.2.4, Build time: 7-January-2014 07:32:49 */ (function(){ var EventTarget = parserlib.util.EventTarget, TokenStreamBase = parserlib.util.TokenStreamBase, @@ -992,6 +992,7 @@ var Colors = { darkcyan :"#008b8b", darkgoldenrod :"#b8860b", darkgray :"#a9a9a9", + darkgrey :"#a9a9a9", darkgreen :"#006400", darkkhaki :"#bdb76b", darkmagenta :"#8b008b", @@ -1003,11 +1004,13 @@ var Colors = { darkseagreen :"#8fbc8f", darkslateblue :"#483d8b", darkslategray :"#2f4f4f", + darkslategrey :"#2f4f4f", darkturquoise :"#00ced1", darkviolet :"#9400d3", deeppink :"#ff1493", deepskyblue :"#00bfff", dimgray :"#696969", + dimgrey :"#696969", dodgerblue :"#1e90ff", firebrick :"#b22222", floralwhite :"#fffaf0", @@ -1018,6 +1021,7 @@ var Colors = { gold :"#ffd700", goldenrod :"#daa520", gray :"#808080", + grey :"#808080", green :"#008000", greenyellow :"#adff2f", honeydew :"#f0fff0", @@ -1035,12 +1039,14 @@ var Colors = { lightcyan :"#e0ffff", lightgoldenrodyellow :"#fafad2", lightgray :"#d3d3d3", + lightgrey :"#d3d3d3", lightgreen :"#90ee90", lightpink :"#ffb6c1", lightsalmon :"#ffa07a", lightseagreen :"#20b2aa", lightskyblue :"#87cefa", lightslategray :"#778899", + lightslategrey :"#778899", lightsteelblue :"#b0c4de", lightyellow :"#ffffe0", lime :"#00ff00", @@ -1093,6 +1099,7 @@ var Colors = { skyblue :"#87ceeb", slateblue :"#6a5acd", slategray :"#708090", + slategrey :"#708090", snow :"#fffafa", springgreen :"#00ff7f", steelblue :"#4682b4", @@ -1118,6 +1125,7 @@ var Colors = { buttontext :"Text on push buttons.", captiontext :"Text in caption, size box, and scrollbar arrow box.", graytext :"Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.", + greytext :"Greyed (disabled) text. This color is set to #000 if the current display driver does not support a solid grey color.", highlight :"Item(s) selected in a control.", highlighttext :"Text of item(s) selected in a control.", inactiveborder :"Inactive window border.", @@ -2812,7 +2820,7 @@ Parser.prototype = function(){ var tokenStream = this._tokenStream, values = [], - //valueParts = [], + //valueParts = [], value = null, operator = null; @@ -2829,9 +2837,9 @@ Parser.prototype = function(){ values.push(operator); } /*else { //if there's not an operator, you have a full value - values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col)); - valueParts = []; - }*/ + values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col)); + valueParts = []; + }*/ value = this._term(); @@ -2843,7 +2851,7 @@ Parser.prototype = function(){ } while(true); } - //cleanup + //cleanup /*if (valueParts.length){ values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col)); }*/ @@ -3534,6 +3542,12 @@ nth var Properties = { //A + "align-items" : "flex-start | flex-end | center | baseline | stretch", + "align-content" : "flex-start | flex-end | center | space-between | space-around | stretch", + "align-self" : "auto | flex-start | flex-end | center | baseline | stretch", + "-webkit-align-items" : "flex-start | flex-end | center | baseline | stretch", + "-webkit-align-content" : "flex-start | flex-end | center | space-between | space-around | stretch", + "-webkit-align-self" : "auto | flex-start | flex-end | center | baseline | stretch", "alignment-adjust" : "auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | | ", "alignment-baseline" : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical", "animation" : 1, @@ -3729,15 +3743,24 @@ var Properties = { "border-top-width" : "", "border-width" : { multi: "", max: 4 }, "bottom" : " | inherit", - "box-align" : "start | end | center | baseline | stretch", //http://www.w3.org/TR/2009/WD-css3-flexbox-20090723/ - "box-decoration-break" : "slice |clone", - "box-direction" : "normal | reverse | inherit", - "box-flex" : "", - "box-flex-group" : "", - "box-lines" : "single | multiple", - "box-ordinal-group" : "", - "box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit", - "box-pack" : "start | end | center | justify", + "-moz-box-align" : "start | end | center | baseline | stretch", + "-moz-box-decoration-break" : "slice |clone", + "-moz-box-direction" : "normal | reverse | inherit", + "-moz-box-flex" : "", + "-moz-box-flex-group" : "", + "-moz-box-lines" : "single | multiple", + "-moz-box-ordinal-group" : "", + "-moz-box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit", + "-moz-box-pack" : "start | end | center | justify", + "-webkit-box-align" : "start | end | center | baseline | stretch", + "-webkit-box-decoration-break" : "slice |clone", + "-webkit-box-direction" : "normal | reverse | inherit", + "-webkit-box-flex" : "", + "-webkit-box-flex-group" : "", + "-webkit-box-lines" : "single | multiple", + "-webkit-box-ordinal-group" : "", + "-webkit-box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit", + "-webkit-box-pack" : "start | end | center | justify", "box-shadow" : function (expression) { var result = false, part; @@ -3783,7 +3806,7 @@ var Properties = { //D "direction" : "ltr | rtl | inherit", - "display" : "inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | box | inline-box | grid | inline-grid | none | inherit | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker | -webkit-box | -webkit-inline-box", + "display" : "inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | grid | inline-grid | none | inherit | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker | -webkit-box | -webkit-inline-box | -ms-flexbox | -ms-inline-flexbox | flex | -webkit-flex | inline-flex | -webkit-inline-flex", "dominant-baseline" : 1, "drop-initial-after-adjust" : "central | middle | after-edge | text-after-edge | ideographic | alphabetic | mathematical | | ", "drop-initial-after-align" : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical", @@ -3800,6 +3823,26 @@ var Properties = { "filter" : 1, "fit" : "fill | hidden | meet | slice", "fit-position" : 1, + "flex" : "none | [ ? || ", + "flex-basis" : "", + "flex-direction" : "row | row-reverse | column | column-reverse", + "flex-flow" : " || ", + "flex-grow" : "", + "flex-shrink" : "", + "flex-wrap" : "nowrap | wrap | wrap-reverse", + "-webkit-flex" : "none | [ ? || ", + "-webkit-flex-basis" : "", + "-webkit-flex-direction" : "row | row-reverse | column | column-reverse", + "-webkit-flex-flow" : " || ", + "-webkit-flex-grow" : "", + "-webkit-flex-shrink" : "", + "-webkit-flex-wrap" : "nowrap | wrap | wrap-reverse", + "-ms-flex" : "[[ ? ] || [ || || auto ] ] | none", + "-ms-flex-align" : "start | end | center | stretch | baseline", + "-ms-flex-direction" : "row | column | row-reverse | column-reverse | inherit", + "-ms-flex-order" : "", + "-ms-flex-pack" : "start | end | center | justify", + "-ms-flex-wrap" : "nowrap | wrap | wrap-reverse", "float" : "left | right | none | inherit", "float-offset" : 1, "font" : 1, @@ -3843,6 +3886,10 @@ var Properties = { "image-resolution" : 1, "inline-box-align" : "initial | last | ", + //J + "justify-content" : "flex-start | flex-end | center | space-between | space-around", + "-webkit-justify-content" : "flex-start | flex-end | center | space-between | space-around", + //L "left" : " | inherit", "letter-spacing" : " | normal | inherit", @@ -3886,6 +3933,8 @@ var Properties = { //O "opacity" : " | inherit", + "order" : "", + "-webkit-order" : "", "orphans" : " | inherit", "outline" : 1, "outline-color" : " | invert | inherit", @@ -3894,6 +3943,7 @@ var Properties = { "outline-width" : " | inherit", "overflow" : "visible | hidden | scroll | auto | inherit", "overflow-style" : 1, + "overflow-wrap" : "normal | break-word", "overflow-x" : 1, "overflow-y" : 1, @@ -3971,6 +4021,8 @@ var Properties = { "text-transform" : "capitalize | uppercase | lowercase | none | inherit", "text-wrap" : "normal | none | avoid", "top" : " | inherit", + "-ms-touch-action" : "auto | none | pan-x | pan-y", + "touch-action" : "auto | none | pan-x | pan-y", "transform" : 1, "transform-origin" : 1, "transform-style" : 1, @@ -3981,7 +4033,7 @@ var Properties = { "transition-timing-function" : 1, //U - "unicode-bidi" : "normal | embed | bidi-override | inherit", + "unicode-bidi" : "normal | embed | isolate | bidi-override | isolate-override | plaintext | inherit", "user-modify" : "read-only | read-write | write-only | inherit", "user-select" : "none | text | toggle | element | elements | all | inherit", @@ -4005,7 +4057,8 @@ var Properties = { "width" : " | | auto | inherit" , "word-break" : "normal | keep-all | break-all", "word-spacing" : " | normal | inherit", - "word-wrap" : 1, + "word-wrap" : "normal | break-word", + "writing-mode" : "horizontal-tb | vertical-rl | vertical-lr | lr-tb | rl-tb | tb-rl | bt-rl | tb-lr | bt-lr | lr-bt | rl-bt | lr | rl | tb | inherit", //Z "z-index" : " | auto | inherit", @@ -4276,9 +4329,6 @@ function PropertyValuePart(text, line, col){ } - } else if (/^([+\-]?[\d\.]+)%$/i.test(text)){ //percentage - this.type = "percentage"; - this.value = +RegExp.$1; } else if (/^([+\-]?[\d\.]+)%$/i.test(text)){ //percentage this.type = "percentage"; this.value = +RegExp.$1; @@ -4375,6 +4425,7 @@ PropertyValuePart.prototype.constructor = PropertyValuePart; PropertyValuePart.fromToken = function(token){ return new PropertyValuePart(token.value, token.startLine, token.startCol); }; + var Pseudos = { ":first-letter": 1, ":first-line": 1, @@ -4668,12 +4719,12 @@ function isIdentStart(c){ } function mix(receiver, supplier){ - for (var prop in supplier){ - if (supplier.hasOwnProperty(prop)){ - receiver[prop] = supplier[prop]; - } - } - return receiver; + for (var prop in supplier){ + if (supplier.hasOwnProperty(prop)){ + receiver[prop] = supplier[prop]; + } + } + return receiver; } //----------------------------------------------------------------------------- @@ -4689,7 +4740,7 @@ function mix(receiver, supplier){ * @namespace parserlib.css */ function TokenStream(input){ - TokenStreamBase.call(this, input, Tokens); + TokenStreamBase.call(this, input, Tokens); } TokenStream.prototype = mix(new TokenStreamBase(), { @@ -5662,7 +5713,7 @@ var Tokens = [ { name: "FONT_FACE_SYM", text: "@font-face"}, { name: "CHARSET_SYM", text: "@charset"}, { name: "NAMESPACE_SYM", text: "@namespace"}, - { name: "VIEWPORT_SYM", text: "@viewport"}, + { name: "VIEWPORT_SYM", text: ["@viewport", "@-ms-viewport"]}, { name: "UNKNOWN_SYM" }, //{ name: "ATKEYWORD"}, @@ -6436,7 +6487,10 @@ exports[prop] = parserlib[prop]; * @static * @extends parserlib.util.EventTarget */ -/*global parserlib, Reporter*/ + +/* global parserlib, Reporter */ +/* exported CSSLint */ + var CSSLint = (function(){ var rules = [], @@ -6602,8 +6656,7 @@ var CSSLint = (function(){ */ api.verify = function(text, ruleset){ - var i = 0, - len = rules.length, + var i = 0, reporter, lines, report, @@ -6611,7 +6664,7 @@ var CSSLint = (function(){ underscoreHack: true, strict: false }); // normalize line endings - lines = text.replace(/\n\r?/g, "$split$").split('$split$'); + lines = text.replace(/\n\r?/g, "$split$").split("$split$"); if (!ruleset){ ruleset = this.getRuleset(); @@ -6668,7 +6721,6 @@ var CSSLint = (function(){ })(); -/*global CSSLint*/ /** * An instance of Report is used to report results of the * verification back to the main API. @@ -6830,8 +6882,6 @@ Reporter.prototype = { //expose for testing purposes CSSLint._Reporter = Reporter; -/*global CSSLint*/ - /* * Utility functions that make life easier. */ @@ -6891,10 +6941,11 @@ CSSLint.Util = { } } }; -/*global CSSLint*/ + /* * Rule: Don't use adjoining classes (.foo.bar). */ + CSSLint.addRule({ //rule information @@ -6936,7 +6987,6 @@ CSSLint.addRule({ } }); -/*global CSSLint*/ /* * Rule: Don't use width or height when using padding or border. @@ -7037,11 +7087,11 @@ CSSLint.addRule({ } }); -/*global CSSLint*/ /* * Rule: box-sizing doesn't work in IE6 and IE7. */ + CSSLint.addRule({ //rule information @@ -7065,11 +7115,12 @@ CSSLint.addRule({ } }); + /* * 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) */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7081,14 +7132,13 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ var rule = this, - count = 0, 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(event){ + parser.addListener("startfontface", function(){ fontFaceRule = true; }); @@ -7106,7 +7156,7 @@ CSSLint.addRule({ col = event.col; // This is the property that we care about, we can ignore the rest - if (propertyName === 'src') { + if (propertyName === "src") { var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i; // We need to handle the advanced syntax with two src properties @@ -7122,7 +7172,7 @@ CSSLint.addRule({ }); // Back to normal rules that we don't need to test - parser.addListener("endfontface", function(event){ + parser.addListener("endfontface", function(){ fontFaceRule = false; if (ruleFailed) { @@ -7131,11 +7181,12 @@ CSSLint.addRule({ }); } }); + /* * Rule: Include all compatible vendor prefixes to reach a wider * range of users. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7223,9 +7274,9 @@ CSSLint.addRule({ for (prop in compatiblePrefixes) { if (compatiblePrefixes.hasOwnProperty(prop)) { variations = []; - prefixed = compatiblePrefixes[prop].split(' '); + prefixed = compatiblePrefixes[prop].split(" "); for (i = 0, len = prefixed.length; i < len; i++) { - variations.push('-' + prefixed[i] + '-' + prop); + variations.push("-" + prefixed[i] + "-" + prop); } compatiblePrefixes[prop] = variations; arrayPush.apply(applyTo, variations); @@ -7240,7 +7291,7 @@ CSSLint.addRule({ inKeyFrame = event.prefix || true; }); - parser.addListener("endkeyframes", function (event) { + parser.addListener("endkeyframes", function () { inKeyFrame = false; }); @@ -7256,7 +7307,7 @@ CSSLint.addRule({ } }); - parser.addListener("endrule", function (event) { + parser.addListener("endrule", function () { if (!properties.length) { return; } @@ -7317,6 +7368,7 @@ CSSLint.addRule({ }); } }); + /* * Rule: Certain properties don't play well with certain display values. * - float should not be used with inline-block @@ -7324,7 +7376,7 @@ CSSLint.addRule({ * - vertical-align should not be used with block * - margin, float should not be used with table-* */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7434,10 +7486,11 @@ CSSLint.addRule({ } }); + /* * Rule: Disallow duplicate background-images (using url). */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7458,8 +7511,8 @@ CSSLint.addRule({ 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') { + if (value.parts[i].type == "uri") { + if (typeof stack[value.parts[i].uri] === "undefined") { stack[value.parts[i].uri] = event; } else { @@ -7471,11 +7524,12 @@ CSSLint.addRule({ }); } }); + /* * 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. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7490,7 +7544,7 @@ CSSLint.addRule({ properties, lastProperty; - function startRule(event){ + function startRule(){ properties = {}; } @@ -7517,10 +7571,11 @@ CSSLint.addRule({ } }); + /* * Rule: Style rules without any properties defined should be removed. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7551,10 +7606,11 @@ CSSLint.addRule({ } }); + /* * Rule: There should be no syntax errors. (Duh.) */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7575,7 +7631,6 @@ CSSLint.addRule({ }); -/*global CSSLint*/ CSSLint.addRule({ //rule information @@ -7605,7 +7660,7 @@ CSSLint.addRule({ }, properties; - function startRule(event){ + function startRule(){ properties = {}; lastProperty = null; } @@ -7651,11 +7706,12 @@ CSSLint.addRule({ } }); + /* * Rule: You shouldn't use more than 10 floats. If you do, there's probably * room for some abstraction. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7687,10 +7743,11 @@ CSSLint.addRule({ } }); + /* * Rule: Avoid too many @font-face declarations in the same stylesheet. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7717,11 +7774,11 @@ CSSLint.addRule({ } }); + /* * Rule: You shouldn't need more than 9 font-size declarations. */ -/*global CSSLint*/ CSSLint.addRule({ //rule information @@ -7752,10 +7809,11 @@ CSSLint.addRule({ } }); + /* * Rule: When using a vendor-prefixed gradient, make sure to use them all. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7820,7 +7878,7 @@ CSSLint.addRule({ /* * Rule: Don't use IDs for selectors. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7867,10 +7925,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use @import, use instead. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7890,12 +7949,13 @@ CSSLint.addRule({ } }); + /* * 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. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7927,11 +7987,12 @@ CSSLint.addRule({ } }); + /* * Rule: Properties should be known (listed in CSS3 specification) or * be a vendor-prefixed property. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7945,7 +8006,6 @@ CSSLint.addRule({ var rule = this; parser.addListener("property", function(event){ - var name = event.property.text.toLowerCase(); // the check is handled entirely by the parser-lib (https://github.com/nzakas/parser-lib) if (event.invalid) { @@ -7956,11 +8016,58 @@ CSSLint.addRule({ } }); + +/* + * Rule: All properties should be in alphabetical order.. + */ +/*global CSSLint*/ +CSSLint.addRule({ + + //rule information + id: "order-alphabetical", + name: "Alphabetical order", + desc: "Assure properties are in alphabetical order", + browsers: "All", + + //initialization + init: function(parser, reporter){ + var rule = this, + properties; + + var startRule = function () { + properties = []; + }; + + parser.addListener("startrule", startRule); + parser.addListener("startfontface", startRule); + parser.addListener("startpage", startRule); + parser.addListener("startpagemargin", startRule); + parser.addListener("startkeyframerule", startRule); + + parser.addListener("property", function(event){ + var name = event.property.text, + lowerCasePrefixLessName = name.toLowerCase().replace(/^-.*?-/, ""); + + properties.push(lowerCasePrefixLessName); + }); + + parser.addListener("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 ordered.", event.line, event.col, rule); + } + }); + } + +}); + /* * 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. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7989,7 +8096,7 @@ CSSLint.addRule({ } } - function endRule(event){ + function endRule(){ if (lastRule){ if (lastRule.outline){ if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") == -1){ @@ -8029,10 +8136,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use classes or IDs with elements (a.foo or a#foo). */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8092,10 +8200,11 @@ CSSLint.addRule({ } }); + /* * Rule: Headings (h1-h6) should not be qualified (namespaced). */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8130,10 +8239,11 @@ CSSLint.addRule({ } }); + /* * Rule: Selectors that look like regular expressions are slow and should be avoided. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8174,10 +8284,11 @@ CSSLint.addRule({ } }); + /* * Rule: Total number of rules should not exceed x. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8188,8 +8299,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ - var rule = this, - count = 0; + var count = 0; //count each rule parser.addListener("startrule", function(){ @@ -8202,10 +8312,11 @@ CSSLint.addRule({ } }); + /* * Rule: Warn people with approaching the IE 4095 limit */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8218,7 +8329,7 @@ CSSLint.addRule({ init: function(parser, reporter) { var rule = this, count = 0; - parser.addListener('startrule', function(event) { + parser.addListener("startrule", function(event) { count += event.selectors.length; }); @@ -8234,7 +8345,7 @@ CSSLint.addRule({ /* * Rule: Warn people past the IE 4095 limit */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8247,7 +8358,7 @@ CSSLint.addRule({ init: function(parser, reporter){ var rule = this, count = 0; - parser.addListener('startrule',function(event) { + parser.addListener("startrule", function(event) { count += event.selectors.length; }); @@ -8259,11 +8370,47 @@ CSSLint.addRule({ } }); + +/* + * 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) { + var rule = this; + + parser.addListener("startrule", function(event) { + var i, len, selector, p, pLen, part, previousLine, currentLine, + 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++) { + part = selector.parts[p]; + currentLine = part.line; + if (currentLine === previousLine) { + reporter.report("newline character found in selector (forgot a comma?)", currentLine, selectors[i].parts[0].col, rule); + } + previousLine = currentLine; + } + } + }); + } +}); + /* * Rule: Use shorthand properties where possible. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8302,7 +8449,7 @@ CSSLint.addRule({ } } - function startRule(event){ + function startRule(){ properties = {}; } @@ -8332,8 +8479,7 @@ CSSLint.addRule({ //check for use of "font-size" parser.addListener("property", function(event){ - var name = event.property.toString().toLowerCase(), - value = event.value.parts[0].value; + var name = event.property.toString().toLowerCase(); if (propertiesToCheck[name]){ properties[name] = 1; @@ -8346,11 +8492,12 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use properties with a star prefix. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8373,11 +8520,12 @@ CSSLint.addRule({ }); } }); + /* * Rule: Don't use text-indent for image replacement if you need to support rtl. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8393,13 +8541,13 @@ CSSLint.addRule({ direction; - function startRule(event){ + function startRule(){ textIndent = false; direction = "inherit"; } //event handler for end of rules - function endRule(event){ + 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); } @@ -8426,11 +8574,12 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use properties with a underscore prefix. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8453,10 +8602,11 @@ CSSLint.addRule({ }); } }); + /* * Rule: Headings (h1-h6) should be defined only once. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8469,7 +8619,7 @@ CSSLint.addRule({ init: function(parser, reporter){ var rule = this; - var headings = { + var headings = { h1: 0, h2: 0, h3: 0, @@ -8508,7 +8658,7 @@ CSSLint.addRule({ } }); - parser.addListener("endstylesheet", function(event){ + parser.addListener("endstylesheet", function(){ var prop, messages = []; @@ -8527,10 +8677,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use universal selector because it's slow. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8547,8 +8698,7 @@ CSSLint.addRule({ var selectors = event.selectors, selector, part, - modifier, - i, j, k; + i; for (i=0; i < selectors.length; i++){ selector = selectors[i]; @@ -8562,10 +8712,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use unqualified attribute selectors because they're just like universal selectors. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8584,7 +8735,7 @@ CSSLint.addRule({ selector, part, modifier, - i, j, k; + i, k; for (i=0; i < selectors.length; i++){ selector = selectors[i]; @@ -8604,11 +8755,12 @@ CSSLint.addRule({ } }); + /* * Rule: When using a vendor-prefixed property, make sure to * include the standard one. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8679,11 +8831,7 @@ CSSLint.addRule({ "-ms-transform-origin" : "transform-origin", "-moz-box-sizing" : "box-sizing", - "-webkit-box-sizing" : "box-sizing", - - "-moz-user-select" : "user-select", - "-khtml-user-select" : "user-select", - "-webkit-user-select" : "user-select" + "-webkit-box-sizing" : "box-sizing" }; //event handler for beginning of rules @@ -8693,10 +8841,10 @@ CSSLint.addRule({ } //event handler for end of rules - function endRule(event){ + function endRule(){ var prop, - i, len, - standard, + i, + len, needed, actual, needsStandard = []; @@ -8747,10 +8895,11 @@ CSSLint.addRule({ } }); + /* * Rule: You don't need to specify units when a value is 0. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8781,7 +8930,7 @@ CSSLint.addRule({ } }); -/*global CSSLint*/ + (function() { /** @@ -8853,7 +9002,7 @@ CSSLint.addRule({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = []; @@ -8865,20 +9014,20 @@ CSSLint.addRule({ * @return rule source as {String} */ var generateSource = function(rule) { - if (!rule || !('name' in rule)) { + if (!rule || !("name" in rule)) { return ""; } - return 'net.csslint.' + rule.name.replace(/\s/g,''); + return "net.csslint." + rule.name.replace(/\s/g,""); }; if (messages.length > 0) { output.push(""); - CSSLint.Util.forEach(messages, function (message, i) { + CSSLint.Util.forEach(messages, function (message) { //ignore rollups for now if (!message.rollup) { - output.push(""); } }); @@ -8890,7 +9039,7 @@ CSSLint.addRule({ }); }()); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "compact", @@ -8937,19 +9086,19 @@ CSSLint.addFormatter({ return options.quiet ? "" : filename + ": Lint Free!"; } - CSSLint.Util.forEach(messages, function(message, i) { + CSSLint.Util.forEach(messages, function(message) { if (message.rollup) { output += filename + ": " + capitalize(message.type) + " - " + message.message + "\n"; } else { output += filename + ": " + "line " + message.line + - ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + "\n"; + ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n"; } }); return output; } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "csslint-xml", @@ -8978,7 +9127,7 @@ CSSLint.addFormatter({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = []; @@ -9003,7 +9152,7 @@ CSSLint.addFormatter({ if (messages.length > 0) { output.push(""); - CSSLint.Util.forEach(messages, function (message, i) { + CSSLint.Util.forEach(messages, function (message) { if (message.rollup) { output.push(""); } else { @@ -9017,7 +9166,7 @@ CSSLint.addFormatter({ return output.join(""); } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "junit-xml", @@ -9046,13 +9195,13 @@ CSSLint.addFormatter({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = [], tests = { - 'error': 0, - 'failure': 0 + "error": 0, + "failure": 0 }; /** @@ -9063,10 +9212,10 @@ CSSLint.addFormatter({ * @return rule source as {String} */ var generateSource = function(rule) { - if (!rule || !('name' in rule)) { + if (!rule || !("name" in rule)) { return ""; } - return 'net.csslint.' + rule.name.replace(/\s/g,''); + return "net.csslint." + rule.name.replace(/\s/g,""); }; /** @@ -9092,11 +9241,11 @@ CSSLint.addFormatter({ if (messages.length > 0) { - messages.forEach(function (message, i) { + messages.forEach(function (message) { // since junit has no warning class // all issues as errors - var type = message.type === 'warning' ? 'error' : message.type; + var type = message.type === "warning" ? "error" : message.type; //ignore rollups for now if (!message.rollup) { @@ -9104,7 +9253,7 @@ CSSLint.addFormatter({ // build the test case seperately, once joined // we'll add it to a custom array filtered by type output.push(""); - output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\">"); + output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\">"); output.push(""); tests[type] += 1; @@ -9122,7 +9271,7 @@ CSSLint.addFormatter({ } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "lint-xml", @@ -9151,7 +9300,7 @@ CSSLint.addFormatter({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = []; @@ -9177,7 +9326,7 @@ CSSLint.addFormatter({ if (messages.length > 0) { output.push(""); - CSSLint.Util.forEach(messages, function (message, i) { + CSSLint.Util.forEach(messages, function (message) { if (message.rollup) { output.push(""); } else { @@ -9191,7 +9340,7 @@ CSSLint.addFormatter({ return output.join(""); } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "text", @@ -9229,7 +9378,14 @@ CSSLint.addFormatter({ return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + "."; } - output = "\n\ncsslint: There are " + messages.length + " problems 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; @@ -9255,13 +9411,16 @@ CSSLint.addFormatter({ return output; } }); + return CSSLint; })(); /* * Encapsulates all of the CLI functionality. The api argument simply * provides environment-specific functionality. */ -/*global CSSLint*/ + +/* exported cli */ + function cli(api){ var globalOptions = { @@ -9430,8 +9589,8 @@ function cli(api){ */ function outputHelp(){ var lenToPad = 40, - toPrint = '', - formatString = ''; + toPrint = "", + formatString = ""; api.print([ "\nUsage: csslint-rhino.js [options]* [file|dir]*", @@ -9444,14 +9603,14 @@ function cli(api){ // Print the option name and the format if present toPrint += " --" + optionName; if (globalOptions[optionName].format !== "") { - formatString = '=' + globalOptions[optionName].format; + formatString = "=" + globalOptions[optionName].format; toPrint += formatString; } else { - formatString = ''; + formatString = ""; } // Pad out with the appropriate number of spaces - toPrint += new Array(lenToPad - (optionName.length + formatString.length)).join(' '); + toPrint += new Array(lenToPad - (optionName.length + formatString.length)).join(" "); // Print the description toPrint += globalOptions[optionName].description + "\n"; @@ -9541,9 +9700,9 @@ function cli(api){ } function validateOptions(options) { - for (var option_key in options) { - if (!globalOptions.hasOwnProperty(option_key) && option_key !== 'files') { - api.print(option_key + ' is not a valid option. Exiting...'); + for (var optionKey in options) { + if (!globalOptions.hasOwnProperty(optionKey) && optionKey !== "files") { + api.print(optionKey + " is not a valid option. Exiting..."); outputHelp(); api.quit(0); } @@ -9551,8 +9710,20 @@ function cli(api){ } function readConfigFile(options) { - var data = api.readFile(api.getFullPath(".csslintrc")); + var data = api.readFile(api.getFullPath(".csslintrc")), + json; if (data) { + if (data.charAt(0) === "{") { + try { + json = JSON.parse(data); + data = ""; + for (var optionName in json) { + if (json.hasOwnProperty(optionName)) { + data += "--" + optionName + "=" + json[optionName].join(); + } + } + } catch(e) {} + } options = processArguments(data.split(/[\s\n\r]+/m), options); } @@ -9599,9 +9770,12 @@ function cli(api){ /* * Windows Script Host Command Line Interface */ -/*global ActiveXObject, WScript, Enumerator, cli*/ + //TODO: This file needs major cleanup!!! +/* jshint wsh:true */ +/* global cli */ + var wshapi = (function(){ var fso = new ActiveXObject("Scripting.FileSystemObject"); var shell = WScript.CreateObject("WScript.Shell"); diff --git a/release/csslint.js b/dist/csslint.js similarity index 96% rename from release/csslint.js rename to dist/csslint.js index 6a60eb99..4184f08f 100644 --- a/release/csslint.js +++ b/dist/csslint.js @@ -1,9 +1,9 @@ /*! CSSLint -Copyright (c) 2013 Nicole Sullivan and Nicholas C. Zakas. All rights reserved. +Copyright (c) 2014 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 +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 @@ -12,7 +12,7 @@ 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 +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 @@ -21,7 +21,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* Build: v0.10.0 15-August-2013 01:07:22 */ +/* Build: v0.10.0 11-April-2014 12:11:52 */ var exports = exports || {}; var CSSLint = (function(){ /*! @@ -47,7 +47,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* Version v0.2.3, Build time: 19-June-2013 11:16:15 */ +/* Version v0.2.4, Build time: 7-January-2014 07:32:49 */ var parserlib = {}; (function(){ @@ -957,7 +957,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* Version v0.2.3, Build time: 19-June-2013 11:16:15 */ +/* Version v0.2.4, Build time: 7-January-2014 07:32:49 */ (function(){ var EventTarget = parserlib.util.EventTarget, TokenStreamBase = parserlib.util.TokenStreamBase, @@ -992,6 +992,7 @@ var Colors = { darkcyan :"#008b8b", darkgoldenrod :"#b8860b", darkgray :"#a9a9a9", + darkgrey :"#a9a9a9", darkgreen :"#006400", darkkhaki :"#bdb76b", darkmagenta :"#8b008b", @@ -1003,11 +1004,13 @@ var Colors = { darkseagreen :"#8fbc8f", darkslateblue :"#483d8b", darkslategray :"#2f4f4f", + darkslategrey :"#2f4f4f", darkturquoise :"#00ced1", darkviolet :"#9400d3", deeppink :"#ff1493", deepskyblue :"#00bfff", dimgray :"#696969", + dimgrey :"#696969", dodgerblue :"#1e90ff", firebrick :"#b22222", floralwhite :"#fffaf0", @@ -1018,6 +1021,7 @@ var Colors = { gold :"#ffd700", goldenrod :"#daa520", gray :"#808080", + grey :"#808080", green :"#008000", greenyellow :"#adff2f", honeydew :"#f0fff0", @@ -1035,12 +1039,14 @@ var Colors = { lightcyan :"#e0ffff", lightgoldenrodyellow :"#fafad2", lightgray :"#d3d3d3", + lightgrey :"#d3d3d3", lightgreen :"#90ee90", lightpink :"#ffb6c1", lightsalmon :"#ffa07a", lightseagreen :"#20b2aa", lightskyblue :"#87cefa", lightslategray :"#778899", + lightslategrey :"#778899", lightsteelblue :"#b0c4de", lightyellow :"#ffffe0", lime :"#00ff00", @@ -1093,6 +1099,7 @@ var Colors = { skyblue :"#87ceeb", slateblue :"#6a5acd", slategray :"#708090", + slategrey :"#708090", snow :"#fffafa", springgreen :"#00ff7f", steelblue :"#4682b4", @@ -1118,6 +1125,7 @@ var Colors = { buttontext :"Text on push buttons.", captiontext :"Text in caption, size box, and scrollbar arrow box.", graytext :"Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.", + greytext :"Greyed (disabled) text. This color is set to #000 if the current display driver does not support a solid grey color.", highlight :"Item(s) selected in a control.", highlighttext :"Text of item(s) selected in a control.", inactiveborder :"Inactive window border.", @@ -2812,7 +2820,7 @@ Parser.prototype = function(){ var tokenStream = this._tokenStream, values = [], - //valueParts = [], + //valueParts = [], value = null, operator = null; @@ -2829,9 +2837,9 @@ Parser.prototype = function(){ values.push(operator); } /*else { //if there's not an operator, you have a full value - values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col)); - valueParts = []; - }*/ + values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col)); + valueParts = []; + }*/ value = this._term(); @@ -2843,7 +2851,7 @@ Parser.prototype = function(){ } while(true); } - //cleanup + //cleanup /*if (valueParts.length){ values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col)); }*/ @@ -3534,6 +3542,12 @@ nth var Properties = { //A + "align-items" : "flex-start | flex-end | center | baseline | stretch", + "align-content" : "flex-start | flex-end | center | space-between | space-around | stretch", + "align-self" : "auto | flex-start | flex-end | center | baseline | stretch", + "-webkit-align-items" : "flex-start | flex-end | center | baseline | stretch", + "-webkit-align-content" : "flex-start | flex-end | center | space-between | space-around | stretch", + "-webkit-align-self" : "auto | flex-start | flex-end | center | baseline | stretch", "alignment-adjust" : "auto | baseline | before-edge | text-before-edge | middle | central | after-edge | text-after-edge | ideographic | alphabetic | hanging | mathematical | | ", "alignment-baseline" : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical", "animation" : 1, @@ -3729,15 +3743,24 @@ var Properties = { "border-top-width" : "", "border-width" : { multi: "", max: 4 }, "bottom" : " | inherit", - "box-align" : "start | end | center | baseline | stretch", //http://www.w3.org/TR/2009/WD-css3-flexbox-20090723/ - "box-decoration-break" : "slice |clone", - "box-direction" : "normal | reverse | inherit", - "box-flex" : "", - "box-flex-group" : "", - "box-lines" : "single | multiple", - "box-ordinal-group" : "", - "box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit", - "box-pack" : "start | end | center | justify", + "-moz-box-align" : "start | end | center | baseline | stretch", + "-moz-box-decoration-break" : "slice |clone", + "-moz-box-direction" : "normal | reverse | inherit", + "-moz-box-flex" : "", + "-moz-box-flex-group" : "", + "-moz-box-lines" : "single | multiple", + "-moz-box-ordinal-group" : "", + "-moz-box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit", + "-moz-box-pack" : "start | end | center | justify", + "-webkit-box-align" : "start | end | center | baseline | stretch", + "-webkit-box-decoration-break" : "slice |clone", + "-webkit-box-direction" : "normal | reverse | inherit", + "-webkit-box-flex" : "", + "-webkit-box-flex-group" : "", + "-webkit-box-lines" : "single | multiple", + "-webkit-box-ordinal-group" : "", + "-webkit-box-orient" : "horizontal | vertical | inline-axis | block-axis | inherit", + "-webkit-box-pack" : "start | end | center | justify", "box-shadow" : function (expression) { var result = false, part; @@ -3783,7 +3806,7 @@ var Properties = { //D "direction" : "ltr | rtl | inherit", - "display" : "inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | box | inline-box | grid | inline-grid | none | inherit | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker | -webkit-box | -webkit-inline-box", + "display" : "inline | block | list-item | inline-block | table | inline-table | table-row-group | table-header-group | table-footer-group | table-row | table-column-group | table-column | table-cell | table-caption | grid | inline-grid | none | inherit | -moz-box | -moz-inline-block | -moz-inline-box | -moz-inline-grid | -moz-inline-stack | -moz-inline-table | -moz-grid | -moz-grid-group | -moz-grid-line | -moz-groupbox | -moz-deck | -moz-popup | -moz-stack | -moz-marker | -webkit-box | -webkit-inline-box | -ms-flexbox | -ms-inline-flexbox | flex | -webkit-flex | inline-flex | -webkit-inline-flex", "dominant-baseline" : 1, "drop-initial-after-adjust" : "central | middle | after-edge | text-after-edge | ideographic | alphabetic | mathematical | | ", "drop-initial-after-align" : "baseline | use-script | before-edge | text-before-edge | after-edge | text-after-edge | central | middle | ideographic | alphabetic | hanging | mathematical", @@ -3800,6 +3823,26 @@ var Properties = { "filter" : 1, "fit" : "fill | hidden | meet | slice", "fit-position" : 1, + "flex" : "none | [ ? || ", + "flex-basis" : "", + "flex-direction" : "row | row-reverse | column | column-reverse", + "flex-flow" : " || ", + "flex-grow" : "", + "flex-shrink" : "", + "flex-wrap" : "nowrap | wrap | wrap-reverse", + "-webkit-flex" : "none | [ ? || ", + "-webkit-flex-basis" : "", + "-webkit-flex-direction" : "row | row-reverse | column | column-reverse", + "-webkit-flex-flow" : " || ", + "-webkit-flex-grow" : "", + "-webkit-flex-shrink" : "", + "-webkit-flex-wrap" : "nowrap | wrap | wrap-reverse", + "-ms-flex" : "[[ ? ] || [ || || auto ] ] | none", + "-ms-flex-align" : "start | end | center | stretch | baseline", + "-ms-flex-direction" : "row | column | row-reverse | column-reverse | inherit", + "-ms-flex-order" : "", + "-ms-flex-pack" : "start | end | center | justify", + "-ms-flex-wrap" : "nowrap | wrap | wrap-reverse", "float" : "left | right | none | inherit", "float-offset" : 1, "font" : 1, @@ -3843,6 +3886,10 @@ var Properties = { "image-resolution" : 1, "inline-box-align" : "initial | last | ", + //J + "justify-content" : "flex-start | flex-end | center | space-between | space-around", + "-webkit-justify-content" : "flex-start | flex-end | center | space-between | space-around", + //L "left" : " | inherit", "letter-spacing" : " | normal | inherit", @@ -3886,6 +3933,8 @@ var Properties = { //O "opacity" : " | inherit", + "order" : "", + "-webkit-order" : "", "orphans" : " | inherit", "outline" : 1, "outline-color" : " | invert | inherit", @@ -3894,6 +3943,7 @@ var Properties = { "outline-width" : " | inherit", "overflow" : "visible | hidden | scroll | auto | inherit", "overflow-style" : 1, + "overflow-wrap" : "normal | break-word", "overflow-x" : 1, "overflow-y" : 1, @@ -3971,6 +4021,8 @@ var Properties = { "text-transform" : "capitalize | uppercase | lowercase | none | inherit", "text-wrap" : "normal | none | avoid", "top" : " | inherit", + "-ms-touch-action" : "auto | none | pan-x | pan-y", + "touch-action" : "auto | none | pan-x | pan-y", "transform" : 1, "transform-origin" : 1, "transform-style" : 1, @@ -3981,7 +4033,7 @@ var Properties = { "transition-timing-function" : 1, //U - "unicode-bidi" : "normal | embed | bidi-override | inherit", + "unicode-bidi" : "normal | embed | isolate | bidi-override | isolate-override | plaintext | inherit", "user-modify" : "read-only | read-write | write-only | inherit", "user-select" : "none | text | toggle | element | elements | all | inherit", @@ -4005,7 +4057,8 @@ var Properties = { "width" : " | | auto | inherit" , "word-break" : "normal | keep-all | break-all", "word-spacing" : " | normal | inherit", - "word-wrap" : 1, + "word-wrap" : "normal | break-word", + "writing-mode" : "horizontal-tb | vertical-rl | vertical-lr | lr-tb | rl-tb | tb-rl | bt-rl | tb-lr | bt-lr | lr-bt | rl-bt | lr | rl | tb | inherit", //Z "z-index" : " | auto | inherit", @@ -4276,9 +4329,6 @@ function PropertyValuePart(text, line, col){ } - } else if (/^([+\-]?[\d\.]+)%$/i.test(text)){ //percentage - this.type = "percentage"; - this.value = +RegExp.$1; } else if (/^([+\-]?[\d\.]+)%$/i.test(text)){ //percentage this.type = "percentage"; this.value = +RegExp.$1; @@ -4375,6 +4425,7 @@ PropertyValuePart.prototype.constructor = PropertyValuePart; PropertyValuePart.fromToken = function(token){ return new PropertyValuePart(token.value, token.startLine, token.startCol); }; + var Pseudos = { ":first-letter": 1, ":first-line": 1, @@ -4668,12 +4719,12 @@ function isIdentStart(c){ } function mix(receiver, supplier){ - for (var prop in supplier){ - if (supplier.hasOwnProperty(prop)){ - receiver[prop] = supplier[prop]; - } - } - return receiver; + for (var prop in supplier){ + if (supplier.hasOwnProperty(prop)){ + receiver[prop] = supplier[prop]; + } + } + return receiver; } //----------------------------------------------------------------------------- @@ -4689,7 +4740,7 @@ function mix(receiver, supplier){ * @namespace parserlib.css */ function TokenStream(input){ - TokenStreamBase.call(this, input, Tokens); + TokenStreamBase.call(this, input, Tokens); } TokenStream.prototype = mix(new TokenStreamBase(), { @@ -5662,7 +5713,7 @@ var Tokens = [ { name: "FONT_FACE_SYM", text: "@font-face"}, { name: "CHARSET_SYM", text: "@charset"}, { name: "NAMESPACE_SYM", text: "@namespace"}, - { name: "VIEWPORT_SYM", text: "@viewport"}, + { name: "VIEWPORT_SYM", text: ["@viewport", "@-ms-viewport"]}, { name: "UNKNOWN_SYM" }, //{ name: "ATKEYWORD"}, @@ -6436,7 +6487,10 @@ exports[prop] = parserlib[prop]; * @static * @extends parserlib.util.EventTarget */ -/*global parserlib, Reporter*/ + +/* global parserlib, Reporter */ +/* exported CSSLint */ + var CSSLint = (function(){ var rules = [], @@ -6602,8 +6656,7 @@ var CSSLint = (function(){ */ api.verify = function(text, ruleset){ - var i = 0, - len = rules.length, + var i = 0, reporter, lines, report, @@ -6611,7 +6664,7 @@ var CSSLint = (function(){ underscoreHack: true, strict: false }); // normalize line endings - lines = text.replace(/\n\r?/g, "$split$").split('$split$'); + lines = text.replace(/\n\r?/g, "$split$").split("$split$"); if (!ruleset){ ruleset = this.getRuleset(); @@ -6668,7 +6721,6 @@ var CSSLint = (function(){ })(); -/*global CSSLint*/ /** * An instance of Report is used to report results of the * verification back to the main API. @@ -6830,8 +6882,6 @@ Reporter.prototype = { //expose for testing purposes CSSLint._Reporter = Reporter; -/*global CSSLint*/ - /* * Utility functions that make life easier. */ @@ -6891,10 +6941,11 @@ CSSLint.Util = { } } }; -/*global CSSLint*/ + /* * Rule: Don't use adjoining classes (.foo.bar). */ + CSSLint.addRule({ //rule information @@ -6936,7 +6987,6 @@ CSSLint.addRule({ } }); -/*global CSSLint*/ /* * Rule: Don't use width or height when using padding or border. @@ -7037,11 +7087,11 @@ CSSLint.addRule({ } }); -/*global CSSLint*/ /* * Rule: box-sizing doesn't work in IE6 and IE7. */ + CSSLint.addRule({ //rule information @@ -7065,11 +7115,12 @@ CSSLint.addRule({ } }); + /* * 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) */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7081,14 +7132,13 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ var rule = this, - count = 0, 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(event){ + parser.addListener("startfontface", function(){ fontFaceRule = true; }); @@ -7106,7 +7156,7 @@ CSSLint.addRule({ col = event.col; // This is the property that we care about, we can ignore the rest - if (propertyName === 'src') { + if (propertyName === "src") { var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i; // We need to handle the advanced syntax with two src properties @@ -7122,7 +7172,7 @@ CSSLint.addRule({ }); // Back to normal rules that we don't need to test - parser.addListener("endfontface", function(event){ + parser.addListener("endfontface", function(){ fontFaceRule = false; if (ruleFailed) { @@ -7131,11 +7181,12 @@ CSSLint.addRule({ }); } }); + /* * Rule: Include all compatible vendor prefixes to reach a wider * range of users. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7223,9 +7274,9 @@ CSSLint.addRule({ for (prop in compatiblePrefixes) { if (compatiblePrefixes.hasOwnProperty(prop)) { variations = []; - prefixed = compatiblePrefixes[prop].split(' '); + prefixed = compatiblePrefixes[prop].split(" "); for (i = 0, len = prefixed.length; i < len; i++) { - variations.push('-' + prefixed[i] + '-' + prop); + variations.push("-" + prefixed[i] + "-" + prop); } compatiblePrefixes[prop] = variations; arrayPush.apply(applyTo, variations); @@ -7240,7 +7291,7 @@ CSSLint.addRule({ inKeyFrame = event.prefix || true; }); - parser.addListener("endkeyframes", function (event) { + parser.addListener("endkeyframes", function () { inKeyFrame = false; }); @@ -7256,7 +7307,7 @@ CSSLint.addRule({ } }); - parser.addListener("endrule", function (event) { + parser.addListener("endrule", function () { if (!properties.length) { return; } @@ -7317,6 +7368,7 @@ CSSLint.addRule({ }); } }); + /* * Rule: Certain properties don't play well with certain display values. * - float should not be used with inline-block @@ -7324,7 +7376,7 @@ CSSLint.addRule({ * - vertical-align should not be used with block * - margin, float should not be used with table-* */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7434,10 +7486,11 @@ CSSLint.addRule({ } }); + /* * Rule: Disallow duplicate background-images (using url). */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7458,8 +7511,8 @@ CSSLint.addRule({ 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') { + if (value.parts[i].type == "uri") { + if (typeof stack[value.parts[i].uri] === "undefined") { stack[value.parts[i].uri] = event; } else { @@ -7471,11 +7524,12 @@ CSSLint.addRule({ }); } }); + /* * 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. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7490,7 +7544,7 @@ CSSLint.addRule({ properties, lastProperty; - function startRule(event){ + function startRule(){ properties = {}; } @@ -7517,10 +7571,11 @@ CSSLint.addRule({ } }); + /* * Rule: Style rules without any properties defined should be removed. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7551,10 +7606,11 @@ CSSLint.addRule({ } }); + /* * Rule: There should be no syntax errors. (Duh.) */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7575,7 +7631,6 @@ CSSLint.addRule({ }); -/*global CSSLint*/ CSSLint.addRule({ //rule information @@ -7605,7 +7660,7 @@ CSSLint.addRule({ }, properties; - function startRule(event){ + function startRule(){ properties = {}; lastProperty = null; } @@ -7651,11 +7706,12 @@ CSSLint.addRule({ } }); + /* * Rule: You shouldn't use more than 10 floats. If you do, there's probably * room for some abstraction. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7687,10 +7743,11 @@ CSSLint.addRule({ } }); + /* * Rule: Avoid too many @font-face declarations in the same stylesheet. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7717,11 +7774,11 @@ CSSLint.addRule({ } }); + /* * Rule: You shouldn't need more than 9 font-size declarations. */ -/*global CSSLint*/ CSSLint.addRule({ //rule information @@ -7752,10 +7809,11 @@ CSSLint.addRule({ } }); + /* * Rule: When using a vendor-prefixed gradient, make sure to use them all. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7820,7 +7878,7 @@ CSSLint.addRule({ /* * Rule: Don't use IDs for selectors. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7867,10 +7925,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use @import, use instead. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7890,12 +7949,13 @@ CSSLint.addRule({ } }); + /* * 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. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7927,11 +7987,12 @@ CSSLint.addRule({ } }); + /* * Rule: Properties should be known (listed in CSS3 specification) or * be a vendor-prefixed property. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7945,7 +8006,6 @@ CSSLint.addRule({ var rule = this; parser.addListener("property", function(event){ - var name = event.property.text.toLowerCase(); // the check is handled entirely by the parser-lib (https://github.com/nzakas/parser-lib) if (event.invalid) { @@ -7956,11 +8016,58 @@ CSSLint.addRule({ } }); + +/* + * Rule: All properties should be in alphabetical order.. + */ +/*global CSSLint*/ +CSSLint.addRule({ + + //rule information + id: "order-alphabetical", + name: "Alphabetical order", + desc: "Assure properties are in alphabetical order", + browsers: "All", + + //initialization + init: function(parser, reporter){ + var rule = this, + properties; + + var startRule = function () { + properties = []; + }; + + parser.addListener("startrule", startRule); + parser.addListener("startfontface", startRule); + parser.addListener("startpage", startRule); + parser.addListener("startpagemargin", startRule); + parser.addListener("startkeyframerule", startRule); + + parser.addListener("property", function(event){ + var name = event.property.text, + lowerCasePrefixLessName = name.toLowerCase().replace(/^-.*?-/, ""); + + properties.push(lowerCasePrefixLessName); + }); + + parser.addListener("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 ordered.", event.line, event.col, rule); + } + }); + } + +}); + /* * 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. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -7989,7 +8096,7 @@ CSSLint.addRule({ } } - function endRule(event){ + function endRule(){ if (lastRule){ if (lastRule.outline){ if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") == -1){ @@ -8029,10 +8136,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use classes or IDs with elements (a.foo or a#foo). */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8092,10 +8200,11 @@ CSSLint.addRule({ } }); + /* * Rule: Headings (h1-h6) should not be qualified (namespaced). */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8130,10 +8239,11 @@ CSSLint.addRule({ } }); + /* * Rule: Selectors that look like regular expressions are slow and should be avoided. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8174,10 +8284,11 @@ CSSLint.addRule({ } }); + /* * Rule: Total number of rules should not exceed x. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8188,8 +8299,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ - var rule = this, - count = 0; + var count = 0; //count each rule parser.addListener("startrule", function(){ @@ -8202,10 +8312,11 @@ CSSLint.addRule({ } }); + /* * Rule: Warn people with approaching the IE 4095 limit */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8218,7 +8329,7 @@ CSSLint.addRule({ init: function(parser, reporter) { var rule = this, count = 0; - parser.addListener('startrule', function(event) { + parser.addListener("startrule", function(event) { count += event.selectors.length; }); @@ -8234,7 +8345,7 @@ CSSLint.addRule({ /* * Rule: Warn people past the IE 4095 limit */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8247,7 +8358,7 @@ CSSLint.addRule({ init: function(parser, reporter){ var rule = this, count = 0; - parser.addListener('startrule',function(event) { + parser.addListener("startrule", function(event) { count += event.selectors.length; }); @@ -8259,11 +8370,47 @@ CSSLint.addRule({ } }); + +/* + * 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) { + var rule = this; + + parser.addListener("startrule", function(event) { + var i, len, selector, p, pLen, part, previousLine, currentLine, + 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++) { + part = selector.parts[p]; + currentLine = part.line; + if (currentLine === previousLine) { + reporter.report("newline character found in selector (forgot a comma?)", currentLine, selectors[i].parts[0].col, rule); + } + previousLine = currentLine; + } + } + }); + } +}); + /* * Rule: Use shorthand properties where possible. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8302,7 +8449,7 @@ CSSLint.addRule({ } } - function startRule(event){ + function startRule(){ properties = {}; } @@ -8332,8 +8479,7 @@ CSSLint.addRule({ //check for use of "font-size" parser.addListener("property", function(event){ - var name = event.property.toString().toLowerCase(), - value = event.value.parts[0].value; + var name = event.property.toString().toLowerCase(); if (propertiesToCheck[name]){ properties[name] = 1; @@ -8346,11 +8492,12 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use properties with a star prefix. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8373,11 +8520,12 @@ CSSLint.addRule({ }); } }); + /* * Rule: Don't use text-indent for image replacement if you need to support rtl. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8393,13 +8541,13 @@ CSSLint.addRule({ direction; - function startRule(event){ + function startRule(){ textIndent = false; direction = "inherit"; } //event handler for end of rules - function endRule(event){ + 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); } @@ -8426,11 +8574,12 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use properties with a underscore prefix. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8453,10 +8602,11 @@ CSSLint.addRule({ }); } }); + /* * Rule: Headings (h1-h6) should be defined only once. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8469,7 +8619,7 @@ CSSLint.addRule({ init: function(parser, reporter){ var rule = this; - var headings = { + var headings = { h1: 0, h2: 0, h3: 0, @@ -8508,7 +8658,7 @@ CSSLint.addRule({ } }); - parser.addListener("endstylesheet", function(event){ + parser.addListener("endstylesheet", function(){ var prop, messages = []; @@ -8527,10 +8677,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use universal selector because it's slow. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8547,8 +8698,7 @@ CSSLint.addRule({ var selectors = event.selectors, selector, part, - modifier, - i, j, k; + i; for (i=0; i < selectors.length; i++){ selector = selectors[i]; @@ -8562,10 +8712,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use unqualified attribute selectors because they're just like universal selectors. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8584,7 +8735,7 @@ CSSLint.addRule({ selector, part, modifier, - i, j, k; + i, k; for (i=0; i < selectors.length; i++){ selector = selectors[i]; @@ -8604,11 +8755,12 @@ CSSLint.addRule({ } }); + /* * Rule: When using a vendor-prefixed property, make sure to * include the standard one. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8679,11 +8831,7 @@ CSSLint.addRule({ "-ms-transform-origin" : "transform-origin", "-moz-box-sizing" : "box-sizing", - "-webkit-box-sizing" : "box-sizing", - - "-moz-user-select" : "user-select", - "-khtml-user-select" : "user-select", - "-webkit-user-select" : "user-select" + "-webkit-box-sizing" : "box-sizing" }; //event handler for beginning of rules @@ -8693,10 +8841,10 @@ CSSLint.addRule({ } //event handler for end of rules - function endRule(event){ + function endRule(){ var prop, - i, len, - standard, + i, + len, needed, actual, needsStandard = []; @@ -8747,10 +8895,11 @@ CSSLint.addRule({ } }); + /* * Rule: You don't need to specify units when a value is 0. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -8781,7 +8930,7 @@ CSSLint.addRule({ } }); -/*global CSSLint*/ + (function() { /** @@ -8853,7 +9002,7 @@ CSSLint.addRule({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = []; @@ -8865,20 +9014,20 @@ CSSLint.addRule({ * @return rule source as {String} */ var generateSource = function(rule) { - if (!rule || !('name' in rule)) { + if (!rule || !("name" in rule)) { return ""; } - return 'net.csslint.' + rule.name.replace(/\s/g,''); + return "net.csslint." + rule.name.replace(/\s/g,""); }; if (messages.length > 0) { output.push(""); - CSSLint.Util.forEach(messages, function (message, i) { + CSSLint.Util.forEach(messages, function (message) { //ignore rollups for now if (!message.rollup) { - output.push(""); } }); @@ -8890,7 +9039,7 @@ CSSLint.addRule({ }); }()); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "compact", @@ -8937,19 +9086,19 @@ CSSLint.addFormatter({ return options.quiet ? "" : filename + ": Lint Free!"; } - CSSLint.Util.forEach(messages, function(message, i) { + CSSLint.Util.forEach(messages, function(message) { if (message.rollup) { output += filename + ": " + capitalize(message.type) + " - " + message.message + "\n"; } else { output += filename + ": " + "line " + message.line + - ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + "\n"; + ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + " (" + message.rule.id + ")\n"; } }); return output; } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "csslint-xml", @@ -8978,7 +9127,7 @@ CSSLint.addFormatter({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = []; @@ -9003,7 +9152,7 @@ CSSLint.addFormatter({ if (messages.length > 0) { output.push(""); - CSSLint.Util.forEach(messages, function (message, i) { + CSSLint.Util.forEach(messages, function (message) { if (message.rollup) { output.push(""); } else { @@ -9017,7 +9166,7 @@ CSSLint.addFormatter({ return output.join(""); } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "junit-xml", @@ -9046,13 +9195,13 @@ CSSLint.addFormatter({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = [], tests = { - 'error': 0, - 'failure': 0 + "error": 0, + "failure": 0 }; /** @@ -9063,10 +9212,10 @@ CSSLint.addFormatter({ * @return rule source as {String} */ var generateSource = function(rule) { - if (!rule || !('name' in rule)) { + if (!rule || !("name" in rule)) { return ""; } - return 'net.csslint.' + rule.name.replace(/\s/g,''); + return "net.csslint." + rule.name.replace(/\s/g,""); }; /** @@ -9092,11 +9241,11 @@ CSSLint.addFormatter({ if (messages.length > 0) { - messages.forEach(function (message, i) { + messages.forEach(function (message) { // since junit has no warning class // all issues as errors - var type = message.type === 'warning' ? 'error' : message.type; + var type = message.type === "warning" ? "error" : message.type; //ignore rollups for now if (!message.rollup) { @@ -9104,7 +9253,7 @@ CSSLint.addFormatter({ // build the test case seperately, once joined // we'll add it to a custom array filtered by type output.push(""); - output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\">"); + output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\">"); output.push(""); tests[type] += 1; @@ -9122,7 +9271,7 @@ CSSLint.addFormatter({ } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "lint-xml", @@ -9151,7 +9300,7 @@ CSSLint.addFormatter({ * @param options {Object} (UNUSED for now) specifies special handling of output * @return {String} output for results */ - formatResults: function(results, filename, options) { + formatResults: function(results, filename/*, options*/) { var messages = results.messages, output = []; @@ -9177,7 +9326,7 @@ CSSLint.addFormatter({ if (messages.length > 0) { output.push(""); - CSSLint.Util.forEach(messages, function (message, i) { + CSSLint.Util.forEach(messages, function (message) { if (message.rollup) { output.push(""); } else { @@ -9191,7 +9340,7 @@ CSSLint.addFormatter({ return output.join(""); } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "text", @@ -9229,7 +9378,14 @@ CSSLint.addFormatter({ return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + "."; } - output = "\n\ncsslint: There are " + messages.length + " problems 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; @@ -9255,5 +9411,6 @@ CSSLint.addFormatter({ return output; } }); + return CSSLint; })(); \ No newline at end of file diff --git a/package.json b/package.json index 43f5d9c9..df4c24dc 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,9 @@ "url": "https://github.com/CSSLint/csslint/issues" }, "license": "MIT", - "main": "./lib/csslint-node.js", + "main": "./dist/csslint-node.js", "bin": { - "csslint": "./cli.js" + "csslint": "./dist/cli.js" }, "scripts": { "test": "grunt test" @@ -30,7 +30,6 @@ "grunt": "~0.4.5", "grunt-contrib-clean": "~0.7.0", "grunt-contrib-concat": "~0.5.1", - "grunt-contrib-copy": "~0.8.0", "grunt-contrib-jshint": "~0.11.0", "grunt-contrib-watch": "~0.6.1", "grunt-include-replace": "~3.2.0", @@ -43,5 +42,11 @@ }, "directories": { "test": "tests" - } + }, + "files": [ + "dist/cli.js", + "dist/csslint-node.js", + "CHANGELOG", + "LICENSE" + ] } diff --git a/release/npm/lib/csslint-node.js b/release/npm/lib/csslint-node.js deleted file mode 100644 index f800f974..00000000 --- a/release/npm/lib/csslint-node.js +++ /dev/null @@ -1,2851 +0,0 @@ -/*! -CSSLint -Copyright (c) 2013 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. - -*/ -/* Build: v0.10.0 15-August-2013 01:07:22 */ -var parserlib = require("parserlib"); -/** - * Main CSSLint object. - * @class CSSLint - * @static - * @extends parserlib.util.EventTarget - */ -/*global parserlib, Reporter*/ -var CSSLint = (function(){ - - var rules = [], - formatters = [], - embeddedRuleset = /\/\*csslint([^\*]*)\*\//, - api = new parserlib.util.EventTarget(); - - api.version = "0.10.0"; - - //------------------------------------------------------------------------- - // 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, - len = rules.length, - reporter, - lines, - 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$'); - - if (!ruleset){ - ruleset = this.getRuleset(); - } - - if (embeddedRuleset.test(text)){ - ruleset = applyEmbeddedRuleset(text, ruleset); - } - - reporter = new Reporter(lines, ruleset); - - 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 - }; - - //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; - -})(); - -/*global CSSLint*/ -/** - * 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. - */ -function Reporter(lines, ruleset){ - - /** - * 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; -} - -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){ - 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){ - 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){ - 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){ - 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){ - 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){ - 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){ - this.stats[name] = value; - } -}; - -//expose for testing purposes -CSSLint._Reporter = Reporter; - -/*global CSSLint*/ - -/* - * Utility functions that make life easier. - */ -CSSLint.Util = { - /* - * Adds all properties from supplier onto receiver, - * overwriting if the same name already exists on - * reciever. - * @param {Object} The object to receive the properties. - * @param {Object} The object to provide the properties. - * @return {Object} The receiver - */ - mix: function(receiver, supplier){ - 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){ - 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) { - if (values.forEach){ - return values.forEach(func); - } else { - for (var i=0, len=values.length; i < len; i++){ - func(values[i], i, values); - } - } - } -}; -/*global CSSLint*/ -/* - * 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.", - browsers: "IE6", - - //initialization - init: function(parser, reporter){ - 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("Don't use adjoining classes.", part.line, part.col, rule); - } - } - } - } - } - }); - } - -}); -/*global CSSLint*/ - -/* - * 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.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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("property", function(event){ - var name = event.property.text.toLowerCase(); - - if (heightProperties[name] || widthProperties[name]){ - if (!/^0\S*$/.test(event.value) && !(name == "border" && event.value == "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); - } - -}); -/*global CSSLint*/ - -/* - * 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.", - browsers: "IE6, IE7", - tags: ["Compatibility"], - - //initialization - init: function(parser, reporter){ - 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) - */ -/*global CSSLint*/ -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).", - browsers: "All", - - //initialization - init: function(parser, reporter){ - var rule = this, - count = 0, - 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(event){ - 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(event){ - 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. - */ -/*global CSSLint*/ -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.", - browsers: "All", - - //initialization - init: function (parser, reporter) { - 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 moz", - "animation-delay" : "webkit moz", - "animation-direction" : "webkit moz", - "animation-duration" : "webkit moz", - "animation-fill-mode" : "webkit moz", - "animation-iteration-count" : "webkit moz", - "animation-name" : "webkit moz", - "animation-play-state" : "webkit moz", - "animation-timing-function" : "webkit moz", - "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" : "webkit moz", - "box-shadow" : "webkit moz", - "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 moz ms o", - "transform-origin" : "webkit moz ms o", - "transition" : "webkit moz o", - "transition-delay" : "webkit moz o", - "transition-duration" : "webkit moz o", - "transition-property" : "webkit moz o", - "transition-timing-function" : "webkit moz o", - "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 (event) { - 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 (event) { - 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-* - */ -/*global CSSLint*/ -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.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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("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); - - } - -}); -/* - * Rule: Disallow duplicate background-images (using url). - */ -/*global CSSLint*/ -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.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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. - */ -/*global CSSLint*/ -CSSLint.addRule({ - - //rule information - id: "duplicate-properties", - name: "Disallow duplicate properties", - desc: "Duplicate properties must appear one after the other.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - var rule = this, - properties, - lastProperty; - - function startRule(event){ - properties = {}; - } - - parser.addListener("startrule", startRule); - parser.addListener("startfontface", startRule); - parser.addListener("startpage", startRule); - parser.addListener("startpagemargin", startRule); - parser.addListener("startkeyframerule", 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. - */ -/*global CSSLint*/ -CSSLint.addRule({ - - //rule information - id: "empty-rules", - name: "Disallow empty rules", - desc: "Rules without any properties specified should be removed.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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.) - */ -/*global CSSLint*/ -CSSLint.addRule({ - - //rule information - id: "errors", - name: "Parsing Errors", - desc: "This rule looks for recoverable syntax errors.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - var rule = this; - - parser.addListener("error", function(event){ - reporter.error(event.message, event.line, event.col, rule); - }); - - } - -}); - -/*global CSSLint*/ -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.", - browsers: "IE6,IE7,IE8", - - //initialization - init: function(parser, reporter){ - 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 - }, - properties; - - function startRule(event){ - properties = {}; - lastProperty = null; - } - - parser.addListener("startrule", startRule); - parser.addListener("startfontface", startRule); - parser.addListener("startpage", startRule); - parser.addListener("startpagemargin", startRule); - parser.addListener("startkeyframerule", 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. - */ -/*global CSSLint*/ -CSSLint.addRule({ - - //rule information - id: "floats", - name: "Disallow too many floats", - desc: "This rule tests if the float property is used too many times", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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. - */ -/*global CSSLint*/ -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.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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. - */ - -/*global CSSLint*/ -CSSLint.addRule({ - - //rule information - id: "font-sizes", - name: "Disallow too many font sizes", - desc: "Checks the number of font-size declarations.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - var rule = this, - count = 0; - - //check for use of "font-size" - parser.addListener("property", function(event){ - if (event.property == "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. - */ -/*global CSSLint*/ -CSSLint.addRule({ - - //rule information - id: "gradients", - name: "Require all gradient definitions", - desc: "When using a vendor-prefixed gradient, make sure to use them all.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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. - */ -/*global CSSLint*/ -CSSLint.addRule({ - - //rule information - id: "ids", - name: "Disallow IDs in selectors", - desc: "Selectors should not contain IDs.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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: Don't use @import, use instead. - */ -/*global CSSLint*/ -CSSLint.addRule({ - - //rule information - id: "import", - name: "Disallow @import", - desc: "Don't use @import, use instead.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - var rule = this; - - parser.addListener("import", function(event){ - reporter.report("@import prevents parallel downloads, use 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. - */ -/*global CSSLint*/ -CSSLint.addRule({ - - //rule information - id: "important", - name: "Disallow !important", - desc: "Be careful when using !important declaration", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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. - */ -/*global CSSLint*/ -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.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - var rule = this; - - parser.addListener("property", function(event){ - var name = event.property.text.toLowerCase(); - - // 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: outline: none or outline: 0 should only be used in a :focus rule - * and only if there are other properties in the same rule. - */ -/*global CSSLint*/ -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.", - browsers: "All", - tags: ["Accessibility"], - - //initialization - init: function(parser, reporter){ - 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(event){ - 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("property", function(event){ - var name = event.property.text.toLowerCase(), - value = event.value; - - if (lastRule){ - lastRule.propCount++; - if (name == "outline" && (value == "none" || value == "0")){ - lastRule.outline = true; - } - } - - }); - - parser.addListener("endrule", endRule); - parser.addListener("endfontface", endRule); - parser.addListener("endpage", endRule); - parser.addListener("endpagemargin", endRule); - parser.addListener("endkeyframerule", endRule); - - } - -}); -/* - * Rule: Don't use classes or IDs with elements (a.foo or a#foo). - */ -/*global CSSLint*/ -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).", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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). - */ -/*global CSSLint*/ -CSSLint.addRule({ - - //rule information - id: "qualified-headings", - name: "Disallow qualified headings", - desc: "Headings should not be qualified (namespaced).", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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. - */ -/*global CSSLint*/ -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.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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. - */ -/*global CSSLint*/ -CSSLint.addRule({ - - //rule information - id: "rules-count", - name: "Rules Count", - desc: "Track how many rules there are.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - var rule = this, - 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 - */ -/*global CSSLint*/ -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) { - 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 - */ -/*global CSSLint*/ -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){ - 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: Use shorthand properties where possible. - * - */ -/*global CSSLint*/ -CSSLint.addRule({ - - //rule information - id: "shorthand", - name: "Require shorthand properties", - desc: "Use shorthand properties where possible.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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(event){ - 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(), - value = event.value.parts[0].value; - - if (propertiesToCheck[name]){ - properties[name] = 1; - } - }); - - parser.addListener("endrule", endRule); - parser.addListener("endfontface", endRule); - - } - -}); -/* - * Rule: Don't use properties with a star prefix. - * - */ -/*global CSSLint*/ -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)", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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. - * - */ -/*global CSSLint*/ -CSSLint.addRule({ - - //rule information - id: "text-indent", - name: "Disallow negative text-indent", - desc: "Checks for text indent less than -99px", - browsers: "All", - - //initialization - init: function(parser, reporter){ - var rule = this, - textIndent, - direction; - - - function startRule(event){ - textIndent = false; - direction = "inherit"; - } - - //event handler for end of rules - function endRule(event){ - 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 == "ltr"){ - direction = "ltr"; - } - }); - - parser.addListener("endrule", endRule); - parser.addListener("endfontface", endRule); - - } - -}); -/* - * Rule: Don't use properties with a underscore prefix. - * - */ -/*global CSSLint*/ -CSSLint.addRule({ - - //rule information - id: "underscore-property-hack", - name: "Disallow properties with an underscore prefix", - desc: "Checks for the underscore property hack (targets IE6)", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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. - */ -/*global CSSLint*/ -CSSLint.addRule({ - - //rule information - id: "unique-headings", - name: "Headings should only be defined once", - desc: "Headings should be defined only once.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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(event){ - 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. - */ -/*global CSSLint*/ -CSSLint.addRule({ - - //rule information - id: "universal-selector", - name: "Disallow universal selector", - desc: "The universal selector (*) is known to be slow.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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]; - - 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. - */ -/*global CSSLint*/ -CSSLint.addRule({ - - //rule information - id: "unqualified-attributes", - name: "Disallow unqualified attribute selectors", - desc: "Unqualified attribute selectors are known to be slow.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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]; - - 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 == "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. - */ -/*global CSSLint*/ -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.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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", - - "-moz-user-select" : "user-select", - "-khtml-user-select" : "user-select", - "-webkit-user-select" : "user-select" - }; - - //event handler for beginning of rules - function startRule(){ - properties = {}; - num=1; - } - - //event handler for end of rules - function endRule(event){ - var prop, - i, len, - standard, - 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("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); - } - -}); -/* - * Rule: You don't need to specify units when a value is 0. - */ -/*global CSSLint*/ -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.", - browsers: "All", - - //initialization - init: function(parser, reporter){ - 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++; - } - - }); - - } - -}); -/*global CSSLint*/ -(function() { - - /** - * 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 ""; - }, - - /** - * Return closing root XML tag. - * @return {String} to append after all results - */ - endFormat: function(){ - return ""; - }, - - /** - * 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 ""; - }, - - /** - * 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(""); - CSSLint.Util.forEach(messages, function (message, i) { - //ignore rollups for now - if (!message.rollup) { - output.push(""); - } - }); - output.push(""); - } - - return output.join(""); - } - }); - -}()); -/*global CSSLint*/ -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() { - return ""; - }, - - /** - * Return content to be printed after all file results. - * @return {String} to append after all results - */ - endFormat: function() { - 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) { - 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, i) { - if (message.rollup) { - output += filename + ": " + capitalize(message.type) + " - " + message.message + "\n"; - } else { - output += filename + ": " + "line " + message.line + - ", col " + message.col + ", " + capitalize(message.type) + " - " + message.message + "\n"; - } - }); - - return output; - } -}); -/*global CSSLint*/ -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(){ - return ""; - }, - - /** - * Return closing root XML tag. - * @return {String} to append after all results - */ - endFormat: function(){ - 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} (UNUSED for now) specifies special handling of output - * @return {String} output for results - */ - formatResults: function(results, filename, options) { - 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, ">"); - }; - - if (messages.length > 0) { - output.push(""); - CSSLint.Util.forEach(messages, function (message, i) { - if (message.rollup) { - output.push(""); - } else { - output.push(""); - } - }); - output.push(""); - } - - return output.join(""); - } -}); -/*global CSSLint*/ -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(){ - return ""; - }, - - /** - * Return closing root XML tag. - * @return {String} to append after all results - */ - endFormat: function() { - 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} (UNUSED for now) specifies special handling of output - * @return {String} output for results - */ - formatResults: function(results, filename, options) { - - 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, ">"); - - }; - - if (messages.length > 0) { - - messages.forEach(function (message, i) { - - // 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 seperately, once joined - // we'll add it to a custom array filtered by type - output.push(""); - output.push("<" + type + " message=\"" + escapeSpecialCharacters(message.message) + "\">"); - output.push(""); - - tests[type] += 1; - - } - - }); - - output.unshift(""); - output.push(""); - - } - - return output.join(""); - - } -}); -/*global CSSLint*/ -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(){ - return ""; - }, - - /** - * Return closing root XML tag. - * @return {String} to append after all results - */ - endFormat: function(){ - 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} (UNUSED for now) specifies special handling of output - * @return {String} output for results - */ - formatResults: function(results, filename, options) { - 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, ">"); - }; - - if (messages.length > 0) { - - output.push(""); - CSSLint.Util.forEach(messages, function (message, i) { - if (message.rollup) { - output.push(""); - } else { - output.push(""); - } - }); - output.push(""); - } - - return output.join(""); - } -}); -/*global CSSLint*/ -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() { - return ""; - }, - - /** - * Return content to be printed after all file results. - * @return {String} to append after all results - */ - endFormat: function() { - 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) { - var messages = results.messages, - output = ""; - options = options || {}; - - if (messages.length === 0) { - return options.quiet ? "" : "\n\ncsslint: No errors in " + filename + "."; - } - - output = "\n\ncsslint: There are " + messages.length + " problems 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; - } -}); -exports.CSSLint = CSSLint; \ No newline at end of file diff --git a/release/npm/package.json b/release/npm/package.json deleted file mode 100644 index 72c49c5c..00000000 --- a/release/npm/package.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "name": "csslint", - "version": "0.10.0", - "description": "CSSLint", - "author": { - "name": "Nicole Sullivan" - }, - "contributors": [ - { - "name": "Nicholas C. Zakas" - } - ], - "engines": { - "node": ">=0.8.0" - }, - "directories": { - "lib": "lib" - }, - "main": "./lib/csslint-node.js", - "bin": { - "csslint": "./cli.js" - }, - "scripts": { - "test": "grunt test" - }, - "licenses": [ - { - "type": "MIT", - "url": "https://github.com/stubbornella/csslint/blob/master/LICENSE" - } - ], - "bugs": { - "url": "https://github.com/stubbornella/csslint/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/stubbornella/csslint.git" - }, - "dependencies": { - "parserlib": "~0.2.2" - }, - "devDependencies": { - "grunt": "~0.4.1", - "grunt-contrib-jshint": "~0.5.4", - "grunt-contrib-concat": "~0.3.0", - "grunt-contrib-watch": "~0.4.4", - "yuitest": "~0.7.9", - "grunt-contrib-clean": "~0.4.1", - "grunt-contrib-copy": "~0.4.1", - "grunt-include-replace": "~0.4.0" - } -} diff --git a/src/cli/node.js b/src/cli/node.js index 662f531a..0f3a5cb0 100644 --- a/src/cli/node.js +++ b/src/cli/node.js @@ -8,7 +8,7 @@ var fs = require("fs"), path = require("path"), - CSSLint = require("./lib/csslint-node").CSSLint; + CSSLint = require("./csslint-node").CSSLint; cli({ args: process.argv.slice(2), diff --git a/tasks/test_rhino.js b/tasks/test_rhino.js index 223e8910..489e10d9 100644 --- a/tasks/test_rhino.js +++ b/tasks/test_rhino.js @@ -10,7 +10,7 @@ module.exports = function( grunt ) { files.forEach(function(filepath) { grunt.util.spawn({ cmd: "java", - args: ["-jar", "lib/js.jar", "lib/yuitest-rhino-cli.js", "build/csslint.js", filepath], + args: ["-jar", "lib/js.jar", "lib/yuitest-rhino-cli.js", "dist/csslint.js", filepath], opts: {stdio: "inherit"} }, function() { progress--; diff --git a/tasks/yuitest.js b/tasks/yuitest.js index c898c4b1..0440664c 100644 --- a/tasks/yuitest.js +++ b/tasks/yuitest.js @@ -4,7 +4,7 @@ module.exports = function( grunt ) { grunt.registerMultiTask("yuitest", "Run the YUITests for the project", function() { var YUITest = require("yuitest"); - var CSSLint = require("../build/csslint-node").CSSLint; // jshint ignore:line + var CSSLint = require("../dist/csslint-node").CSSLint; // jshint ignore:line var files = this.filesSrc; var TestRunner = YUITest.TestRunner; var done = this.async(); diff --git a/tests/testrunner.htm b/tests/testrunner.htm index 8edc3830..fba985aa 100644 --- a/tests/testrunner.htm +++ b/tests/testrunner.htm @@ -5,9 +5,9 @@ YUI Test - + - +