1 // Copyright 2014 Google Inc. All rights reserved.
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 (function(scope, testing) {
17 function parseDimension(unitRegExp, string) {
18 string = string.trim().toLowerCase();
20 if (string == '0' && 'px'.search(unitRegExp) >= 0)
23 // If we have parenthesis, we're a calc and need to start with 'calc'.
24 if (!/^[^(]*$|^calc/.test(string))
26 string = string.replace(/calc\(/g, '(');
28 // We tag units by prefixing them with 'U' (note that we are already
29 // lowercase) to prevent problems with types which are substrings of
30 // each other (although prefixes may be problematic!)
31 var matchedUnits = {};
32 string = string.replace(unitRegExp, function(match) {
33 matchedUnits[match] = null;
36 var taggedUnitRegExp = 'U(' + unitRegExp.source + ')';
38 // Validating input is simply applying as many reductions as we can.
39 var typeCheck = string.replace(/[-+]?(\d*\.)?\d+/g, 'N')
40 .replace(new RegExp('N' + taggedUnitRegExp, 'g'), 'D')
41 .replace(/\s[+-]\s/g, 'O')
43 var reductions = [/N\*(D)/g, /(N|D)[*/]N/g, /(N|D)O\1/g, /\((N|D)\)/g];
45 while (i < reductions.length) {
46 if (reductions[i].test(typeCheck)) {
47 typeCheck = typeCheck.replace(reductions[i], '$1');
56 for (var unit in matchedUnits) {
57 var result = eval(string.replace(new RegExp('U' + unit, 'g'), '').replace(new RegExp(taggedUnitRegExp, 'g'), '*0'));
58 if (!isFinite(result))
60 matchedUnits[unit] = result;
65 function mergeDimensionsNonNegative(left, right) {
66 return mergeDimensions(left, right, true);
69 function mergeDimensions(left, right, nonNegative) {
74 if (units.indexOf(unit) < 0)
78 left = units.map(function(unit) { return left[unit] || 0; });
79 right = units.map(function(unit) { return right[unit] || 0; });
80 return [left, right, function(values) {
81 var result = values.map(function(value, i) {
82 if (values.length == 1 && nonNegative) {
83 value = Math.max(value, 0);
85 // Scientific notation (e.g. 1e2) is not yet widely supported by browser vendors.
86 return scope.numberToString(value) + units[i];
88 return values.length > 1 ? 'calc(' + result + ')' : result;
92 var lengthUnits = 'px|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc';
93 var parseLength = parseDimension.bind(null, new RegExp(lengthUnits, 'g'));
94 var parseLengthOrPercent = parseDimension.bind(null, new RegExp(lengthUnits + '|%', 'g'));
95 var parseAngle = parseDimension.bind(null, /deg|rad|grad|turn/g);
97 scope.parseLength = parseLength;
98 scope.parseLengthOrPercent = parseLengthOrPercent;
99 scope.consumeLengthOrPercent = scope.consumeParenthesised.bind(null, parseLengthOrPercent);
100 scope.parseAngle = parseAngle;
101 scope.mergeDimensions = mergeDimensions;
103 var consumeLength = scope.consumeParenthesised.bind(null, parseLength);
104 var consumeSizePair = scope.consumeRepeated.bind(undefined, consumeLength, /^/);
105 var consumeSizePairList = scope.consumeRepeated.bind(undefined, consumeSizePair, /^,/);
106 scope.consumeSizePairList = consumeSizePairList;
108 var parseSizePairList = function(input) {
109 var result = consumeSizePairList(input);
110 if (result && result[1] == '') {
115 var mergeNonNegativeSizePair = scope.mergeNestedRepeated.bind(undefined, mergeDimensionsNonNegative, ' ');
116 var mergeNonNegativeSizePairList = scope.mergeNestedRepeated.bind(undefined, mergeNonNegativeSizePair, ',');
117 scope.mergeNonNegativeSizePair = mergeNonNegativeSizePair;
119 scope.addPropertiesHandler(parseSizePairList, mergeNonNegativeSizePairList, [
123 scope.addPropertiesHandler(parseLengthOrPercent, mergeDimensionsNonNegative, [
124 'border-bottom-width',
125 'border-image-width',
127 'border-right-width',
139 scope.addPropertiesHandler(parseLengthOrPercent, mergeDimensions, [
140 'border-bottom-left-radius',
141 'border-bottom-right-radius',
142 'border-top-left-radius',
143 'border-top-right-radius',
167 })(webAnimations1, webAnimationsTesting);