1// Copyright 2014 Google Inc. All rights reserved. 2// 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 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 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. 14 15(function(scope) { 16 17 // consume* functions return a 2 value array of [parsed-data, '' or not-yet consumed input] 18 19 // Regex should be anchored with /^ 20 function consumeToken(regex, string) { 21 var result = regex.exec(string); 22 if (result) { 23 result = regex.ignoreCase ? result[0].toLowerCase() : result[0]; 24 return [result, string.substr(result.length)]; 25 } 26 } 27 28 function consumeTrimmed(consumer, string) { 29 string = string.replace(/^\s*/, ''); 30 var result = consumer(string); 31 if (result) { 32 return [result[0], result[1].replace(/^\s*/, '')]; 33 } 34 } 35 36 function consumeRepeated(consumer, separator, string) { 37 consumer = consumeTrimmed.bind(null, consumer); 38 var list = []; 39 while (true) { 40 var result = consumer(string); 41 if (!result) { 42 return [list, string]; 43 } 44 list.push(result[0]); 45 string = result[1]; 46 result = consumeToken(separator, string); 47 if (!result || result[1] == '') { 48 return [list, string]; 49 } 50 string = result[1]; 51 } 52 } 53 54 // Consumes a token or expression with balanced parentheses 55 function consumeParenthesised(parser, string) { 56 var nesting = 0; 57 for (var n = 0; n < string.length; n++) { 58 if (/\s|,/.test(string[n]) && nesting == 0) { 59 break; 60 } else if (string[n] == '(') { 61 nesting++; 62 } else if (string[n] == ')') { 63 nesting--; 64 if (nesting == 0) 65 n++; 66 if (nesting <= 0) 67 break; 68 } 69 } 70 var parsed = parser(string.substr(0, n)); 71 return parsed == undefined ? undefined : [parsed, string.substr(n)]; 72 } 73 74 function lcm(a, b) { 75 var c = a; 76 var d = b; 77 while (c && d) 78 c > d ? c %= d : d %= c; 79 c = (a * b) / (c + d); 80 return c; 81 } 82 83 function ignore(value) { 84 return function(input) { 85 var result = value(input); 86 if (result) 87 result[0] = undefined; 88 return result; 89 } 90 } 91 92 function optional(value, defaultValue) { 93 return function(input) { 94 var result = value(input); 95 if (result) 96 return result; 97 return [defaultValue, input]; 98 } 99 } 100 101 function consumeList(list, input) { 102 var output = []; 103 for (var i = 0; i < list.length; i++) { 104 var result = scope.consumeTrimmed(list[i], input); 105 if (!result || result[0] == '') 106 return; 107 if (result[0] !== undefined) 108 output.push(result[0]); 109 input = result[1]; 110 } 111 if (input == '') { 112 return output; 113 } 114 } 115 116 function mergeWrappedNestedRepeated(wrap, nestedMerge, separator, left, right) { 117 var matchingLeft = []; 118 var matchingRight = []; 119 var reconsititution = []; 120 var length = lcm(left.length, right.length); 121 for (var i = 0; i < length; i++) { 122 var thing = nestedMerge(left[i % left.length], right[i % right.length]); 123 if (!thing) { 124 return; 125 } 126 matchingLeft.push(thing[0]); 127 matchingRight.push(thing[1]); 128 reconsititution.push(thing[2]); 129 } 130 return [matchingLeft, matchingRight, function(positions) { 131 var result = positions.map(function(position, i) { 132 return reconsititution[i](position); 133 }).join(separator); 134 return wrap ? wrap(result) : result; 135 }]; 136 } 137 138 function mergeList(left, right, list) { 139 var lefts = []; 140 var rights = []; 141 var functions = []; 142 var j = 0; 143 for (var i = 0; i < list.length; i++) { 144 if (typeof list[i] == 'function') { 145 var result = list[i](left[j], right[j++]); 146 lefts.push(result[0]); 147 rights.push(result[1]); 148 functions.push(result[2]); 149 } else { 150 (function(pos) { 151 lefts.push(false); 152 rights.push(false); 153 functions.push(function() { return list[pos]; }); 154 })(i); 155 } 156 } 157 return [lefts, rights, function(results) { 158 var result = ''; 159 for (var i = 0; i < results.length; i++) { 160 result += functions[i](results[i]); 161 } 162 return result; 163 }]; 164 } 165 166 scope.consumeToken = consumeToken; 167 scope.consumeTrimmed = consumeTrimmed; 168 scope.consumeRepeated = consumeRepeated; 169 scope.consumeParenthesised = consumeParenthesised; 170 scope.ignore = ignore; 171 scope.optional = optional; 172 scope.consumeList = consumeList; 173 scope.mergeNestedRepeated = mergeWrappedNestedRepeated.bind(null, null); 174 scope.mergeWrappedNestedRepeated = mergeWrappedNestedRepeated; 175 scope.mergeList = mergeList; 176 177})(webAnimations1); 178