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(scope, testing) { |
| 16 |
| 17 var SVG_TRANSFORM_PROP = '_webAnimationsUpdateSvgTransformAttr'; |
| 18 |
| 19 /** |
| 20 * IE/Edge do not support `transform` styles for SVG elements. Instead, |
| 21 * `transform` attribute can be animated with some restrictions. |
| 22 * See https://connect.microsoft.com/IE/feedback/details/811744/ie11-bug-with-
implementation-of-css-transforms-in-svg, |
| 23 * https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/117375
4/, |
| 24 * https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/101242
/, etc. |
| 25 * The same problem is exhibited by pre-Chrome Android browsers (ICS). |
| 26 * Unfortunately, there's no easy way to feature-detect it. |
| 27 */ |
| 28 function updateSvgTransformAttr(window, element) { |
| 29 if (!element.namespaceURI || element.namespaceURI.indexOf('/svg') == -1) { |
| 30 return false; |
| 31 } |
| 32 if (!(SVG_TRANSFORM_PROP in window)) { |
| 33 window[SVG_TRANSFORM_PROP] = |
| 34 /Trident|MSIE|IEMobile|Edge|Android 4/i.test(window.navigator.userAgen
t); |
| 35 } |
| 36 return window[SVG_TRANSFORM_PROP]; |
| 37 } |
| 38 |
| 39 var styleAttributes = { |
| 40 cssText: 1, |
| 41 length: 1, |
| 42 parentRule: 1, |
| 43 }; |
| 44 |
| 45 var styleMethods = { |
| 46 getPropertyCSSValue: 1, |
| 47 getPropertyPriority: 1, |
| 48 getPropertyValue: 1, |
| 49 item: 1, |
| 50 removeProperty: 1, |
| 51 setProperty: 1, |
| 52 }; |
| 53 |
| 54 var styleMutatingMethods = { |
| 55 removeProperty: 1, |
| 56 setProperty: 1, |
| 57 }; |
| 58 |
| 59 function configureProperty(object, property, descriptor) { |
| 60 descriptor.enumerable = true; |
| 61 descriptor.configurable = true; |
| 62 Object.defineProperty(object, property, descriptor); |
| 63 } |
| 64 |
| 65 function AnimatedCSSStyleDeclaration(element) { |
| 66 WEB_ANIMATIONS_TESTING && console.assert(!(element.style instanceof Animated
CSSStyleDeclaration), |
| 67 'Element must not already have an animated style attached.'); |
| 68 |
| 69 this._element = element; |
| 70 // Stores the inline style of the element on its behalf while the |
| 71 // polyfill uses the element's inline style to simulate web animations. |
| 72 // This is needed to fake regular inline style CSSOM access on the element. |
| 73 this._surrogateStyle = document.createElementNS('http://www.w3.org/1999/xhtm
l', 'div').style; |
| 74 this._style = element.style; |
| 75 this._length = 0; |
| 76 this._isAnimatedProperty = {}; |
| 77 this._updateSvgTransformAttr = updateSvgTransformAttr(window, element); |
| 78 this._savedTransformAttr = null; |
| 79 |
| 80 // Copy the inline style contents over to the surrogate. |
| 81 for (var i = 0; i < this._style.length; i++) { |
| 82 var property = this._style[i]; |
| 83 this._surrogateStyle[property] = this._style[property]; |
| 84 } |
| 85 this._updateIndices(); |
| 86 } |
| 87 |
| 88 AnimatedCSSStyleDeclaration.prototype = { |
| 89 get cssText() { |
| 90 return this._surrogateStyle.cssText; |
| 91 }, |
| 92 set cssText(text) { |
| 93 var isAffectedProperty = {}; |
| 94 for (var i = 0; i < this._surrogateStyle.length; i++) { |
| 95 isAffectedProperty[this._surrogateStyle[i]] = true; |
| 96 } |
| 97 this._surrogateStyle.cssText = text; |
| 98 this._updateIndices(); |
| 99 for (var i = 0; i < this._surrogateStyle.length; i++) { |
| 100 isAffectedProperty[this._surrogateStyle[i]] = true; |
| 101 } |
| 102 for (var property in isAffectedProperty) { |
| 103 if (!this._isAnimatedProperty[property]) { |
| 104 this._style.setProperty(property, this._surrogateStyle.getPropertyValu
e(property)); |
| 105 } |
| 106 } |
| 107 }, |
| 108 get length() { |
| 109 return this._surrogateStyle.length; |
| 110 }, |
| 111 get parentRule() { |
| 112 return this._style.parentRule; |
| 113 }, |
| 114 // Mirror the indexed getters and setters of the surrogate style. |
| 115 _updateIndices: function() { |
| 116 while (this._length < this._surrogateStyle.length) { |
| 117 Object.defineProperty(this, this._length, { |
| 118 configurable: true, |
| 119 enumerable: false, |
| 120 get: (function(index) { |
| 121 return function() { return this._surrogateStyle[index]; }; |
| 122 })(this._length) |
| 123 }); |
| 124 this._length++; |
| 125 } |
| 126 while (this._length > this._surrogateStyle.length) { |
| 127 this._length--; |
| 128 Object.defineProperty(this, this._length, { |
| 129 configurable: true, |
| 130 enumerable: false, |
| 131 value: undefined |
| 132 }); |
| 133 } |
| 134 }, |
| 135 _set: function(property, value) { |
| 136 this._style[property] = value; |
| 137 this._isAnimatedProperty[property] = true; |
| 138 if (this._updateSvgTransformAttr && |
| 139 scope.unprefixedPropertyName(property) == 'transform') { |
| 140 // On IE/Edge, also set SVG element's `transform` attribute to 2d |
| 141 // matrix of the transform. The `transform` style does not work, but |
| 142 // `transform` attribute can be used instead. |
| 143 // Notice, if the platform indeed supports SVG/CSS transforms the CSS |
| 144 // declaration is supposed to override the attribute. |
| 145 if (this._savedTransformAttr == null) { |
| 146 this._savedTransformAttr = this._element.getAttribute('transform'); |
| 147 } |
| 148 this._element.setAttribute('transform', scope.transformToSvgMatrix(value
)); |
| 149 } |
| 150 }, |
| 151 _clear: function(property) { |
| 152 this._style[property] = this._surrogateStyle[property]; |
| 153 if (this._updateSvgTransformAttr && |
| 154 scope.unprefixedPropertyName(property) == 'transform') { |
| 155 if (this._savedTransformAttr) { |
| 156 this._element.setAttribute('transform', this._savedTransformAttr); |
| 157 } else { |
| 158 this._element.removeAttribute('transform'); |
| 159 } |
| 160 this._savedTransformAttr = null; |
| 161 } |
| 162 delete this._isAnimatedProperty[property]; |
| 163 }, |
| 164 }; |
| 165 |
| 166 // Wrap the style methods. |
| 167 for (var method in styleMethods) { |
| 168 AnimatedCSSStyleDeclaration.prototype[method] = (function(method, modifiesSt
yle) { |
| 169 return function() { |
| 170 var result = this._surrogateStyle[method].apply(this._surrogateStyle, ar
guments); |
| 171 if (modifiesStyle) { |
| 172 if (!this._isAnimatedProperty[arguments[0]]) |
| 173 this._style[method].apply(this._style, arguments); |
| 174 this._updateIndices(); |
| 175 } |
| 176 return result; |
| 177 } |
| 178 })(method, method in styleMutatingMethods); |
| 179 } |
| 180 |
| 181 // Wrap the style.cssProperty getters and setters. |
| 182 for (var property in document.documentElement.style) { |
| 183 if (property in styleAttributes || property in styleMethods) { |
| 184 continue; |
| 185 } |
| 186 (function(property) { |
| 187 configureProperty(AnimatedCSSStyleDeclaration.prototype, property, { |
| 188 get: function() { |
| 189 return this._surrogateStyle[property]; |
| 190 }, |
| 191 set: function(value) { |
| 192 this._surrogateStyle[property] = value; |
| 193 this._updateIndices(); |
| 194 if (!this._isAnimatedProperty[property]) |
| 195 this._style[property] = value; |
| 196 } |
| 197 }); |
| 198 })(property); |
| 199 } |
| 200 |
| 201 function ensureStyleIsPatched(element) { |
| 202 if (element._webAnimationsPatchedStyle) |
| 203 return; |
| 204 |
| 205 var animatedStyle = new AnimatedCSSStyleDeclaration(element); |
| 206 try { |
| 207 configureProperty(element, 'style', { get: function() { return animatedSty
le; } }); |
| 208 } catch (_) { |
| 209 // iOS and older versions of Safari (pre v7) do not support overriding an
element's |
| 210 // style object. Animations will clobber any inline styles as a result. |
| 211 element.style._set = function(property, value) { |
| 212 element.style[property] = value; |
| 213 }; |
| 214 element.style._clear = function(property) { |
| 215 element.style[property] = ''; |
| 216 }; |
| 217 } |
| 218 |
| 219 // We must keep a handle on the patched style to prevent it from getting GC'
d. |
| 220 element._webAnimationsPatchedStyle = element.style; |
| 221 } |
| 222 |
| 223 scope.apply = function(element, property, value) { |
| 224 ensureStyleIsPatched(element); |
| 225 element.style._set(scope.propertyName(property), value); |
| 226 }; |
| 227 |
| 228 scope.clear = function(element, property) { |
| 229 if (element._webAnimationsPatchedStyle) { |
| 230 element.style._clear(scope.propertyName(property)); |
| 231 } |
| 232 }; |
| 233 |
| 234 if (WEB_ANIMATIONS_TESTING) { |
| 235 testing.ensureStyleIsPatched = ensureStyleIsPatched; |
| 236 testing.updateSvgTransformAttr = updateSvgTransformAttr; |
| 237 } |
| 238 |
| 239 })(webAnimations1, webAnimationsTesting); |
OLD | NEW |