OLD | NEW |
(Empty) | |
| 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(shared, testing) { |
| 16 var shorthandToLonghand = { |
| 17 background: [ |
| 18 'backgroundImage', |
| 19 'backgroundPosition', |
| 20 'backgroundSize', |
| 21 'backgroundRepeat', |
| 22 'backgroundAttachment', |
| 23 'backgroundOrigin', |
| 24 'backgroundClip', |
| 25 'backgroundColor' |
| 26 ], |
| 27 border: [ |
| 28 'borderTopColor', |
| 29 'borderTopStyle', |
| 30 'borderTopWidth', |
| 31 'borderRightColor', |
| 32 'borderRightStyle', |
| 33 'borderRightWidth', |
| 34 'borderBottomColor', |
| 35 'borderBottomStyle', |
| 36 'borderBottomWidth', |
| 37 'borderLeftColor', |
| 38 'borderLeftStyle', |
| 39 'borderLeftWidth' |
| 40 ], |
| 41 borderBottom: [ |
| 42 'borderBottomWidth', |
| 43 'borderBottomStyle', |
| 44 'borderBottomColor' |
| 45 ], |
| 46 borderColor: [ |
| 47 'borderTopColor', |
| 48 'borderRightColor', |
| 49 'borderBottomColor', |
| 50 'borderLeftColor' |
| 51 ], |
| 52 borderLeft: [ |
| 53 'borderLeftWidth', |
| 54 'borderLeftStyle', |
| 55 'borderLeftColor' |
| 56 ], |
| 57 borderRadius: [ |
| 58 'borderTopLeftRadius', |
| 59 'borderTopRightRadius', |
| 60 'borderBottomRightRadius', |
| 61 'borderBottomLeftRadius' |
| 62 ], |
| 63 borderRight: [ |
| 64 'borderRightWidth', |
| 65 'borderRightStyle', |
| 66 'borderRightColor' |
| 67 ], |
| 68 borderTop: [ |
| 69 'borderTopWidth', |
| 70 'borderTopStyle', |
| 71 'borderTopColor' |
| 72 ], |
| 73 borderWidth: [ |
| 74 'borderTopWidth', |
| 75 'borderRightWidth', |
| 76 'borderBottomWidth', |
| 77 'borderLeftWidth' |
| 78 ], |
| 79 flex: [ |
| 80 'flexGrow', |
| 81 'flexShrink', |
| 82 'flexBasis' |
| 83 ], |
| 84 font: [ |
| 85 'fontFamily', |
| 86 'fontSize', |
| 87 'fontStyle', |
| 88 'fontVariant', |
| 89 'fontWeight', |
| 90 'lineHeight' |
| 91 ], |
| 92 margin: [ |
| 93 'marginTop', |
| 94 'marginRight', |
| 95 'marginBottom', |
| 96 'marginLeft' |
| 97 ], |
| 98 outline: [ |
| 99 'outlineColor', |
| 100 'outlineStyle', |
| 101 'outlineWidth' |
| 102 ], |
| 103 padding: [ |
| 104 'paddingTop', |
| 105 'paddingRight', |
| 106 'paddingBottom', |
| 107 'paddingLeft' |
| 108 ] |
| 109 }; |
| 110 |
| 111 var shorthandExpanderElem = document.createElementNS('http://www.w3.org/1999/x
html', 'div'); |
| 112 |
| 113 var borderWidthAliases = { |
| 114 thin: '1px', |
| 115 medium: '3px', |
| 116 thick: '5px' |
| 117 }; |
| 118 |
| 119 var aliases = { |
| 120 borderBottomWidth: borderWidthAliases, |
| 121 borderLeftWidth: borderWidthAliases, |
| 122 borderRightWidth: borderWidthAliases, |
| 123 borderTopWidth: borderWidthAliases, |
| 124 fontSize: { |
| 125 'xx-small': '60%', |
| 126 'x-small': '75%', |
| 127 'small': '89%', |
| 128 'medium': '100%', |
| 129 'large': '120%', |
| 130 'x-large': '150%', |
| 131 'xx-large': '200%' |
| 132 }, |
| 133 fontWeight: { |
| 134 normal: '400', |
| 135 bold: '700' |
| 136 }, |
| 137 outlineWidth: borderWidthAliases, |
| 138 textShadow: { |
| 139 none: '0px 0px 0px transparent' |
| 140 }, |
| 141 boxShadow: { |
| 142 none: '0px 0px 0px 0px transparent' |
| 143 } |
| 144 }; |
| 145 |
| 146 function antiAlias(property, value) { |
| 147 if (property in aliases) { |
| 148 return aliases[property][value] || value; |
| 149 } |
| 150 return value; |
| 151 } |
| 152 |
| 153 function isNotAnimatable(property) { |
| 154 // https://w3c.github.io/web-animations/#concept-not-animatable |
| 155 return property === 'display' || property.lastIndexOf('animation', 0) === 0
|| property.lastIndexOf('transition', 0) === 0; |
| 156 } |
| 157 |
| 158 // This delegates parsing shorthand value syntax to the browser. |
| 159 function expandShorthandAndAntiAlias(property, value, result) { |
| 160 if (isNotAnimatable(property)) { |
| 161 return; |
| 162 } |
| 163 var longProperties = shorthandToLonghand[property]; |
| 164 if (longProperties) { |
| 165 shorthandExpanderElem.style[property] = value; |
| 166 for (var i in longProperties) { |
| 167 var longProperty = longProperties[i]; |
| 168 var longhandValue = shorthandExpanderElem.style[longProperty]; |
| 169 result[longProperty] = antiAlias(longProperty, longhandValue); |
| 170 } |
| 171 } else { |
| 172 result[property] = antiAlias(property, value); |
| 173 } |
| 174 }; |
| 175 |
| 176 function convertToArrayForm(effectInput) { |
| 177 var normalizedEffectInput = []; |
| 178 |
| 179 for (var property in effectInput) { |
| 180 if (property in ['easing', 'offset', 'composite']) { |
| 181 continue; |
| 182 } |
| 183 |
| 184 var values = effectInput[property]; |
| 185 if (!Array.isArray(values)) { |
| 186 values = [values]; |
| 187 } |
| 188 |
| 189 var keyframe; |
| 190 var numKeyframes = values.length; |
| 191 for (var i = 0; i < numKeyframes; i++) { |
| 192 keyframe = {}; |
| 193 |
| 194 if ('offset' in effectInput) { |
| 195 keyframe.offset = effectInput.offset; |
| 196 } else if (numKeyframes == 1) { |
| 197 keyframe.offset = 1.0; |
| 198 } else { |
| 199 keyframe.offset = i / (numKeyframes - 1.0); |
| 200 } |
| 201 |
| 202 if ('easing' in effectInput) { |
| 203 keyframe.easing = effectInput.easing; |
| 204 } |
| 205 |
| 206 if ('composite' in effectInput) { |
| 207 keyframe.composite = effectInput.composite; |
| 208 } |
| 209 |
| 210 keyframe[property] = values[i]; |
| 211 |
| 212 normalizedEffectInput.push(keyframe); |
| 213 } |
| 214 } |
| 215 |
| 216 normalizedEffectInput.sort(function(a, b) { return a.offset - b.offset; }); |
| 217 return normalizedEffectInput; |
| 218 }; |
| 219 |
| 220 function normalizeKeyframes(effectInput) { |
| 221 if (effectInput == null) { |
| 222 return []; |
| 223 } |
| 224 |
| 225 if (window.Symbol && Symbol.iterator && Array.prototype.from && effectInput[
Symbol.iterator]) { |
| 226 // Handle custom iterables in most browsers by converting to an array |
| 227 effectInput = Array.from(effectInput); |
| 228 } |
| 229 |
| 230 if (!Array.isArray(effectInput)) { |
| 231 effectInput = convertToArrayForm(effectInput); |
| 232 } |
| 233 |
| 234 var keyframes = effectInput.map(function(originalKeyframe) { |
| 235 var keyframe = {}; |
| 236 for (var member in originalKeyframe) { |
| 237 var memberValue = originalKeyframe[member]; |
| 238 if (member == 'offset') { |
| 239 if (memberValue != null) { |
| 240 memberValue = Number(memberValue); |
| 241 if (!isFinite(memberValue)) |
| 242 throw new TypeError('Keyframe offsets must be numbers.'); |
| 243 if (memberValue < 0 || memberValue > 1) |
| 244 throw new TypeError('Keyframe offsets must be between 0 and 1.'); |
| 245 } |
| 246 } else if (member == 'composite') { |
| 247 if (memberValue == 'add' || memberValue == 'accumulate') { |
| 248 throw { |
| 249 type: DOMException.NOT_SUPPORTED_ERR, |
| 250 name: 'NotSupportedError', |
| 251 message: 'add compositing is not supported' |
| 252 }; |
| 253 } else if (memberValue != 'replace') { |
| 254 throw new TypeError('Invalid composite mode ' + memberValue + '.'); |
| 255 } |
| 256 } else if (member == 'easing') { |
| 257 memberValue = shared.normalizeEasing(memberValue); |
| 258 } else { |
| 259 memberValue = '' + memberValue; |
| 260 } |
| 261 expandShorthandAndAntiAlias(member, memberValue, keyframe); |
| 262 } |
| 263 if (keyframe.offset == undefined) |
| 264 keyframe.offset = null; |
| 265 if (keyframe.easing == undefined) |
| 266 keyframe.easing = 'linear'; |
| 267 return keyframe; |
| 268 }); |
| 269 |
| 270 var everyFrameHasOffset = true; |
| 271 var looselySortedByOffset = true; |
| 272 var previousOffset = -Infinity; |
| 273 for (var i = 0; i < keyframes.length; i++) { |
| 274 var offset = keyframes[i].offset; |
| 275 if (offset != null) { |
| 276 if (offset < previousOffset) { |
| 277 throw new TypeError('Keyframes are not loosely sorted by offset. Sort
or specify offsets.'); |
| 278 } |
| 279 previousOffset = offset; |
| 280 } else { |
| 281 everyFrameHasOffset = false; |
| 282 } |
| 283 } |
| 284 |
| 285 keyframes = keyframes.filter(function(keyframe) { |
| 286 return keyframe.offset >= 0 && keyframe.offset <= 1; |
| 287 }); |
| 288 |
| 289 function spaceKeyframes() { |
| 290 var length = keyframes.length; |
| 291 if (keyframes[length - 1].offset == null) |
| 292 keyframes[length - 1].offset = 1; |
| 293 if (length > 1 && keyframes[0].offset == null) |
| 294 keyframes[0].offset = 0; |
| 295 |
| 296 var previousIndex = 0; |
| 297 var previousOffset = keyframes[0].offset; |
| 298 for (var i = 1; i < length; i++) { |
| 299 var offset = keyframes[i].offset; |
| 300 if (offset != null) { |
| 301 for (var j = 1; j < i - previousIndex; j++) |
| 302 keyframes[previousIndex + j].offset = previousOffset + (offset - pre
viousOffset) * j / (i - previousIndex); |
| 303 previousIndex = i; |
| 304 previousOffset = offset; |
| 305 } |
| 306 } |
| 307 } |
| 308 if (!everyFrameHasOffset) |
| 309 spaceKeyframes(); |
| 310 |
| 311 return keyframes; |
| 312 } |
| 313 |
| 314 shared.convertToArrayForm = convertToArrayForm; |
| 315 shared.normalizeKeyframes = normalizeKeyframes; |
| 316 |
| 317 if (WEB_ANIMATIONS_TESTING) { |
| 318 testing.normalizeKeyframes = normalizeKeyframes; |
| 319 } |
| 320 |
| 321 })(webAnimationsShared, webAnimationsTesting); |
OLD | NEW |