| // Copyright 2014 Google Inc. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| (function(scope, testing) { |
| |
| var styleAttributes = { |
| cssText: 1, |
| length: 1, |
| parentRule: 1, |
| }; |
| |
| var styleMethods = { |
| getPropertyCSSValue: 1, |
| getPropertyPriority: 1, |
| getPropertyValue: 1, |
| item: 1, |
| removeProperty: 1, |
| setProperty: 1, |
| }; |
| |
| var styleMutatingMethods = { |
| removeProperty: 1, |
| setProperty: 1, |
| }; |
| |
| function configureProperty(object, property, descriptor) { |
| descriptor.enumerable = true; |
| descriptor.configurable = true; |
| Object.defineProperty(object, property, descriptor); |
| } |
| |
| function AnimatedCSSStyleDeclaration(element) { |
| WEB_ANIMATIONS_TESTING && console.assert(!(element.style instanceof AnimatedCSSStyleDeclaration), |
| 'Element must not already have an animated style attached.'); |
| |
| // Stores the inline style of the element on its behalf while the |
| // polyfill uses the element's inline style to simulate web animations. |
| // This is needed to fake regular inline style CSSOM access on the element. |
| this._surrogateStyle = document.createElementNS('http://www.w3.org/1999/xhtml', 'div').style; |
| this._style = element.style; |
| this._length = 0; |
| this._isAnimatedProperty = {}; |
| |
| // Copy the inline style contents over to the surrogate. |
| for (var i = 0; i < this._style.length; i++) { |
| var property = this._style[i]; |
| this._surrogateStyle[property] = this._style[property]; |
| } |
| this._updateIndices(); |
| } |
| |
| AnimatedCSSStyleDeclaration.prototype = { |
| get cssText() { |
| return this._surrogateStyle.cssText; |
| }, |
| set cssText(text) { |
| var isAffectedProperty = {}; |
| for (var i = 0; i < this._surrogateStyle.length; i++) { |
| isAffectedProperty[this._surrogateStyle[i]] = true; |
| } |
| this._surrogateStyle.cssText = text; |
| this._updateIndices(); |
| for (var i = 0; i < this._surrogateStyle.length; i++) { |
| isAffectedProperty[this._surrogateStyle[i]] = true; |
| } |
| for (var property in isAffectedProperty) { |
| if (!this._isAnimatedProperty[property]) { |
| this._style.setProperty(property, this._surrogateStyle.getPropertyValue(property)); |
| } |
| } |
| }, |
| get length() { |
| return this._surrogateStyle.length; |
| }, |
| get parentRule() { |
| return this._style.parentRule; |
| }, |
| // Mirror the indexed getters and setters of the surrogate style. |
| _updateIndices: function() { |
| while (this._length < this._surrogateStyle.length) { |
| Object.defineProperty(this, this._length, { |
| configurable: true, |
| enumerable: false, |
| get: (function(index) { |
| return function() { return this._surrogateStyle[index]; }; |
| })(this._length) |
| }); |
| this._length++; |
| } |
| while (this._length > this._surrogateStyle.length) { |
| this._length--; |
| Object.defineProperty(this, this._length, { |
| configurable: true, |
| enumerable: false, |
| value: undefined |
| }); |
| } |
| }, |
| _set: function(property, value) { |
| this._style[property] = value; |
| this._isAnimatedProperty[property] = true; |
| }, |
| _clear: function(property) { |
| this._style[property] = this._surrogateStyle[property]; |
| delete this._isAnimatedProperty[property]; |
| }, |
| }; |
| |
| // Wrap the style methods. |
| for (var method in styleMethods) { |
| AnimatedCSSStyleDeclaration.prototype[method] = (function(method, modifiesStyle) { |
| return function() { |
| var result = this._surrogateStyle[method].apply(this._surrogateStyle, arguments); |
| if (modifiesStyle) { |
| if (!this._isAnimatedProperty[arguments[0]]) |
| this._style[method].apply(this._style, arguments); |
| this._updateIndices(); |
| } |
| return result; |
| } |
| })(method, method in styleMutatingMethods); |
| } |
| |
| // Wrap the style.cssProperty getters and setters. |
| for (var property in document.documentElement.style) { |
| if (property in styleAttributes || property in styleMethods) { |
| continue; |
| } |
| (function(property) { |
| configureProperty(AnimatedCSSStyleDeclaration.prototype, property, { |
| get: function() { |
| return this._surrogateStyle[property]; |
| }, |
| set: function(value) { |
| this._surrogateStyle[property] = value; |
| this._updateIndices(); |
| if (!this._isAnimatedProperty[property]) |
| this._style[property] = value; |
| } |
| }); |
| })(property); |
| } |
| |
| function ensureStyleIsPatched(element) { |
| if (element._webAnimationsPatchedStyle) |
| return; |
| |
| var animatedStyle = new AnimatedCSSStyleDeclaration(element); |
| try { |
| configureProperty(element, 'style', { get: function() { return animatedStyle; } }); |
| } catch (_) { |
| // iOS and older versions of Safari (pre v7) do not support overriding an element's |
| // style object. Animations will clobber any inline styles as a result. |
| element.style._set = function(property, value) { |
| element.style[property] = value; |
| }; |
| element.style._clear = function(property) { |
| element.style[property] = ''; |
| }; |
| } |
| |
| // We must keep a handle on the patched style to prevent it from getting GC'd. |
| element._webAnimationsPatchedStyle = element.style; |
| } |
| |
| scope.apply = function(element, property, value) { |
| ensureStyleIsPatched(element); |
| element.style._set(scope.propertyName(property), value); |
| }; |
| |
| scope.clear = function(element, property) { |
| if (element._webAnimationsPatchedStyle) { |
| element.style._clear(scope.propertyName(property)); |
| } |
| }; |
| |
| if (WEB_ANIMATIONS_TESTING) |
| testing.ensureStyleIsPatched = ensureStyleIsPatched; |
| |
| })(webAnimations1, webAnimationsTesting); |