|
14 | 14 |
|
15 | 15 | (function(scope, testing) {
|
16 | 16 |
|
| 17 | + // Evaluates a calc expression. |
| 18 | + // https://drafts.csswg.org/css-values-3/#calc-notation |
| 19 | + function calculate(expression) { |
| 20 | + // In calc expressions, white space is required on both sides of the |
| 21 | + // + and - operators. https://drafts.csswg.org/css-values-3/#calc-notation |
| 22 | + // Thus any + or - immediately adjacent to . or 0..9 is part of the number, |
| 23 | + // e.g. -1.23e+45 |
| 24 | + // This regular expression matches ( ) * / + - and numbers. |
| 25 | + var tokenRegularExpression = /([\+\-\w\.]+|[\(\)\*\/])/g; |
| 26 | + var currentToken; |
| 27 | + function consume() { |
| 28 | + var matchResult = tokenRegularExpression.exec(expression); |
| 29 | + if (matchResult) |
| 30 | + currentToken = matchResult[0]; |
| 31 | + else |
| 32 | + currentToken = undefined; |
| 33 | + } |
| 34 | + consume(); // Read the initial token. |
| 35 | + |
| 36 | + function calcNumber() { |
| 37 | + // https://drafts.csswg.org/css-values-3/#number-value |
| 38 | + var result = Number(currentToken); |
| 39 | + consume(); |
| 40 | + return result; |
| 41 | + } |
| 42 | + |
| 43 | + function calcValue() { |
| 44 | + // <calc-value> = <number> | <dimension> | <percentage> | ( <calc-sum> ) |
| 45 | + if (currentToken !== '(') |
| 46 | + return calcNumber(); |
| 47 | + consume(); |
| 48 | + var result = calcSum(); |
| 49 | + if (currentToken !== ')') |
| 50 | + return NaN; |
| 51 | + consume(); |
| 52 | + return result; |
| 53 | + } |
| 54 | + |
| 55 | + function calcProduct() { |
| 56 | + // <calc-product> = <calc-value> [ '*' <calc-value> | '/' <calc-number-value> ]* |
| 57 | + var left = calcValue(); |
| 58 | + while (currentToken === '*' || currentToken === '/') { |
| 59 | + var operator = currentToken; |
| 60 | + consume(); |
| 61 | + var right = calcValue(); |
| 62 | + if (operator === '*') |
| 63 | + left *= right; |
| 64 | + else |
| 65 | + left /= right; |
| 66 | + } |
| 67 | + return left; |
| 68 | + } |
| 69 | + |
| 70 | + function calcSum() { |
| 71 | + // <calc-sum> = <calc-product> [ [ '+' | '-' ] <calc-product> ]* |
| 72 | + var left = calcProduct(); |
| 73 | + while (currentToken === '+' || currentToken === '-') { |
| 74 | + var operator = currentToken; |
| 75 | + consume(); |
| 76 | + var right = calcProduct(); |
| 77 | + if (operator === '+') |
| 78 | + left += right; |
| 79 | + else |
| 80 | + left -= right; |
| 81 | + } |
| 82 | + return left; |
| 83 | + } |
| 84 | + |
| 85 | + // <calc()> = calc( <calc-sum> ) |
| 86 | + return calcSum(); |
| 87 | + } |
| 88 | + |
17 | 89 | function parseDimension(unitRegExp, string) {
|
18 | 90 | string = string.trim().toLowerCase();
|
19 | 91 |
|
|
36 | 108 | var taggedUnitRegExp = 'U(' + unitRegExp.source + ')';
|
37 | 109 |
|
38 | 110 | // Validating input is simply applying as many reductions as we can.
|
39 |
| - var typeCheck = string.replace(/[-+]?(\d*\.)?\d+/g, 'N') |
| 111 | + var typeCheck = string.replace(/[-+]?(\d*\.)?\d+([Ee][-+]?\d+)?/g, 'N') |
40 | 112 | .replace(new RegExp('N' + taggedUnitRegExp, 'g'), 'D')
|
41 | 113 | .replace(/\s[+-]\s/g, 'O')
|
42 | 114 | .replace(/\s/g, '');
|
|
54 | 126 | return;
|
55 | 127 |
|
56 | 128 | for (var unit in matchedUnits) {
|
57 |
| - var result = eval(string.replace(new RegExp('U' + unit, 'g'), '').replace(new RegExp(taggedUnitRegExp, 'g'), '*0')); |
| 129 | + var result = calculate(string.replace(new RegExp('U' + unit, 'g'), '').replace(new RegExp(taggedUnitRegExp, 'g'), '*0')); |
58 | 130 | if (!isFinite(result))
|
59 | 131 | return;
|
60 | 132 | matchedUnits[unit] = result;
|
|
0 commit comments