Skip to content

Commit ea19b92

Browse files
committed
Merge pull request #331 from jklein/bulletproof-font-face-rule
Adding a new rule (bulletproof @font-face declarations)
2 parents 0add5e3 + ffde7d8 commit ea19b92

File tree

3 files changed

+171
-5
lines changed

3 files changed

+171
-5
lines changed

src/rules/bulletproof-font-face.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Rule: Use the bulletproof @font-face syntax to avoid 404's in old IE
3+
* (http://www.fontspring.com/blog/the-new-bulletproof-font-face-syntax)
4+
*/
5+
/*global CSSLint*/
6+
CSSLint.addRule({
7+
8+
//rule information
9+
id: "bulletproof-font-face",
10+
name: "Use the bulletproof @font-face syntax",
11+
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).",
12+
browsers: "All",
13+
14+
//initialization
15+
init: function(parser, reporter){
16+
var rule = this,
17+
count = 0,
18+
fontFaceRule = false,
19+
firstSrc = true,
20+
ruleFailed = false,
21+
line, col;
22+
23+
// Mark the start of a @font-face declaration so we only test properties inside it
24+
parser.addListener("startfontface", function(event){
25+
fontFaceRule = true;
26+
});
27+
28+
parser.addListener("property", function(event){
29+
// If we aren't inside an @font-face declaration then just return
30+
if (!fontFaceRule) {
31+
return;
32+
}
33+
34+
var propertyName = event.property.toString().toLowerCase(),
35+
value = event.value.toString();
36+
37+
// Set the line and col numbers for use in the endfontface listener
38+
line = event.line;
39+
col = event.col;
40+
41+
// This is the property that we care about, we can ignore the rest
42+
if (propertyName === 'src') {
43+
var regex = /^\s?url\(['"].+\.eot\?.*['"]\)\s*format\(['"]embedded-opentype['"]\).*$/i;
44+
45+
// We need to handle the advanced syntax with two src properties
46+
if (!value.match(regex) && firstSrc) {
47+
ruleFailed = true;
48+
firstSrc = false;
49+
} else if (value.match(regex) && !firstSrc) {
50+
ruleFailed = false;
51+
}
52+
}
53+
54+
55+
});
56+
57+
// Back to normal rules that we don't need to test
58+
parser.addListener("endfontface", function(event){
59+
fontFaceRule = false;
60+
61+
if (ruleFailed) {
62+
reporter.report("@font-face declaration doesn't follow the fontspring bulletproof syntax.", line, col, rule);
63+
}
64+
});
65+
}
66+
});

tests/all-rules.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,6 @@
3333
Assert.areEqual(0, result.messages.length);
3434
},
3535

36-
"Using @font-face should not result in an error": function(){
37-
var result = CSSLint.verify("@font-face { src: local(foo); }", this.options);
38-
Assert.areEqual(0, result.messages.length);
39-
},
40-
4136
"Using @page should not result in an error": function(){
4237
var result = CSSLint.verify("@page { width: 100px; }", this.options);
4338
Assert.areEqual(0, result.messages.length);

tests/rules/bulletproof-font-face.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
(function(){
2+
3+
/*global YUITest, CSSLint*/
4+
var Assert = YUITest.Assert;
5+
6+
YUITest.TestRunner.add(new YUITest.TestCase({
7+
8+
name: "Bulletproof @font-face syntax tests",
9+
10+
"Using the official bulletproof syntax should pass": function(){
11+
var result = CSSLint.verify("@font-face { font-family: 'MyFontFamily';" +
12+
"src: url('myfont-webfont.eot?') format('embedded-opentype')," +
13+
"url('myfont-webfont.woff') format('woff')," +
14+
"url('myfont-webfont.ttf') format('truetype')," +
15+
"url('myfont-webfont.svg#svgFontName') format('svg');}",
16+
{ "bulletproof-font-face": 1 });
17+
Assert.areEqual(0, result.messages.length);
18+
},
19+
20+
"Skipping the hashtag with the svgFontName and having content after .eot? should also pass": function(){
21+
var result = CSSLint.verify("@font-face { font-family: 'MyFontFamily';" +
22+
"src: url('myfont-webfont.eot?#iefix') format('embedded-opentype')," +
23+
"url('myfont-webfont.woff') format('woff')," +
24+
"url('myfont-webfont.ttf') format('truetype')," +
25+
"url('myfont-webfont.svg') format('svg');}",
26+
{ "bulletproof-font-face": 1 });
27+
Assert.areEqual(0, result.messages.length);
28+
},
29+
30+
"The minified version of the code should pass": function(){
31+
var result = CSSLint.verify("@font-face{font-family:'MyFontFamily';" +
32+
"src:url('myfont-webfont.eot?#iefix') format('embedded-opentype')," +
33+
"url('myfont-webfont.woff') format('woff')," +
34+
"url('myfont-webfont.ttf') format('truetype')," +
35+
"url('myfont-webfont.svg#svgFontName') format('svg')}",
36+
{ "bulletproof-font-face": 1 });
37+
Assert.areEqual(0, result.messages.length);
38+
},
39+
40+
"Skipping one of the font types should pass": function(){
41+
var result = CSSLint.verify("@font-face { font-family: 'MyFontFamily';" +
42+
"src: url('myfont-webfont.eot?') format('embedded-opentype')," +
43+
"url('myfont-webfont.woff') format('woff')," +
44+
"url('myfont-webfont.svg#svgFontName') format('svg');}",
45+
{ "bulletproof-font-face": 1 });
46+
Assert.areEqual(0, result.messages.length);
47+
},
48+
49+
50+
"Supplying the fonts in a different order should pass": function(){
51+
var result = CSSLint.verify("@font-face { font-family: 'MyFontFamily';" +
52+
"src: url('myfont-webfont.eot?#iefix') format('embedded-opentype')," +
53+
"url('myfont-webfont.ttf') format('truetype')," +
54+
"url('myfont-webfont.woff') format('woff')," +
55+
"url('myfont-webfont.svg#svgFontName') format('svg');}",
56+
{ "bulletproof-font-face": 1 });
57+
Assert.areEqual(0, result.messages.length);
58+
},
59+
60+
"Using mixed double and single quotes should pass": function(){
61+
var result = CSSLint.verify("@font-face { font-family: 'MyFontFamily';" +
62+
"src: url(\"myfont-webfont.eot?#iefix\") format(\"embedded-opentype\")," +
63+
"url('myfont-webfont.ttf') format('truetype')," +
64+
"url(\"myfont-webfont.woff\") format('woff')," +
65+
"url('myfont-webfont.svg#svgFontName') format('svg');}",
66+
{ "bulletproof-font-face": 1 });
67+
Assert.areEqual(0, result.messages.length);
68+
},
69+
70+
"Having only one font declaration with the question mark should pass": function(){
71+
var result = CSSLint.verify("@font-face{src:url('myfont-webfont.eot?') format('embedded-opentype');}",
72+
{ "bulletproof-font-face": 1 });
73+
Assert.areEqual(0, result.messages.length);
74+
},
75+
76+
"When we aren't inside an @font-face declaration the src property should not be checked": function(){
77+
var result = CSSLint.verify(".foo .bar{src:url('baz.png');}", { "bulletproof-font-face": 1 });
78+
Assert.areEqual(0, result.messages.length);
79+
},
80+
81+
"Using the advanced syntax with two src properties should pass": function(){
82+
var result = CSSLint.verify("@font-face { font-family: 'MyFontFamily';" +
83+
"src: url('webfont.eot'); /* IE9 Compat Modes */" +
84+
"src: url('myfont-webfont.eot?') format('embedded-opentype')," +
85+
"url('myfont-webfont.woff') format('woff')," +
86+
"url('myfont-webfont.ttf') format('truetype')," +
87+
"url('myfont-webfont.svg#svgFontName') format('svg');}",
88+
{ "bulletproof-font-face": 1 });
89+
Assert.areEqual(0, result.messages.length);
90+
},
91+
92+
"Leaving off the question mark should fail": function(){
93+
var result = CSSLint.verify("@font-face { font-family: 'MyFontFamily';" +
94+
"src: url('myfont-webfont.eot') format('embedded-opentype')," +
95+
"url('myfont-webfont.woff') format('woff')," +
96+
"url('myfont-webfont.ttf') format('truetype')," +
97+
"url('myfont-webfont.svg#svgFontName') format('svg');}",
98+
{ "bulletproof-font-face": 1 });
99+
Assert.areEqual(1, result.messages.length);
100+
Assert.areEqual("warning", result.messages[0].type);
101+
Assert.areEqual("@font-face declaration doesn't follow the fontspring bulletproof syntax.", result.messages[0].message);
102+
}
103+
104+
}));
105+
})();

0 commit comments

Comments
 (0)