Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/css/Properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -309,11 +309,11 @@ var Properties = {
"-ms-flex-wrap" : "nowrap | wrap | wrap-reverse",
"float" : "left | right | none | inherit",
"float-offset" : 1,
"font" : 1,
"font-family" : 1,
"font" : "<font-shorthand> | caption | icon | menu | message-box | small-caption | status-bar | inherit",
"font-family" : "<font-family> | inherit",
"font-feature-settings" : "<feature-tag-value> | normal | inherit",
"font-kerning" : "auto | normal | none | initial | inherit | unset",
"font-size" : "<absolute-size> | <relative-size> | <length> | <percentage> | inherit",
"font-size" : "<font-size> | inherit",
"font-size-adjust" : "<number> | none | inherit",
"font-stretch" : "normal | ultra-condensed | extra-condensed | condensed | semi-condensed | semi-expanded | expanded | extra-expanded | ultra-expanded | inherit",
"font-style" : "normal | italic | oblique | inherit",
Expand Down Expand Up @@ -376,7 +376,7 @@ var Properties = {
//L
"left" : "<margin-width> | inherit",
"letter-spacing" : "<length> | normal | inherit",
"line-height" : "<number> | <length> | <percentage> | normal | inherit",
"line-height" : "<line-height> | inherit",
"line-break" : "auto | loose | normal | strict",
"line-stacking" : 1,
"line-stacking-ruby" : "exclude-ruby | include-ruby",
Expand Down
105 changes: 105 additions & 0 deletions src/css/ValidationTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,16 @@ var ValidationTypes = {

"<feature-tag-value>": function(part){
return (part.type == "function" && /^[A-Z0-9]{4}$/i.test(part));
},

"<font-size>": function(part){
var result = this["<absolute-size>"](part) || this["<relative-size>"](part) || this["<length>"](part) || this["<percentage>"](part);
return result;
},

"<line-height>": function(part){
var result = this["<number>"](part) || this["<length>"](part) || this["<percentage>"](part) || ValidationTypes.isLiteral(part, "normal");
return result;
}
},

Expand Down Expand Up @@ -428,6 +438,101 @@ var ValidationTypes = {
}

return result;
},

"<font-family>": function(expression){
// identifier: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
var part,
evenness = expression._i % 2,
partResult,
expressionResult = true,
isEscaped = function (text) {
var result = true;
for (var i = 1; i < text.length; i++) {
// 47 is slash, 92 is backslash
if ( /[\x20-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]/.test(text.charAt(i)) && text.charCodeAt(i) != 92 ) {
result = result && text.charCodeAt(i-1) == 92 ;
}
}
return result;
},
isQuoted = function (text) {
return (text.charAt(0) == "'" && text.charAt(text.length-1) == "'") || (text.charAt(0) == '"' && text.charAt(text.length-1) == '"');
};

while (expression.hasNext()) {
part = expression.next();
if (expression._i % 2 == evenness) {
// must be a seperator (thus there is a next value)
partResult = part.value == ',' && expression.hasNext();
} else {
partResult = part.type != 'operator' && (
part.type == 'identifier' ||
(part.type == 'color' && !(/[^A-Za-z]/.test(part.text))) ||
(part.type == 'unknown' && isEscaped(part.text)) ||
(part.type == 'string' && isQuoted(part.text))
) && !(
/^--/.test(part.text) || /^\d/.test(part.text) || /^-\d/.test(part.text)
);
}
expressionResult = expressionResult && partResult;
}
return expressionResult;
},

"<font-shorthand>": function(expression){
// font [ [ <‘font-style’> || <font-variant-css21> || <‘font-weight’> || <‘font-stretch’ ]? <‘font-size’> [ / <‘line-height’> ]? <‘font-family’> ]
var
appearances = {style: 0, variant: 0, weight: 0, stretch: 0},
foundThisIteration,
part,
result;

// check font-appearance, they are optional but may not be doubled
do {
part = expression.next();
foundThisIteration = 0;
if (ValidationTypes.isLiteral(part, Properties["font-style"]) && part.value != 'inherit') {
appearances.style++;
foundThisIteration = appearances.style;
}
// <font-variant-css21>
if (ValidationTypes.isLiteral(part, "normal | small-caps")) {
appearances.variant++;
foundThisIteration = appearances.variant;
}
if (ValidationTypes.isLiteral(part, Properties["font-weight"]) && part.value != 'inherit') {
appearances.weight++;
foundThisIteration = appearances.weight;
}
if (ValidationTypes.isLiteral(part, Properties["font-stretch"]) && part.value != 'inherit') {
appearances.stretch++;
foundThisIteration = appearances.stretch;
}
} while (foundThisIteration == 1);

// validate font-appearance
result = (foundThisIteration <= 1);
// last one was not an appearance
part = expression.previous();

// evaluate font-size, the first obligation
result = result && ValidationTypes.isType(expression, "<font-size>");

part = expression.next();
if (part == '/') {
result = result && ValidationTypes.isType(expression, "<line-height>");
} else if (part) {
expression.previous();
} else {
result = false;
}

// font-family is the latter obligation
result = result && ValidationTypes.isType(expression, "<font-family>");

return result;

}
}
};
66 changes: 66 additions & 0 deletions tests/css/Validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,73 @@
}
}));

suite.add(new ValidationTestCase({
property: "font",

valid: [
"italic small-caps 300 1.3em/10% Genova, 'Comic Sans', sans-serif",
"1.3em Shorties, sans-serif",
"12px monospace",
"caption;",
"status-bar",
"inherit;",
],

invalid: {
"italic oblique bold 1.3em/10% Genova, 'Comic Sans', sans-serif" : "Expected end of value but found 'oblique'.",
"0.9em Nirwana, 'Comic Sans', sans-serif bold" : "Expected (<font-shorthand> | caption | icon | menu | message-box | small-caption | status-bar | inherit) but found '0.9em Nirwana , 'Comic Sans' , sans-serif bold'.",
"'Helvetica Neue', sans-serif 1.2em" : "Expected (<font-shorthand> | caption | icon | menu | message-box | small-caption | status-bar | inherit) but found ''Helvetica Neue' , sans-serif 1.2em'.",
"1.3em" : "Expected (<font-shorthand> | caption | icon | menu | message-box | small-caption | status-bar | inherit) but found '1.3em'.",
"cursive;" : "Expected (<font-shorthand> | caption | icon | menu | message-box | small-caption | status-bar | inherit) but found 'cursive'.",
"'Dormant', sans-serif;" : "Expected (<font-shorthand> | caption | icon | menu | message-box | small-caption | status-bar | inherit) but found ''Dormant' , sans-serif'."
}
}));

suite.add(new ValidationTestCase({
property: "font-family",

valid: [
"Futura, sans-serif",
'"New Century Schoolbook", serif',
"'21st Century', fantasy",
"serif",
"sans-serif",
"cursive",
"fantasy",
"monospace",
// solve problem by quoting
"'Red/Black', sans-serif",
'"Lucida\\", Grande", sans-serif',
"'Ahem!}', sans-serif",
'"test@foo", sans-serif',
"'#POUND', sans-serif",
"'Hawaii 5-0', sans-serif",
// solve problem by escaping
"Red\\/Black, sans-serif",
// accepted in the wild but rejected by the unittest
// '\\"Lucida\\", Grande, sans-serif', // Unexpected error: Expected RBRACE at line 1, col 21.
"Ahem\\!, sans-serif",
"test\\@foo, sans-serif",
// rejected both in the wild and by the unittest
// "\\#POUND, sans-serif", // Unexpected error: Expected a hex color but found '#POUND' at line 1, col 20.
"Hawaii\\ 5\\-0, sans-serif",
"yellowgreen"
],

invalid: {
"--Futura, sans-serif" : "Expected (<font-family> | inherit) but found '--Futura , sans-serif'.",
// errors both in the wild by the unittest
// "47Futura, sans-serif" : "Unexpected token '47Futura' at line 1, col 20.",
// "-7Futura, sans-serif" : "Unexpected token '7Futura' at line 1, col 21.",
"Red/Black, sans-serif" : "Expected (<font-family> | inherit) but found 'Red / Black , sans-serif'.",
"'Lucida' Grande, sans-serif" : "Expected (<font-family> | inherit) but found ''Lucida' Grande , sans-serif'.",
// errors both in the wild by the unittest
// "Ahem!, sans-serif" : "Expected RBRACE at line 59, col 22. This rule looks for recoverable syntax errors.",
// "test@foo, sans-serif" : "Expected RBRACE at line 60, col 22. This rule looks for recoverable syntax errors.",
// "#POUND, sans-serif" : "Expected a hex color but found '#POUND' at line 1, col 20.",
"Hawaii 5-0, sans-serif" : "Expected (<font-family> | inherit) but found 'Hawaii 5 -0 , sans-serif'."
}
}));

suite.add(new ValidationTestCase({
property: "min-height",
Expand Down