From 6bb93f5d0e56cc0b7918e6cfb4a0a4881cb8e92d Mon Sep 17 00:00:00 2001 From: Oleg Elifantiev Date: Sat, 5 Jan 2013 00:17:32 +0400 Subject: [PATCH 1/5] First steps - checking fonts, no tests --- src/rules/check-fonts.js | 56 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/rules/check-fonts.js diff --git a/src/rules/check-fonts.js b/src/rules/check-fonts.js new file mode 100644 index 00000000..9c1e4c93 --- /dev/null +++ b/src/rules/check-fonts.js @@ -0,0 +1,56 @@ +CSSLint.addRule({ + + //rule information + id: "check-fonts", + name: "Check font alternatives", + desc: "Check font alternatives for Mac OS (OS X, iOS)", + browsers: "All", + + //initialization + init: function(parser, reporter){ + var rule = this, + macMap = { + "Tahoma": "Geneva", + "Lucida Console": "Monaco", + "Lucida Sans Unicode": "Lucida Grande", + "Palatino Linotype": "Palatino", + "Book Antiqua": "Palatino", + "Wingdings": "Zapf Dingbats", + "MS Sans Serif": "Geneva", + "MS Serif": "New York" + }; + + parser.addListener("property", function(event){ + + var property = event.property, + value = event.value, + propertyName = property.text.toLowerCase(), + valueParts = value.parts, + currentRuleFonts = {}, + i, l, value, type, fontName, line, col; + + if(propertyName == "font" || propertyName == "font-family") { + for(i = 0, l = valueParts.length; i < l; i++) { + value = valueParts[i]; + type = value.type; + if(type == 'string' || type == 'identifier') { + fontName = value.text; + currentRuleFonts[fontName] = { line: value.line, col: value.col }; + } + } + + for(i in currentRuleFonts) { + if(currentRuleFonts.hasOwnProperty(i)) { + line = currentRuleFonts[i].line; + col = currentRuleFonts[i].col; + if(i in macMap && currentRuleFonts[macMap[i]] === undefined) + reporter.warn("No MacOS-alternative for font '" + i + "'. " + + "Consider adding '" + macMap[i] + "'.", line, col, rule); + } + } + } + }); + + } + +}); \ No newline at end of file From c5f41072ee848f84675f973e552664d9a190815c Mon Sep 17 00:00:00 2001 From: Oleg Elifantiev Date: Mon, 7 Jan 2013 00:54:43 +0400 Subject: [PATCH 2/5] Lint errors fixed --- src/rules/check-fonts.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rules/check-fonts.js b/src/rules/check-fonts.js index 9c1e4c93..6c246977 100644 --- a/src/rules/check-fonts.js +++ b/src/rules/check-fonts.js @@ -1,3 +1,4 @@ +/*global CSSLint*/ CSSLint.addRule({ //rule information @@ -23,11 +24,10 @@ CSSLint.addRule({ parser.addListener("property", function(event){ var property = event.property, - value = event.value, propertyName = property.text.toLowerCase(), - valueParts = value.parts, + valueParts = event.value.parts, currentRuleFonts = {}, - i, l, value, type, fontName, line, col; + i, l, type, value, fontName, line, col; if(propertyName == "font" || propertyName == "font-family") { for(i = 0, l = valueParts.length; i < l; i++) { @@ -43,9 +43,10 @@ CSSLint.addRule({ if(currentRuleFonts.hasOwnProperty(i)) { line = currentRuleFonts[i].line; col = currentRuleFonts[i].col; - if(i in macMap && currentRuleFonts[macMap[i]] === undefined) + if(i in macMap && currentRuleFonts[macMap[i]] === undefined) { reporter.warn("No MacOS-alternative for font '" + i + "'. " + "Consider adding '" + macMap[i] + "'.", line, col, rule); + } } } } From 7f284c0eedfb4f35e58872de290ca97637a6b459 Mon Sep 17 00:00:00 2001 From: Oleg Elifantiev Date: Wed, 9 Jan 2013 22:34:57 +0400 Subject: [PATCH 3/5] Tests added, some fixes in rule --- src/rules/check-fonts.js | 30 +++++++---- tests/rules/mac-fonts-check.js | 94 ++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 9 deletions(-) create mode 100644 tests/rules/mac-fonts-check.js diff --git a/src/rules/check-fonts.js b/src/rules/check-fonts.js index 6c246977..06e64150 100644 --- a/src/rules/check-fonts.js +++ b/src/rules/check-fonts.js @@ -19,7 +19,17 @@ CSSLint.addRule({ "Wingdings": "Zapf Dingbats", "MS Sans Serif": "Geneva", "MS Serif": "New York" - }; + }, + fontFaceRule = false; + + // Disable checking inside @font-face block + parser.addListener("startfontface", function(){ + fontFaceRule = true; + }); + + parser.addListener("endfontface", function(){ + fontFaceRule = false; + }); parser.addListener("property", function(event){ @@ -27,23 +37,25 @@ CSSLint.addRule({ propertyName = property.text.toLowerCase(), valueParts = event.value.parts, currentRuleFonts = {}, - i, l, type, value, fontName, line, col; + i, l, value, line, col; + + if(fontFaceRule) { + return; + } if(propertyName == "font" || propertyName == "font-family") { for(i = 0, l = valueParts.length; i < l; i++) { value = valueParts[i]; - type = value.type; - if(type == 'string' || type == 'identifier') { - fontName = value.text; - currentRuleFonts[fontName] = { line: value.line, col: value.col }; + if(value.type == 'identifier') { + currentRuleFonts[value.text] = { line: value.line, col: value.col }; } } - +console.log(currentRuleFonts); for(i in currentRuleFonts) { if(currentRuleFonts.hasOwnProperty(i)) { - line = currentRuleFonts[i].line; - col = currentRuleFonts[i].col; if(i in macMap && currentRuleFonts[macMap[i]] === undefined) { + line = currentRuleFonts[i].line; + col = currentRuleFonts[i].col; reporter.warn("No MacOS-alternative for font '" + i + "'. " + "Consider adding '" + macMap[i] + "'.", line, col, rule); } diff --git a/tests/rules/mac-fonts-check.js b/tests/rules/mac-fonts-check.js new file mode 100644 index 00000000..fee0d02e --- /dev/null +++ b/tests/rules/mac-fonts-check.js @@ -0,0 +1,94 @@ +(function(){ + + /*global YUITest, CSSLint*/ + var + Assert = YUITest.Assert, + testSpec = { "check-fonts": 1 }; + + function mkErrorMessage(font, alternative) { + return "No MacOS-alternative for font '" + font + "'. Consider adding '" + alternative + "'."; + } + + YUITest.TestRunner.add(new YUITest.TestCase({ + + name: "Mac alternative font checker - font property", + + "No erros when no Mac alternative needed": function(){ + var result = CSSLint.verify(".c { font-family: Arial }", testSpec); + + Assert.areEqual(0, result.messages.length); + }, + + "No erros when Mac alternative is specified": function(){ + var result = CSSLint.verify(".c { font: 14px bold Tahoma, Geneva }", testSpec); + + Assert.areEqual(0, result.messages.length); + }, + + "No alternative detection 0": function(){ + var result = CSSLint.verify(".c { font: 14px bold Lucida Console }", testSpec); + + Assert.areEqual(1, result.messages.length); + Assert.areEqual(mkErrorMessage("Lucida Console", "Monaco"), result.messages[0].message); + }, + + "No alternative detection 1": function(){ + var result = CSSLint.verify(".c { font: 14px bold MS Serif }", testSpec); + + Assert.areEqual(1, result.messages.length); + Assert.areEqual(mkErrorMessage("MS Serif", "New York"), result.messages[0].message); + }, + + "'font: inherit' causes no error": function() { + var result = CSSLint.verify(".c { font: inherit }", testSpec); + + Assert.areEqual(0, result.messages.length); + } + + })); + + YUITest.TestRunner.add(new YUITest.TestCase({ + + name: "Mac alternative font checker - font-family property", + + "No erros when no Mac alternative needed": function(){ + var result = CSSLint.verify(".c { font-family: Arial }", testSpec); + + Assert.areEqual(0, result.messages.length); + }, + + "No erros when Mac alternative is specified": function(){ + var result = CSSLint.verify(".c { font-family: Tahoma, Geneva }", testSpec); + + Assert.areEqual(0, result.messages.length); + }, + + "No alternative detection 0": function(){ + var result = CSSLint.verify(".c { font-family: Lucida Console }", testSpec); + + Assert.areEqual(1, result.messages.length); + Assert.areEqual(mkErrorMessage("Lucida Console", "Monaco"), result.messages[0].message); + }, + + "No alternative detection 1": function(){ + var result = CSSLint.verify(".c { font-family: MS Serif }", testSpec); + + Assert.areEqual(1, result.messages.length); + Assert.areEqual(mkErrorMessage("MS Serif", "New York"), result.messages[0].message); + }, + + "No detection inside @font-face rule 1": function() { + var result = CSSLint.verify("@font-face { font-family: MyFont; }", testSpec); + + Assert.areEqual(0, result.messages.length); + }, + + "No detection inside @font-face rule 2": function() { + var result = CSSLint.verify("@font-face { font-family: Tahoma; }", testSpec); + + Assert.areEqual(0, result.messages.length); + } + + })); + +})(); \ No newline at end of file From af2c8bf4e6b649fe89cac3a085b2e4c861d1f880 Mon Sep 17 00:00:00 2001 From: Oleg Elifantiev Date: Thu, 10 Jan 2013 23:59:31 +0400 Subject: [PATCH 4/5] Rule renamed, tests added and rule fixed to conform --- ...k-fonts.js => check-fonts-alternatives.js} | 46 +++++- tests/rules/check-fonts-alternatives.js | 138 ++++++++++++++++++ tests/rules/mac-fonts-check.js | 94 ------------ 3 files changed, 177 insertions(+), 101 deletions(-) rename src/rules/{check-fonts.js => check-fonts-alternatives.js} (53%) create mode 100644 tests/rules/check-fonts-alternatives.js delete mode 100644 tests/rules/mac-fonts-check.js diff --git a/src/rules/check-fonts.js b/src/rules/check-fonts-alternatives.js similarity index 53% rename from src/rules/check-fonts.js rename to src/rules/check-fonts-alternatives.js index 06e64150..3f815640 100644 --- a/src/rules/check-fonts.js +++ b/src/rules/check-fonts-alternatives.js @@ -2,7 +2,7 @@ CSSLint.addRule({ //rule information - id: "check-fonts", + id: "check-fonts-alternatives", name: "Check font alternatives", desc: "Check font alternatives for Mac OS (OS X, iOS)", browsers: "All", @@ -20,6 +20,20 @@ CSSLint.addRule({ "MS Sans Serif": "Geneva", "MS Serif": "New York" }, + skipIdentifiers = { + // font-style related + "normal": 1, + "italic": 2, + "oblique": 3, + // any + "inherit": 4, + // font-variant related + "small-caps": 5, + // font-weight related + "bold": 6, + "bolder": 7, + "lighter": 8 + }, fontFaceRule = false; // Disable checking inside @font-face block @@ -33,12 +47,19 @@ CSSLint.addRule({ parser.addListener("property", function(event){ + // font: [font-style||font-variant||font-weight] font-size[/line-height] font-family | inherit + // font-style: normal | italic | oblique | inherit + // font-variant: normal | small-caps | inherit + // font-weight: bold|bolder|lighter|normal|100|200|300|400|500|600|700|800|900 + var property = event.property, propertyName = property.text.toLowerCase(), valueParts = event.value.parts, currentRuleFonts = {}, - i, l, value, line, col; + idStack = [], + i, l, value; + // Skip any detection if we inside @font-face block if(fontFaceRule) { return; } @@ -47,17 +68,28 @@ CSSLint.addRule({ for(i = 0, l = valueParts.length; i < l; i++) { value = valueParts[i]; if(value.type == 'identifier') { - currentRuleFonts[value.text] = { line: value.line, col: value.col }; + // skip not-font-family-related identifiers + if(value.text in skipIdentifiers) { + continue; + } + idStack.push(value.text); + } else if(value.type == 'operator' && value.text == ',') { + currentRuleFonts[idStack.join(' ')] = { line: value.line, col: value.col }; + idStack = []; } } -console.log(currentRuleFonts); + if(idStack.length > 0) { + currentRuleFonts[idStack.join(' ')] = { line: value.line, col: value.col }; + } + for(i in currentRuleFonts) { if(currentRuleFonts.hasOwnProperty(i)) { if(i in macMap && currentRuleFonts[macMap[i]] === undefined) { - line = currentRuleFonts[i].line; - col = currentRuleFonts[i].col; reporter.warn("No MacOS-alternative for font '" + i + "'. " + - "Consider adding '" + macMap[i] + "'.", line, col, rule); + "Consider adding '" + macMap[i] + "'.", + currentRuleFonts[i].line, + currentRuleFonts[i].col, + rule); } } } diff --git a/tests/rules/check-fonts-alternatives.js b/tests/rules/check-fonts-alternatives.js new file mode 100644 index 00000000..e57b77d1 --- /dev/null +++ b/tests/rules/check-fonts-alternatives.js @@ -0,0 +1,138 @@ +(function(){ + + /*global YUITest, CSSLint*/ + var + Assert = YUITest.Assert, + testSpec = { "check-fonts-alternatives": 1 }; + + function mkErrorMessage(font, alternative) { + return "No MacOS-alternative for font '" + font + "'. Consider adding '" + alternative + "'."; + } + + YUITest.TestRunner.add(new YUITest.TestCase({ + + name: "Mac alternative font checker - font property", + + "No erros when no Mac alternative needed": function(){ + var result = CSSLint.verify(".c { font-family: Arial }", testSpec); + + Assert.areEqual(0, result.messages.length); + }, + + "No erros when Mac alternative is specified": function(){ + var result = CSSLint.verify(".c { font: 14px Tahoma, Geneva }", testSpec); + + Assert.areEqual(0, result.messages.length); + }, + + "Font size + family form": function(){ + var result = CSSLint.verify(".c { font: 14px Lucida Console }", testSpec); + + Assert.areEqual(1, result.messages.length); + Assert.areEqual(mkErrorMessage("Lucida Console", "Monaco"), result.messages[0].message); + }, + + "Font variant, size, family form": function(){ + var result = CSSLint.verify(".c { font: italic 14px MS Serif }", testSpec); + + Assert.areEqual(1, result.messages.length); + Assert.areEqual(mkErrorMessage("MS Serif", "New York"), result.messages[0].message); + }, + + "Font weight, size, family form": function(){ + var result = CSSLint.verify(".c { font: bold 14px MS Serif }", testSpec); + + Assert.areEqual(1, result.messages.length); + Assert.areEqual(mkErrorMessage("MS Serif", "New York"), result.messages[0].message); + }, + + "Font weight as number, size + family form": function(){ + var result = CSSLint.verify(".c { font: 500 14px MS Serif }", testSpec); + + Assert.areEqual(1, result.messages.length); + Assert.areEqual(mkErrorMessage("MS Serif", "New York"), result.messages[0].message); + }, + + "Font size/height + family form": function(){ + var result = CSSLint.verify(".c { font: 14px/20px MS Serif }", testSpec); + + Assert.areEqual(1, result.messages.length); + Assert.areEqual(mkErrorMessage("MS Serif", "New York"), result.messages[0].message); + }, + + "Font - badly formed rule": function(){ + // This is equal to identifier 'Tahoma Tahoma Tahoma' + var result = CSSLint.verify(".c { font: 14px Tahoma Tahoma Tahoma }", testSpec); + + Assert.areEqual(0, result.messages.length); + }, + + "Multiple font families without an alternative": function(){ + var result = CSSLint.verify(".c { font: 14px MS Serif, Tahoma, Verdana }", testSpec); + + Assert.areEqual(2, result.messages.length); + Assert.areEqual(mkErrorMessage("MS Serif", "New York"), result.messages[0].message); + Assert.areEqual(mkErrorMessage("Tahoma", "Geneva"), result.messages[1].message); + }, + + "Font = inherit": function() { + var result = CSSLint.verify(".c { font: inherit }", testSpec); + + Assert.areEqual(0, result.messages.length); + } + + })); + + YUITest.TestRunner.add(new YUITest.TestCase({ + + name: "Mac alternative font checker - font-family property", + + "No erros when no Mac alternative needed": function(){ + var result = CSSLint.verify(".c { font-family: Arial }", testSpec); + + Assert.areEqual(0, result.messages.length); + }, + + "No erros when Mac alternative is specified": function(){ + var result = CSSLint.verify(".c { font-family: Tahoma, Geneva }", testSpec); + + Assert.areEqual(0, result.messages.length); + }, + + "No alternative specified, font-family without space": function(){ + var result = CSSLint.verify(".c { font-family: Tahoma }", testSpec); + + Assert.areEqual(1, result.messages.length); + Assert.areEqual(mkErrorMessage("Tahoma", "Geneva"), result.messages[0].message); + }, + + "No alternative specified, font-family with space": function(){ + var result = CSSLint.verify(".c { font-family: MS Serif }", testSpec); + + Assert.areEqual(1, result.messages.length); + Assert.areEqual(mkErrorMessage("MS Serif", "New York"), result.messages[0].message); + }, + + "No alternative specified, font-family with space, multiple families": function(){ + var result = CSSLint.verify(".c { font-family: MS Serif, Tahoma, Arial }", testSpec); + + Assert.areEqual(2, result.messages.length); + Assert.areEqual(mkErrorMessage("MS Serif", "New York"), result.messages[0].message); + Assert.areEqual(mkErrorMessage("Tahoma", "Geneva"), result.messages[1].message); + }, + + "No detection inside @font-face rule 1": function() { + var result = CSSLint.verify("@font-face { font-family: MyFont; }", testSpec); + + Assert.areEqual(0, result.messages.length); + }, + + "No detection inside @font-face rule 2": function() { + var result = CSSLint.verify("@font-face { font-family: Tahoma; }", testSpec); + + Assert.areEqual(0, result.messages.length); + } + + })); + +})(); \ No newline at end of file diff --git a/tests/rules/mac-fonts-check.js b/tests/rules/mac-fonts-check.js deleted file mode 100644 index fee0d02e..00000000 --- a/tests/rules/mac-fonts-check.js +++ /dev/null @@ -1,94 +0,0 @@ -(function(){ - - /*global YUITest, CSSLint*/ - var - Assert = YUITest.Assert, - testSpec = { "check-fonts": 1 }; - - function mkErrorMessage(font, alternative) { - return "No MacOS-alternative for font '" + font + "'. Consider adding '" + alternative + "'."; - } - - YUITest.TestRunner.add(new YUITest.TestCase({ - - name: "Mac alternative font checker - font property", - - "No erros when no Mac alternative needed": function(){ - var result = CSSLint.verify(".c { font-family: Arial }", testSpec); - - Assert.areEqual(0, result.messages.length); - }, - - "No erros when Mac alternative is specified": function(){ - var result = CSSLint.verify(".c { font: 14px bold Tahoma, Geneva }", testSpec); - - Assert.areEqual(0, result.messages.length); - }, - - "No alternative detection 0": function(){ - var result = CSSLint.verify(".c { font: 14px bold Lucida Console }", testSpec); - - Assert.areEqual(1, result.messages.length); - Assert.areEqual(mkErrorMessage("Lucida Console", "Monaco"), result.messages[0].message); - }, - - "No alternative detection 1": function(){ - var result = CSSLint.verify(".c { font: 14px bold MS Serif }", testSpec); - - Assert.areEqual(1, result.messages.length); - Assert.areEqual(mkErrorMessage("MS Serif", "New York"), result.messages[0].message); - }, - - "'font: inherit' causes no error": function() { - var result = CSSLint.verify(".c { font: inherit }", testSpec); - - Assert.areEqual(0, result.messages.length); - } - - })); - - YUITest.TestRunner.add(new YUITest.TestCase({ - - name: "Mac alternative font checker - font-family property", - - "No erros when no Mac alternative needed": function(){ - var result = CSSLint.verify(".c { font-family: Arial }", testSpec); - - Assert.areEqual(0, result.messages.length); - }, - - "No erros when Mac alternative is specified": function(){ - var result = CSSLint.verify(".c { font-family: Tahoma, Geneva }", testSpec); - - Assert.areEqual(0, result.messages.length); - }, - - "No alternative detection 0": function(){ - var result = CSSLint.verify(".c { font-family: Lucida Console }", testSpec); - - Assert.areEqual(1, result.messages.length); - Assert.areEqual(mkErrorMessage("Lucida Console", "Monaco"), result.messages[0].message); - }, - - "No alternative detection 1": function(){ - var result = CSSLint.verify(".c { font-family: MS Serif }", testSpec); - - Assert.areEqual(1, result.messages.length); - Assert.areEqual(mkErrorMessage("MS Serif", "New York"), result.messages[0].message); - }, - - "No detection inside @font-face rule 1": function() { - var result = CSSLint.verify("@font-face { font-family: MyFont; }", testSpec); - - Assert.areEqual(0, result.messages.length); - }, - - "No detection inside @font-face rule 2": function() { - var result = CSSLint.verify("@font-face { font-family: Tahoma; }", testSpec); - - Assert.areEqual(0, result.messages.length); - } - - })); - -})(); \ No newline at end of file From ddb440f482b9bc2e83de595c45d9b9620df4b88f Mon Sep 17 00:00:00 2001 From: Oleg Elifantiev Date: Fri, 11 Jan 2013 00:55:30 +0400 Subject: [PATCH 5/5] Test for font-size identifiers, rule fixed also --- src/rules/check-fonts-alternatives.js | 15 ++++++++++++++- tests/rules/check-fonts-alternatives.js | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/rules/check-fonts-alternatives.js b/src/rules/check-fonts-alternatives.js index 3f815640..8dc3ee0a 100644 --- a/src/rules/check-fonts-alternatives.js +++ b/src/rules/check-fonts-alternatives.js @@ -32,7 +32,17 @@ CSSLint.addRule({ // font-weight related "bold": 6, "bolder": 7, - "lighter": 8 + "lighter": 8, + // font-size related + "xx-small": 9, + "x-small": 10, + "small": 11, + "medium": 12, + "large": 13, + "x-large": 14, + "xx-large": 15, + "larger": 16, + "smaller": 17 }, fontFaceRule = false; @@ -51,6 +61,9 @@ CSSLint.addRule({ // font-style: normal | italic | oblique | inherit // font-variant: normal | small-caps | inherit // font-weight: bold|bolder|lighter|normal|100|200|300|400|500|600|700|800|900 + // font-size: absolute | relative | value | precents | inherit + // font-size-absolute: xx-small, x-small, small, medium, large, x-large, xx-large + // font-size-relative: larger | smaller var property = event.property, propertyName = property.text.toLowerCase(), diff --git a/tests/rules/check-fonts-alternatives.js b/tests/rules/check-fonts-alternatives.js index e57b77d1..3251ec56 100644 --- a/tests/rules/check-fonts-alternatives.js +++ b/tests/rules/check-fonts-alternatives.js @@ -60,6 +60,20 @@ Assert.areEqual(mkErrorMessage("MS Serif", "New York"), result.messages[0].message); }, + "Font size (absolute) + family form": function(){ + var result = CSSLint.verify(".c { font: xx-small MS Serif }", testSpec); + + Assert.areEqual(1, result.messages.length); + Assert.areEqual(mkErrorMessage("MS Serif", "New York"), result.messages[0].message); + }, + + "Font size (relative) + family form": function(){ + var result = CSSLint.verify(".c { font: larger MS Serif }", testSpec); + + Assert.areEqual(1, result.messages.length); + Assert.areEqual(mkErrorMessage("MS Serif", "New York"), result.messages[0].message); + }, + "Font - badly formed rule": function(){ // This is equal to identifier 'Tahoma Tahoma Tahoma' var result = CSSLint.verify(".c { font: 14px Tahoma Tahoma Tahoma }", testSpec);