diff --git a/.gitattributes b/.gitattributes index dfe07704..bb1f0b47 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ # Auto detect text files and perform LF normalization -* text=auto +* text eol=lf +*.jar binary 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/.jshintrc b/.jshintrc index 4cf0db5e..1cc6261b 100644 --- a/.jshintrc +++ b/.jshintrc @@ -2,6 +2,7 @@ "camelcase": true, "curly": true, "eqeqeq": true, + "es3": true, "forin": true, "immed": true, "indent": 4, @@ -15,7 +16,6 @@ "undef": true, "unused": true, "globals": { - "CSSLint": true, - "YUITest": true + "CSSLint": true } -} \ No newline at end of file +} diff --git a/Gruntfile.js b/Gruntfile.js index 3a5efa19..1cf3cea4 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -4,18 +4,15 @@ module.exports = function(grunt) { + // Force use of Unix newlines + grunt.util.linefeed = "\n"; + // Project configuration. grunt.initConfig({ // Metadata. pkg: grunt.file.readJSON("package.json"), - banner: { - compact: "/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - " + - "<%= grunt.template.today('yyyy-mm-dd') %>\n" + - "<%= pkg.homepage ? '* ' + pkg.homepage + '\\n' : '' %>" + - "* Copyright (c) <%= grunt.template.today('yyyy') %> Nicole Sullivan and Nicholas C. Zakas;\n" + - "* Licensed <%= _.pluck(pkg.licenses, 'type').join(', ') %> <%= _.pluck(pkg.licenses, 'url').join(', ') %> */\n", - full: "/*!\n" + - "CSSLint\n" + + banner: "/*!\n" + + "CSSLint v<%= pkg.version %>\n" + "Copyright (c) <%= grunt.template.today('yyyy') %> Nicole Sullivan and Nicholas C. Zakas. All rights reserved.\n" + "\n" + "Permission is hereby granted, free of charge, to any person obtaining a copy\n" + @@ -35,10 +32,8 @@ module.exports = function(grunt) { "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n" + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n" + "THE SOFTWARE.\n\n" + - "*/\n" + - "/* Build: v<%= pkg.version %> <%= grunt.template.today('dd-mmmm-yyyy hh:MM:ss') %> */" - }, - build_dir: "build", + "*/\n", + 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 @@ -56,27 +51,30 @@ module.exports = function(grunt) { "<%= clone %>", "<%= csslint_files %>" ], + // Task configuration. clean: { - build: ["<%= build_dir %>"], - release: ["release"] + dist: "<%= build_dir %>" }, + changelog: { dest: "CHANGELOG" }, + concat: { core: { options: { - banner: "<%= banner.full %>\n" + - //Hack for using the node version of parserlib - "var exports = exports || {};\n" + + banner: "<%= banner %>\n" + + // Hack for using the node version of parserlib and clone + "var module = module || {},\n" + + " exports = exports || {};\n\n" + "var CSSLint = (function(){\n", footer: "\nreturn CSSLint;\n})();" }, src: [ "<%= core_files %>" ], - dest: "<%= build_dir %>/<%= pkg.name %>.js" + dest: "<%= build_dir %>/csslint.js" },//Build environment workers rhino: { src: [ @@ -84,40 +82,39 @@ module.exports = function(grunt) { "src/cli/common.js", "src/cli/rhino.js" ], - dest: "<%= build_dir %>/<%= pkg.name %>-rhino.js" + dest: "<%= build_dir %>/csslint-rhino.js" }, node: { options: { - banner: "<%= banner.full %>\n" + + banner: "<%= banner %>" + "var clone = require('clone');\n" + "var parserlib = require('parserlib');\n", 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 %>/csslint-node.js": ["<%= csslint_files %>"] } }, node_cli: { options: { - banner: "#!/usr/bin/env node\n<%= banner.full %>" + banner: "#!/usr/bin/env node\n<%= banner %>" }, src: [ "src/cli/common.js", "src/cli/node.js" ], - dest: "<%= build_dir %>/npm/cli.js" + dest: "<%= build_dir %>/cli.js" }, tests: { src: [ "tests/**/*.js", "!tests/all-rules.js" ], - dest: "<%= build_dir %>/<%= pkg.name %>-tests.js" + dest: "<%= build_dir %>/csslint-tests.js" }, worker: { options: { - banner: "<%= banner.full %>\n" + + banner: "<%= banner %>" + //Hack for using the node version of parserlib "var exports = exports || {};\n" }, @@ -125,7 +122,7 @@ module.exports = function(grunt) { "<%= core_files %>", "src/worker/*.js" ], - dest: "<%= build_dir %>/<%= pkg.name %>-worker.js" + dest: "<%= build_dir %>/csslint-worker.js" }, wsh: { src: [ @@ -133,24 +130,12 @@ module.exports = function(grunt) { "src/cli/common.js", "src/cli/wsh.js" ], - 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/" + dest: "<%= build_dir %>/csslint-wsh.js" } }, + includereplace: { - release: { + dist: { options: { // Global variables available in all files globals: { @@ -163,10 +148,11 @@ module.exports = function(grunt) { expand: true, cwd: "<%= build_dir %>/", src: "**/*", - dest: "release/" + dest: "<%= build_dir %>/" }] } }, + jshint: { options: { jshintrc: ".jshintrc" @@ -174,30 +160,35 @@ module.exports = function(grunt) { gruntfile: { src: ["Gruntfile.js", "tasks/*.js"] }, + core: { + src: "src/**/*.js" + }, demo: { src: "demos/*.js" }, - all: { - src: "src/**/*.js" - }, tests: { + options: { + jshintrc: "tests/.jshintrc" + }, src: "tests/**/*.js" } }, + watch: { gruntfile: { files: "<%= jshint.gruntfile.src %>", - tasks: ["jshint"] + tasks: "jshint" }, src: { files: "<%= jshint.all.src %>", - tasks: ["jshint:all"] + tasks: "jshint:core" }, lib_test: { files: "<%= jshint.tests.src %>", - tasks: ["jshint:tests"] + tasks: "jshint:tests" } }, + yuitest: { tests: { src: [ @@ -205,6 +196,7 @@ module.exports = function(grunt) { ] } }, + test_rhino: { tests: { src: [ @@ -223,14 +215,17 @@ 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 - grunt.registerTask("lint", ["jshint"]); + //Alias for + grunt.registerTask("dist", "build"); + 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 84% rename from release/npm/cli.js rename to dist/cli.js index f78d8dba..b68da989 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. +CSSLint v0.10.0 +Copyright (c) 2015 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,12 +22,16 @@ 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 *//* +/* * Encapsulates all of the CLI functionality. The api argument simply * provides environment-specific functionality. */ -/*global CSSLint*/ + +/* global JSON */ +/* exported cli */ + function cli(api){ + "use strict"; var globalOptions = { "help" : { "format" : "", "description" : "Displays this information."}, @@ -38,6 +42,7 @@ function cli(api){ "warnings" : { "format" : "", "description" : "Indicate which rules to include as warnings."}, "ignore" : { "format" : "", "description" : "Indicate which rules to ignore completely."}, "exclude-list": { "format" : "", "description" : "Indicate which files/directories to exclude from being linted."}, + "config" : { "format" : "", "description" : "Reads csslint options from specified file."}, "version" : { "format" : "", "description" : "Outputs the current version number."} }; @@ -195,8 +200,8 @@ function cli(api){ */ function outputHelp(){ var lenToPad = 40, - toPrint = '', - formatString = ''; + toPrint = "", + formatString = ""; api.print([ "\nUsage: csslint-rhino.js [options]* [file|dir]*", @@ -209,14 +214,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"; @@ -272,8 +277,9 @@ function cli(api){ } - function processArguments(args, options) { + function processArguments(args, extend) { var arg = args.shift(), + options = extend || {}, argName, parts, files = []; @@ -306,58 +312,84 @@ 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); } } } - function readConfigFile(options) { - var data = api.readFile(api.getFullPath(".csslintrc")); + function readConfigFile(config) { + var csslintrc = config || ".csslintrc", + data = api.readFile(api.getFullPath(csslintrc)); + return data; + } + + function readConfigData(config) { + var data = readConfigFile(config), + json, + options = {}; if (data) { - options = processArguments(data.split(/[\s\n\r]+/m), options); + 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)); } return options; } - - //----------------------------------------------------------------------------- // Process command line //----------------------------------------------------------------------------- var args = api.args, argCount = args.length, - options = {}; + options, + rcOptions, + cliOptions; - // first look for config file .csslintrc - options = readConfigFile(options); - - // Command line arguments override config file - options = processArguments(args, options); + // Preprocess command line arguments + cliOptions = processArguments(args); - if (options.help || argCount === 0){ + if (cliOptions.help || argCount === 0){ outputHelp(); api.quit(0); } - // Validate options - validateOptions(options); - - if (options.version){ + if (cliOptions.version){ api.print("v" + CSSLint.version); api.quit(0); } - if (options["list-rules"]){ + if (cliOptions["list-rules"]){ printRules(); api.quit(0); } + // Look for config file + rcOptions = readConfigData(cliOptions.config); + + // Command line arguments override config file + options = CSSLint.Util.mix(rcOptions, cliOptions); + + // hot fix for CSSLint.Util.mix current behavior + // https://github.com/CSSLint/csslint/issues/501 + options = rcOptions; + + // Validate options + validateOptions(options); + api.quit(processFiles(options.files,options)); } @@ -365,12 +397,14 @@ function cli(api){ * CSSLint Node.js Command Line Interface */ -/*jshint node:true*/ -/*global cli*/ +/* jshint node:true */ +/* global cli */ +/* exported CSSLint */ +"use strict"; var fs = require("fs"), path = require("path"), - CSSLint = require("./lib/csslint-node").CSSLint; + CSSLint = require("./csslint-node").CSSLint; cli({ args: process.argv.slice(2), @@ -406,7 +440,7 @@ cli({ var path = stack.concat([file]).join("/"), stat = fs.statSync(path); - if (file[0] == ".") { + if (file[0] === ".") { return; } else if (stat.isFile() && /\.css$/.test(file)){ files.push(path); @@ -438,4 +472,3 @@ cli({ } } }); - diff --git a/release/csslint-node.js b/dist/csslint-node.js similarity index 88% rename from release/csslint-node.js rename to dist/csslint-node.js index f800f974..76973b6f 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. +CSSLint v0.10.0 +Copyright (c) 2015 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,16 +21,20 @@ 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"); +var clone = require('clone'); +var parserlib = require('parserlib'); /** * Main CSSLint object. * @class CSSLint * @static * @extends parserlib.util.EventTarget */ -/*global parserlib, Reporter*/ + +/* global parserlib, clone, Reporter */ +/* exported CSSLint */ + var CSSLint = (function(){ + "use strict"; var rules = [], formatters = [], @@ -195,8 +199,7 @@ var CSSLint = (function(){ */ api.verify = function(text, ruleset){ - var i = 0, - len = rules.length, + var i = 0, reporter, lines, report, @@ -204,13 +207,15 @@ 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(); } if (embeddedRuleset.test(text)){ + //defensively copy so that caller's version does not get modified + ruleset = clone(ruleset); ruleset = applyEmbeddedRuleset(text, ruleset); } @@ -261,7 +266,6 @@ var CSSLint = (function(){ })(); -/*global CSSLint*/ /** * An instance of Report is used to report results of the * verification back to the main API. @@ -272,6 +276,7 @@ var CSSLint = (function(){ * they are errors or warnings. */ function Reporter(lines, ruleset){ + "use strict"; /** * List of messages being reported. @@ -318,6 +323,7 @@ Reporter.prototype = { * @method error */ error: function(message, line, col, rule){ + "use strict"; this.messages.push({ type : "error", line : line, @@ -338,6 +344,7 @@ Reporter.prototype = { * @deprecated Use report instead. */ warn: function(message, line, col, rule){ + "use strict"; this.report(message, line, col, rule); }, @@ -350,8 +357,9 @@ Reporter.prototype = { * @method report */ report: function(message, line, col, rule){ + "use strict"; this.messages.push({ - type : this.ruleset[rule.id] == 2 ? "error" : "warning", + type : this.ruleset[rule.id] === 2 ? "error" : "warning", line : line, col : col, message : message, @@ -369,6 +377,7 @@ Reporter.prototype = { * @method info */ info: function(message, line, col, rule){ + "use strict"; this.messages.push({ type : "info", line : line, @@ -386,6 +395,7 @@ Reporter.prototype = { * @method rollupError */ rollupError: function(message, rule){ + "use strict"; this.messages.push({ type : "error", rollup : true, @@ -401,6 +411,7 @@ Reporter.prototype = { * @method rollupWarn */ rollupWarn: function(message, rule){ + "use strict"; this.messages.push({ type : "warning", rollup : true, @@ -416,6 +427,7 @@ Reporter.prototype = { * @method stat */ stat: function(name, value){ + "use strict"; this.stats[name] = value; } }; @@ -423,8 +435,6 @@ Reporter.prototype = { //expose for testing purposes CSSLint._Reporter = Reporter; -/*global CSSLint*/ - /* * Utility functions that make life easier. */ @@ -438,6 +448,7 @@ CSSLint.Util = { * @return {Object} The receiver */ mix: function(receiver, supplier){ + "use strict"; var prop; for (prop in supplier){ @@ -456,6 +467,7 @@ CSSLint.Util = { * @return {int} The index of the value if found, -1 if not. */ indexOf: function(values, value){ + "use strict"; if (values.indexOf){ return values.indexOf(value); } else { @@ -475,6 +487,7 @@ CSSLint.Util = { * @return {void} */ forEach: function(values, func) { + "use strict"; if (values.forEach){ return values.forEach(func); } else { @@ -484,10 +497,11 @@ CSSLint.Util = { } } }; -/*global CSSLint*/ + /* * Rule: Don't use adjoining classes (.foo.bar). */ + CSSLint.addRule({ //rule information @@ -498,6 +512,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this; parser.addListener("startrule", function(event){ var selectors = event.selectors, @@ -511,11 +526,11 @@ CSSLint.addRule({ selector = selectors[i]; for (j=0; j < selector.parts.length; j++){ part = selector.parts[j]; - if (part.type == parser.SELECTOR_PART_TYPE){ + 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"){ + if (modifier.type === "class"){ classCount++; } if (classCount > 1){ @@ -529,7 +544,6 @@ CSSLint.addRule({ } }); -/*global CSSLint*/ /* * Rule: Don't use width or height when using padding or border. @@ -544,6 +558,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this, widthProperties = { border: 1, @@ -578,7 +593,7 @@ CSSLint.addRule({ 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)){ + 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); } } @@ -590,7 +605,7 @@ CSSLint.addRule({ if (widthProperties.hasOwnProperty(prop) && properties[prop]){ value = properties[prop].value; - if (!(prop == "padding" && value.parts.length === 2 && value.parts[1].value === 0)){ + 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); } } @@ -604,18 +619,19 @@ CSSLint.addRule({ parser.addListener("startpage", startRule); parser.addListener("startpagemargin", startRule); parser.addListener("startkeyframerule", startRule); + parser.addListener("startviewport", startRule); parser.addListener("property", function(event){ var name = event.property.text.toLowerCase(); if (heightProperties[name] || widthProperties[name]){ - if (!/^0\S*$/.test(event.value) && !(name == "border" && event.value == "none")){ + if (!/^0\S*$/.test(event.value) && !(name === "border" && event.value.toString() === "none")){ properties[name] = { line: event.property.line, col: event.property.col, value: event.value }; } } else { if (/^(width|height)/i.test(name) && /^(length|percentage)/.test(event.value.parts[0].type)){ properties[name] = 1; - } else if (name == "box-sizing") { + } else if (name === "box-sizing") { boxSizing = true; } } @@ -627,14 +643,15 @@ CSSLint.addRule({ parser.addListener("endpage", endRule); parser.addListener("endpagemargin", endRule); parser.addListener("endkeyframerule", endRule); + parser.addListener("endviewport", endRule); } }); -/*global CSSLint*/ /* * Rule: box-sizing doesn't work in IE6 and IE7. */ + CSSLint.addRule({ //rule information @@ -646,23 +663,25 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this; parser.addListener("property", function(event){ var name = event.property.text.toLowerCase(); - if (name == "box-sizing"){ + 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 @@ -673,15 +692,15 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; 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 +718,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 +734,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 +743,12 @@ CSSLint.addRule({ }); } }); + /* * Rule: Include all compatible vendor prefixes to reach a wider * range of users. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -739,6 +759,7 @@ CSSLint.addRule({ //initialization init: function (parser, reporter) { + "use strict"; var rule = this, compatiblePrefixes, properties, @@ -816,9 +837,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 +854,7 @@ CSSLint.addRule({ inKeyFrame = event.prefix || true; }); - parser.addListener("endkeyframes", function (event) { + parser.addListener("endkeyframes", function () { inKeyFrame = false; }); @@ -842,14 +863,14 @@ CSSLint.addRule({ 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" || + if (!inKeyFrame || typeof inKeyFrame !== "string" || name.text.indexOf("-" + inKeyFrame + "-") !== 0) { properties.push(name); } } }); - parser.addListener("endrule", function (event) { + parser.addListener("endrule", function () { if (!properties.length) { return; } @@ -899,7 +920,7 @@ CSSLint.addRule({ 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(", "); + 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); } } @@ -910,6 +931,7 @@ CSSLint.addRule({ }); } }); + /* * Rule: Certain properties don't play well with certain display values. * - float should not be used with inline-block @@ -917,7 +939,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 @@ -928,6 +950,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this; var propertiesToCheck = { @@ -951,7 +974,7 @@ CSSLint.addRule({ function reportProperty(name, display, msg){ if (properties[name]){ - if (typeof propertiesToCheck[name] != "string" || properties[name].value.toLowerCase() != propertiesToCheck[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); } } @@ -1009,6 +1032,7 @@ CSSLint.addRule({ parser.addListener("startkeyframerule", startRule); parser.addListener("startpagemargin", startRule); parser.addListener("startpage", startRule); + parser.addListener("startviewport", startRule); parser.addListener("property", function(event){ var name = event.property.text.toLowerCase(); @@ -1023,14 +1047,16 @@ CSSLint.addRule({ parser.addListener("endkeyframerule", endRule); parser.addListener("endpagemargin", endRule); parser.addListener("endpage", endRule); + parser.addListener("endviewport", endRule); } }); + /* * Rule: Disallow duplicate background-images (using url). */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1041,6 +1067,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this, stack = {}; @@ -1051,8 +1078,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 +1091,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 @@ -1079,11 +1107,12 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this, properties, lastProperty; - function startRule(event){ + function startRule(){ properties = {}; } @@ -1092,12 +1121,13 @@ CSSLint.addRule({ parser.addListener("startpage", startRule); parser.addListener("startpagemargin", startRule); parser.addListener("startkeyframerule", startRule); + parser.addListener("startviewport", startRule); parser.addListener("property", function(event){ var property = event.property, name = property.text.toLowerCase(); - if (properties[name] && (lastProperty != name || properties[name] == event.value.text)){ + if (properties[name] && (lastProperty !== name || properties[name] === event.value.text)){ reporter.report("Duplicate property '" + event.property + "' found.", event.line, event.col, rule); } @@ -1110,10 +1140,11 @@ CSSLint.addRule({ } }); + /* * Rule: Style rules without any properties defined should be removed. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1124,6 +1155,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this, count = 0; @@ -1144,10 +1176,11 @@ CSSLint.addRule({ } }); + /* * Rule: There should be no syntax errors. (Duh.) */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1158,6 +1191,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this; parser.addListener("error", function(event){ @@ -1168,7 +1202,6 @@ CSSLint.addRule({ }); -/*global CSSLint*/ CSSLint.addRule({ //rule information @@ -1179,6 +1212,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this, lastProperty, propertiesToCheck = { @@ -1198,7 +1232,7 @@ CSSLint.addRule({ }, properties; - function startRule(event){ + function startRule(){ properties = {}; lastProperty = null; } @@ -1208,6 +1242,7 @@ CSSLint.addRule({ parser.addListener("startpage", startRule); parser.addListener("startpagemargin", startRule); parser.addListener("startkeyframerule", startRule); + parser.addListener("startviewport", startRule); parser.addListener("property", function(event){ var property = event.property, @@ -1219,14 +1254,14 @@ CSSLint.addRule({ if(propertiesToCheck[name]){ while(i < len){ - if (parts[i].type == "color"){ + 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")){ + 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 { @@ -1244,11 +1279,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 @@ -1259,13 +1295,14 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this; var count = 0; //count how many times "float" is used parser.addListener("property", function(event){ - if (event.property.text.toLowerCase() == "float" && - event.value.text.toLowerCase() != "none"){ + if (event.property.text.toLowerCase() === "float" && + event.value.text.toLowerCase() !== "none"){ count++; } }); @@ -1280,10 +1317,11 @@ CSSLint.addRule({ } }); + /* * Rule: Avoid too many @font-face declarations in the same stylesheet. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1294,6 +1332,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this, count = 0; @@ -1310,11 +1349,11 @@ CSSLint.addRule({ } }); + /* * Rule: You shouldn't need more than 9 font-size declarations. */ -/*global CSSLint*/ CSSLint.addRule({ //rule information @@ -1325,12 +1364,13 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this, count = 0; //check for use of "font-size" parser.addListener("property", function(event){ - if (event.property == "font-size"){ + if (event.property.toString() === "font-size"){ count++; } }); @@ -1345,10 +1385,11 @@ CSSLint.addRule({ } }); + /* * Rule: When using a vendor-prefixed gradient, make sure to use them all. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1359,6 +1400,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this, gradients; @@ -1413,7 +1455,7 @@ CSSLint.addRule({ /* * Rule: Don't use IDs for selectors. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1424,6 +1466,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this; parser.addListener("startrule", function(event){ var selectors = event.selectors, @@ -1439,17 +1482,17 @@ CSSLint.addRule({ for (j=0; j < selector.parts.length; j++){ part = selector.parts[j]; - if (part.type == parser.SELECTOR_PART_TYPE){ + if (part.type === parser.SELECTOR_PART_TYPE){ for (k=0; k < part.modifiers.length; k++){ modifier = part.modifiers[k]; - if (modifier.type == "id"){ + if (modifier.type === "id"){ idCount++; } } } } - if (idCount == 1){ + 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); @@ -1460,10 +1503,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use @import, use instead. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1474,6 +1518,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this; parser.addListener("import", function(event){ @@ -1483,12 +1528,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 @@ -1499,6 +1545,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this, count = 0; @@ -1520,11 +1567,12 @@ CSSLint.addRule({ } }); + /* * Rule: Properties should be known (listed in CSS3 specification) or * be a vendor-prefixed property. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1535,10 +1583,10 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; 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 +1597,67 @@ 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){ + "use strict"; + var rule = this, + properties; + + var startRule = function () { + properties = []; + }; + + var endRule = function(event){ + var currentProperties = properties.join(","), + expectedProperties = properties.sort().join(","); + + if (currentProperties !== expectedProperties){ + reporter.report("Rule doesn't have all its properties in alphabetical ordered.", event.line, event.col, rule); + } + }; + + parser.addListener("startrule", startRule); + parser.addListener("startfontface", startRule); + parser.addListener("startpage", startRule); + parser.addListener("startpagemargin", startRule); + parser.addListener("startkeyframerule", startRule); + parser.addListener("startviewport", startRule); + + parser.addListener("property", function(event){ + var name = event.property.text, + lowerCasePrefixLessName = name.toLowerCase().replace(/^-.*?-/, ""); + + properties.push(lowerCasePrefixLessName); + }); + + parser.addListener("endrule", endRule); + parser.addListener("endfontface", endRule); + parser.addListener("endpage", endRule); + parser.addListener("endpagemargin", endRule); + parser.addListener("endkeyframerule", endRule); + parser.addListener("endviewport", endRule); + } + +}); + /* * Rule: outline: none or outline: 0 should only be used in a :focus rule * and only if there are other properties in the same rule. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1565,6 +1669,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this, lastRule; @@ -1582,12 +1687,12 @@ CSSLint.addRule({ } } - function endRule(event){ + function endRule(){ if (lastRule){ if (lastRule.outline){ - if (lastRule.selectors.toString().toLowerCase().indexOf(":focus") == -1){ + 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) { + } else if (lastRule.propCount === 1) { reporter.report("Outlines shouldn't be hidden unless other visual changes are made.", lastRule.line, lastRule.col, rule); } } @@ -1599,6 +1704,7 @@ CSSLint.addRule({ parser.addListener("startpage", startRule); parser.addListener("startpagemargin", startRule); parser.addListener("startkeyframerule", startRule); + parser.addListener("startviewport", startRule); parser.addListener("property", function(event){ var name = event.property.text.toLowerCase(), @@ -1606,7 +1712,7 @@ CSSLint.addRule({ if (lastRule){ lastRule.propCount++; - if (name == "outline" && (value == "none" || value == "0")){ + if (name === "outline" && (value.toString() === "none" || value.toString() === "0")){ lastRule.outline = true; } } @@ -1618,14 +1724,16 @@ CSSLint.addRule({ parser.addListener("endpage", endRule); parser.addListener("endpagemargin", endRule); parser.addListener("endkeyframerule", endRule); + parser.addListener("endviewport", endRule); } }); + /* * Rule: Don't use classes or IDs with elements (a.foo or a#foo). */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1636,6 +1744,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this, classes = {}; @@ -1651,12 +1760,12 @@ CSSLint.addRule({ for (j=0; j < selector.parts.length; j++){ part = selector.parts[j]; - if (part.type == parser.SELECTOR_PART_TYPE){ + 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"){ + 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"){ + } else if (modifier.type === "class"){ if (!classes[modifier]){ classes[modifier] = []; @@ -1676,7 +1785,7 @@ CSSLint.addRule({ if (classes.hasOwnProperty(prop)){ //one use means that this is overqualified - if (classes[prop].length == 1 && classes[prop][0].part.elementName){ + 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); } } @@ -1685,10 +1794,11 @@ CSSLint.addRule({ } }); + /* * Rule: Headings (h1-h6) should not be qualified (namespaced). */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1699,6 +1809,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this; parser.addListener("startrule", function(event){ @@ -1712,7 +1823,7 @@ CSSLint.addRule({ for (j=0; j < selector.parts.length; j++){ part = selector.parts[j]; - if (part.type == parser.SELECTOR_PART_TYPE){ + 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); } @@ -1723,10 +1834,11 @@ CSSLint.addRule({ } }); + /* * Rule: Selectors that look like regular expressions are slow and should be avoided. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1737,6 +1849,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this; parser.addListener("startrule", function(event){ @@ -1750,10 +1863,10 @@ CSSLint.addRule({ selector = selectors[i]; for (j=0; j < selector.parts.length; j++){ part = selector.parts[j]; - if (part.type == parser.SELECTOR_PART_TYPE){ + if (part.type === parser.SELECTOR_PART_TYPE){ for (k=0; k < part.modifiers.length; k++){ modifier = part.modifiers[k]; - if (modifier.type == "attribute"){ + if (modifier.type === "attribute"){ if (/([\~\|\^\$\*]=)/.test(modifier)){ reporter.report("Attribute selectors with " + RegExp.$1 + " are slow!", modifier.line, modifier.col, rule); } @@ -1767,10 +1880,11 @@ CSSLint.addRule({ } }); + /* * Rule: Total number of rules should not exceed x. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1781,8 +1895,8 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ - var rule = this, - count = 0; + "use strict"; + var count = 0; //count each rule parser.addListener("startrule", function(){ @@ -1795,10 +1909,11 @@ CSSLint.addRule({ } }); + /* * Rule: Warn people with approaching the IE 4095 limit */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1809,9 +1924,10 @@ CSSLint.addRule({ //initialization init: function(parser, reporter) { + "use strict"; var rule = this, count = 0; - parser.addListener('startrule', function(event) { + parser.addListener("startrule", function(event) { count += event.selectors.length; }); @@ -1827,7 +1943,7 @@ CSSLint.addRule({ /* * Rule: Warn people past the IE 4095 limit */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1838,9 +1954,10 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this, count = 0; - parser.addListener('startrule',function(event) { + parser.addListener("startrule", function(event) { count += event.selectors.length; }); @@ -1852,11 +1969,57 @@ 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) { + "use strict"; + var rule = this; + + function startRule(event) { + var i, len, selector, p, n, pLen, part, part2, type, currentLine, nextLine, + selectors = event.selectors; + + for (i = 0, len = selectors.length; i < len; i++) { + selector = selectors[i]; + for (p = 0, pLen = selector.parts.length; p < pLen; p++) { + for (n = p + 1; n < pLen; n++) { + part = selector.parts[p]; + part2 = selector.parts[n]; + type = part.type; + currentLine = part.line; + nextLine = part2.line; + + if (type === "descendant" && nextLine > currentLine) { + reporter.report("newline character found in selector (forgot a comma?)", currentLine, selectors[i].parts[0].col, rule); + } + } + } + + } + } + + parser.addListener("startrule", startRule); + + } +}); + /* * Rule: Use shorthand properties where possible. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1867,6 +2030,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this, prop, i, len, propertiesToCheck = {}, @@ -1895,7 +2059,7 @@ CSSLint.addRule({ } } - function startRule(event){ + function startRule(){ properties = {}; } @@ -1913,7 +2077,7 @@ CSSLint.addRule({ total += properties[mapping[prop][i]] ? 1 : 0; } - if (total == mapping[prop].length){ + if (total === mapping[prop].length){ reporter.report("The properties " + mapping[prop].join(", ") + " can be replaced by " + prop + ".", event.line, event.col, rule); } } @@ -1925,8 +2089,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 +2102,12 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use properties with a star prefix. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -1954,23 +2118,25 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this; //check if property name starts with "*" parser.addListener("property", function(event){ var property = event.property; - if (property.hack == "*") { + 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 @@ -1981,19 +2147,20 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this, textIndent, direction; - function startRule(event){ + function startRule(){ textIndent = false; direction = "inherit"; } //event handler for end of rules - function endRule(event){ - if (textIndent && direction != "ltr"){ + 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); } } @@ -2006,9 +2173,9 @@ CSSLint.addRule({ var name = event.property.toString().toLowerCase(), value = event.value; - if (name == "text-indent" && value.parts[0].value < -99){ + if (name === "text-indent" && value.parts[0].value < -99){ textIndent = event.property; - } else if (name == "direction" && value == "ltr"){ + } else if (name === "direction" && value.toString() === "ltr"){ direction = "ltr"; } }); @@ -2019,11 +2186,12 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use properties with a underscore prefix. * */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -2034,22 +2202,24 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this; //check if property name starts with "_" parser.addListener("property", function(event){ var property = event.property; - if (property.hack == "_") { + 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 @@ -2060,9 +2230,10 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this; - var headings = { + var headings = { h1: 0, h2: 0, h3: 0, @@ -2085,7 +2256,7 @@ CSSLint.addRule({ 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"){ + if (part.modifiers[j].type === "pseudo"){ pseudo = true; break; } @@ -2101,7 +2272,7 @@ CSSLint.addRule({ } }); - parser.addListener("endstylesheet", function(event){ + parser.addListener("endstylesheet", function(){ var prop, messages = []; @@ -2120,10 +2291,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use universal selector because it's slow. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -2134,20 +2306,20 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this; parser.addListener("startrule", function(event){ var selectors = event.selectors, selector, part, - modifier, - i, j, k; + i; for (i=0; i < selectors.length; i++){ selector = selectors[i]; part = selector.parts[selector.parts.length-1]; - if (part.elementName == "*"){ + if (part.elementName === "*"){ reporter.report(rule.desc, part.line, part.col, rule); } } @@ -2155,10 +2327,11 @@ CSSLint.addRule({ } }); + /* * Rule: Don't use unqualified attribute selectors because they're just like universal selectors. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -2169,6 +2342,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this; parser.addListener("startrule", function(event){ @@ -2177,16 +2351,16 @@ CSSLint.addRule({ selector, part, modifier, - i, j, k; + i, k; for (i=0; i < selectors.length; i++){ selector = selectors[i]; part = selector.parts[selector.parts.length-1]; - if (part.type == parser.SELECTOR_PART_TYPE){ + 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 == "*")){ + if (modifier.type === "attribute" && (!part.elementName || part.elementName === "*")){ reporter.report(rule.desc, part.line, part.col, rule); } } @@ -2197,11 +2371,12 @@ CSSLint.addRule({ } }); + /* * Rule: When using a vendor-prefixed property, make sure to * include the standard one. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -2212,6 +2387,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this, properties, num, @@ -2272,24 +2448,20 @@ 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 function startRule(){ properties = {}; - num=1; + num = 1; } //event handler for end of rules - function endRule(event){ + function endRule(){ var prop, - i, len, - standard, + i, + len, needed, actual, needsStandard = []; @@ -2321,6 +2493,7 @@ CSSLint.addRule({ parser.addListener("startpage", startRule); parser.addListener("startpagemargin", startRule); parser.addListener("startkeyframerule", startRule); + parser.addListener("startviewport", startRule); parser.addListener("property", function(event){ var name = event.property.text.toLowerCase(); @@ -2337,13 +2510,15 @@ CSSLint.addRule({ parser.addListener("endpage", endRule); parser.addListener("endpagemargin", endRule); parser.addListener("endkeyframerule", endRule); + parser.addListener("endviewport", endRule); } }); + /* * Rule: You don't need to specify units when a value is 0. */ -/*global CSSLint*/ + CSSLint.addRule({ //rule information @@ -2354,6 +2529,7 @@ CSSLint.addRule({ //initialization init: function(parser, reporter){ + "use strict"; var rule = this; //count how many times "float" is used @@ -2363,7 +2539,7 @@ CSSLint.addRule({ len = parts.length; while(i < len){ - if ((parts[i].units || parts[i].type == "percentage") && parts[i].value === 0 && parts[i].type != "time"){ + 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++; @@ -2374,8 +2550,9 @@ CSSLint.addRule({ } }); -/*global CSSLint*/ + (function() { + "use strict"; /** * Replace special characters before write to output. @@ -2446,7 +2623,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 +2635,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 +2660,7 @@ CSSLint.addRule({ }); }()); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "compact", @@ -2494,6 +2671,7 @@ CSSLint.addFormatter({ * @return {String} to prepend before all results */ startFormat: function() { + "use strict"; return ""; }, @@ -2502,6 +2680,7 @@ CSSLint.addFormatter({ * @return {String} to append after all results */ endFormat: function() { + "use strict"; return ""; }, @@ -2513,6 +2692,7 @@ CSSLint.addFormatter({ * @return {String} output for results */ formatResults: function(results, filename, options) { + "use strict"; var messages = results.messages, output = ""; options = options || {}; @@ -2527,22 +2707,22 @@ CSSLint.addFormatter({ }; if (messages.length === 0) { - return options.quiet ? "" : filename + ": Lint Free!"; + 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", @@ -2553,6 +2733,7 @@ CSSLint.addFormatter({ * @return {String} to prepend before all results */ startFormat: function(){ + "use strict"; return ""; }, @@ -2561,6 +2742,7 @@ CSSLint.addFormatter({ * @return {String} to append after all results */ endFormat: function(){ + "use strict"; return ""; }, @@ -2571,7 +2753,8 @@ 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*/) { + "use strict"; var messages = results.messages, output = []; @@ -2596,7 +2779,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 +2793,7 @@ CSSLint.addFormatter({ return output.join(""); } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "junit-xml", @@ -2621,6 +2804,7 @@ CSSLint.addFormatter({ * @return {String} to prepend before all results */ startFormat: function(){ + "use strict"; return ""; }, @@ -2629,6 +2813,7 @@ CSSLint.addFormatter({ * @return {String} to append after all results */ endFormat: function() { + "use strict"; return ""; }, @@ -2639,13 +2824,14 @@ 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*/) { + "use strict"; var messages = results.messages, output = [], tests = { - 'error': 0, - 'failure': 0 + "error": 0, + "failure": 0 }; /** @@ -2656,10 +2842,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 +2871,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 +2883,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 +2901,7 @@ CSSLint.addFormatter({ } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "lint-xml", @@ -2726,6 +2912,7 @@ CSSLint.addFormatter({ * @return {String} to prepend before all results */ startFormat: function(){ + "use strict"; return ""; }, @@ -2734,6 +2921,7 @@ CSSLint.addFormatter({ * @return {String} to append after all results */ endFormat: function(){ + "use strict"; return ""; }, @@ -2744,7 +2932,8 @@ 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*/) { + "use strict"; var messages = results.messages, output = []; @@ -2770,7 +2959,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 +2973,7 @@ CSSLint.addFormatter({ return output.join(""); } }); -/*global CSSLint*/ + CSSLint.addFormatter({ //format information id: "text", @@ -2795,6 +2984,7 @@ CSSLint.addFormatter({ * @return {String} to prepend before all results */ startFormat: function() { + "use strict"; return ""; }, @@ -2803,6 +2993,7 @@ CSSLint.addFormatter({ * @return {String} to append after all results */ endFormat: function() { + "use strict"; return ""; }, @@ -2814,6 +3005,7 @@ CSSLint.addFormatter({ * @return {String} output for results */ formatResults: function(results, filename, options) { + "use strict"; var messages = results.messages, output = ""; options = options || {}; @@ -2822,7 +3014,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 +3047,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 91% rename from release/csslint-rhino.js rename to dist/csslint-rhino.js index 587d438b..7ca4d140 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. +CSSLint v0.10.0 +Copyright (c) 2015 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,8 +21,10 @@ 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 exports = exports || {}; + +var module = module || {}, + exports = exports || {}; + var CSSLint = (function(){ /*! Parser-Lib @@ -47,11 +49,10 @@ 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.5, Build time: 7-May-2014 03:37:38 */ var parserlib = {}; (function(){ - /** * A generic base to inherit from for any object * that needs event handling. @@ -922,8 +923,6 @@ TokenStreamBase.prototype = { }; - - parserlib.util = { StringReader: StringReader, SyntaxError : SyntaxError, @@ -932,8 +931,6 @@ EventTarget : EventTarget, TokenStreamBase : TokenStreamBase }; })(); - - /* Parser-Lib Copyright (c) 2009-2011 Nicholas C. Zakas. All rights reserved. @@ -957,7 +954,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.5, Build time: 7-May-2014 03:37:38 */ (function(){ var EventTarget = parserlib.util.EventTarget, TokenStreamBase = parserlib.util.TokenStreamBase, @@ -965,7 +962,6 @@ StringReader = parserlib.util.StringReader, SyntaxError = parserlib.util.SyntaxError, SyntaxUnit = parserlib.util.SyntaxUnit; - var Colors = { aliceblue :"#f0f8ff", antiquewhite :"#faebd7", @@ -992,6 +988,7 @@ var Colors = { darkcyan :"#008b8b", darkgoldenrod :"#b8860b", darkgray :"#a9a9a9", + darkgrey :"#a9a9a9", darkgreen :"#006400", darkkhaki :"#bdb76b", darkmagenta :"#8b008b", @@ -1003,11 +1000,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 +1017,7 @@ var Colors = { gold :"#ffd700", goldenrod :"#daa520", gray :"#808080", + grey :"#808080", green :"#008000", greenyellow :"#adff2f", honeydew :"#f0fff0", @@ -1035,12 +1035,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 +1095,7 @@ var Colors = { skyblue :"#87ceeb", slateblue :"#6a5acd", slategray :"#708090", + slategrey :"#708090", snow :"#fffafa", springgreen :"#00ff7f", steelblue :"#4682b4", @@ -1118,6 +1121,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.", @@ -1175,7 +1179,6 @@ function Combinator(text, line, col){ Combinator.prototype = new SyntaxUnit(); Combinator.prototype.constructor = Combinator; - /*global SyntaxUnit, Parser*/ /** * Represents a media feature, such as max-width:500. @@ -1208,7 +1211,6 @@ function MediaFeature(name, value){ MediaFeature.prototype = new SyntaxUnit(); MediaFeature.prototype.constructor = MediaFeature; - /*global SyntaxUnit, Parser*/ /** * Represents an individual media query. @@ -1252,7 +1254,6 @@ function MediaQuery(modifier, mediaType, features, line, col){ MediaQuery.prototype = new SyntaxUnit(); MediaQuery.prototype.constructor = MediaQuery; - /*global Tokens, TokenStream, SyntaxError, Properties, Validation, ValidationError, SyntaxUnit, PropertyValue, PropertyValuePart, SelectorPart, SelectorSubPart, Selector, PropertyName, Combinator, MediaFeature, MediaQuery, EventTarget */ @@ -1514,7 +1515,7 @@ Parser.prototype = function(){ tokenStream.mustMatch([Tokens.STRING, Tokens.URI]); //grab the URI value - uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1"); + uri = tokenStream.token().value.replace(/^(?:url\()?["']?([^"']+?)["']?\)?$/, "$1"); this._readWhitespace(); @@ -1619,8 +1620,10 @@ Parser.prototype = function(){ while(true) { if (tokenStream.peek() == Tokens.PAGE_SYM){ this._page(); - } else if (tokenStream.peek() == Tokens.FONT_FACE_SYM){ + } else if (tokenStream.peek() == Tokens.FONT_FACE_SYM){ this._font_face(); + } else if (tokenStream.peek() == Tokens.VIEWPORT_SYM){ + this._viewport(); } else if (!this._ruleset()){ break; } @@ -2812,11 +2815,11 @@ Parser.prototype = function(){ var tokenStream = this._tokenStream, values = [], - //valueParts = [], + //valueParts = [], value = null, operator = null; - value = this._term(); + value = this._term(inFunction); if (value !== null){ values.push(value); @@ -2829,11 +2832,11 @@ 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(); + value = this._term(inFunction); if (value === null){ break; @@ -2843,7 +2846,7 @@ Parser.prototype = function(){ } while(true); } - //cleanup + //cleanup /*if (valueParts.length){ values.push(new PropertyValue(valueParts, valueParts[0].line, valueParts[0].col)); }*/ @@ -2851,7 +2854,7 @@ Parser.prototype = function(){ return values.length > 0 ? new PropertyValue(values, values[0].line, values[0].col) : null; }, - _term: function(){ + _term: function(inFunction){ /* * term @@ -2865,6 +2868,7 @@ Parser.prototype = function(){ var tokenStream = this._tokenStream, unary = null, value = null, + endChar = null, token, line, col; @@ -2885,6 +2889,20 @@ Parser.prototype = function(){ col = tokenStream.token().startCol; } + //see if it's a simple block + } else if (inFunction && tokenStream.match([Tokens.LPAREN, Tokens.LBRACE, Tokens.LBRACKET])){ + + token = tokenStream.token(); + endChar = token.endChar; + value = token.value + this._expr(inFunction).text; + if (unary === null){ + line = tokenStream.token().startLine; + col = tokenStream.token().startCol; + } + tokenStream.mustMatch(Tokens.type(endChar)); + value += endChar; + this._readWhitespace(); + //see if there's a simple match } else if (tokenStream.match([Tokens.NUMBER, Tokens.PERCENTAGE, Tokens.LENGTH, Tokens.ANGLE, Tokens.TIME, @@ -3529,17 +3547,23 @@ nth ['-'|'+']? INTEGER | {O}{D}{D} | {E}{V}{E}{N} ] S* ; */ - /*global Validation, ValidationTypes, ValidationError*/ 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, "animation-delay" : { multi: "