Skip to content
Merged
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
76 changes: 74 additions & 2 deletions src/dimension-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,78 @@

(function(scope, testing) {

// Evaluates a calc expression.
// https://drafts.csswg.org/css-values-3/#calc-notation
function calculate(expression) {
// In calc expressions, white space is required on both sides of the
// + and - operators. https://drafts.csswg.org/css-values-3/#calc-notation
// Thus any + or - immediately adjacent to . or 0..9 is part of the number,
// e.g. -1.23e+45
// This regular expression matches ( ) * / + - and numbers.
var tokenRegularExpression = /([\+\-\w\.]+|[\(\)\*\/])/g;
var currentToken;
function consume() {
var matchResult = tokenRegularExpression.exec(expression);
if (matchResult)
currentToken = matchResult[0];
else
currentToken = undefined;
}
consume(); // Read the initial token.

function calcNumber() {
// https://drafts.csswg.org/css-values-3/#number-value
var result = Number(currentToken);
consume();
return result;
}

function calcValue() {
// <calc-value> = <number> | <dimension> | <percentage> | ( <calc-sum> )
if (currentToken !== '(')
return calcNumber();
consume();
var result = calcSum();
if (currentToken !== ')')
return NaN;
consume();
return result;
}

function calcProduct() {
// <calc-product> = <calc-value> [ '*' <calc-value> | '/' <calc-number-value> ]*
var left = calcValue();
while (currentToken === '*' || currentToken === '/') {
var operator = currentToken;
consume();
var right = calcValue();
if (operator === '*')
left *= right;
else
left /= right;
}
return left;
}

function calcSum() {
// <calc-sum> = <calc-product> [ [ '+' | '-' ] <calc-product> ]*
var left = calcProduct();
while (currentToken === '+' || currentToken === '-') {
var operator = currentToken;
consume();
var right = calcProduct();
if (operator === '+')
left += right;
else
left -= right;
}
return left;
}

// <calc()> = calc( <calc-sum> )
return calcSum();
}

function parseDimension(unitRegExp, string) {
string = string.trim().toLowerCase();

Expand All @@ -36,7 +108,7 @@
var taggedUnitRegExp = 'U(' + unitRegExp.source + ')';

// Validating input is simply applying as many reductions as we can.
var typeCheck = string.replace(/[-+]?(\d*\.)?\d+/g, 'N')
var typeCheck = string.replace(/[-+]?(\d*\.)?\d+([Ee][-+]?\d+)?/g, 'N')
.replace(new RegExp('N' + taggedUnitRegExp, 'g'), 'D')
.replace(/\s[+-]\s/g, 'O')
.replace(/\s/g, '');
Expand All @@ -54,7 +126,7 @@
return;

for (var unit in matchedUnits) {
var result = eval(string.replace(new RegExp('U' + unit, 'g'), '').replace(new RegExp(taggedUnitRegExp, 'g'), '*0'));
var result = calculate(string.replace(new RegExp('U' + unit, 'g'), '').replace(new RegExp(taggedUnitRegExp, 'g'), '*0'));
if (!isFinite(result))
return;
matchedUnits[unit] = result;
Expand Down
2 changes: 2 additions & 0 deletions test/js/dimension-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ suite('dimension-handler', function() {
{px: 50.0, em: -6.5});
assert.deepEqual(webAnimations1.parseLength('calc((5px + 2px)*(1 + 2*(4 + 2*-5)) + 7px - (5em + 6vw/2)*4)'),
{px: -70, em: -20, vw: -12});
assert.deepEqual(webAnimations1.parseLength('calc(-13.2E+1rem/+12e-1/(+1 + +2 - -2 * 2 - -3))'),
{rem: -11});
assert.deepEqual(webAnimations1.parseLength('calc(calc(5px) + calc(((3))) *calc(calc(10px)))'),
{px: 35});
});
Expand Down