/* anim8js-dom 1.0.17 - anim8 your HTML elements by Philip Diffenderfer */ // UMD (Universal Module Definition) (function (root, factory) { if (typeof define === 'function' && define.amd) // jshint ignore:line { // AMD. Register as an anonymous module. define(['anim8js'], function(anim8) { // jshint ignore:line return factory(anim8, root); }); } else if (typeof module === 'object' && module.exports) // jshint ignore:line { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require('anim8js'), global); // jshint ignore:line } else { // Browser globals (root is window) factory(root.anim8, root); } }(this, function(anim8, window) { var FastMap = anim8.FastMap; var Animator = anim8.Animator; var Factory = anim8.Factory; var Class = anim8.Class; var Color = anim8.Color; var Easings = anim8.Easings; var EasingTypes = anim8.EasingTypes; var isFunction = anim8.isFunction; var isString = anim8.isString; var isNumber = anim8.isNumber; var isDefined = anim8.isDefined; var isObject = anim8.isObject; var isBoolean = anim8.isBoolean; var coalesce = anim8.coalesce; var clamp = anim8.clamp; var toArray = anim8.toArray; var $calculator = anim8.calculator; var HTMLElement = (this.HTMLElement || window.HTMLElement); var document = (this.document || window.document); if (!document) { throw 'document is not defined on this or window, if you are building for node you cannot use the anim8js-dom package. If you are building with webpack make sure to set output.globalObject to "this".'; } if (!HTMLElement) { throw 'HTMLElement is not defined on this or window, if you are building for node you cannot use the anim8js-dom package. If you are building with webpack make sure to set output.globalObject to "this".'; } function override(target, source) { for (var prop in source) { target[ prop ] = source[ prop ]; } } /** * Returns true if the given variable is an HTML element. * * @method anim8.isElement * @param {Any} x * @return {Boolean} */ function isElement(x) { return typeof HTMLElement === "object" ? x instanceof HTMLElement : x && typeof x === "object" && x !== null && x.nodeType === 1 && typeof x.nodeName === "string"; } function unset( e, anim, attr, property, css, clearedValue ) { if ( attr === true ) { e.style[ css ] = clearedValue; } else { delete anim.frame[ attr ]; property.set( e, anim ); e.style[ css ] = anim.styles[ css ]; } } /** * Computes the desired style of the given element and returns it as a string. * The style given must be in hyphenated format like so: * anim8.dom.style( element, 'font-size' ) = '12px' * * @param {HTMLElement} e * @param {String} style * @return {String} */ var $style = (function() { var hyphenated = {}; var hyphenize = function(str) { if ( str in hyphenated ) { return hyphenated[ str ]; } var key = str; str = str.replace(/[a-z][A-Z]/g, function(str, letter) { return str[0] + '-' + str[1].toLowerCase(); }); str = str.replace(/^Webkit/, '-webkit'); str = str.replace(/^Moz/, '-moz'); str = str.replace(/^Ms/, '-ms'); str = str.replace(/^O/, '-o'); str = str.replace(/^Khtml/, '-khtml'); hyphenated[ key ] = str; return str; }; return function(e, style) { if (e.currentStyle) { return e.currentStyle[ style ]; } else if (document.defaultView && document.defaultView.getComputedStyle) { return document.defaultView.getComputedStyle( e, null ).getPropertyValue( hyphenize( style ) ); } else { return e.style[ style ]; } }; })(); /** * Given an array of styles this will return the first one that is present on elements in the current browser. * * @param {Array} prefixes * @return {String|false} */ var $prefix = (function() { var a = document.createElement('a'); return function(prefixes) { for (var i = 0; i < prefixes.length; i++) { if ( isDefined( a.style[ prefixes[ i ] ] ) ) { return prefixes[i]; } } return false; }; })(); /** * Parses the string for a value and a unit. * * @param {String} value * @return {Object|false} */ var $parseValue = (function() { var regex = /(-?\d*(\.\d+)|-?\d+)(px|em|%|vw|ex|cm|mm|in|pt|pc|deg|rad)?/; return function(x) { var parsed = regex.exec( x ); if (parsed) { return { value: parseFloat( parsed[1] ), unit: parsed[3] }; } return false; }; })(); /** * Converts one unit to another for a given element. * * For Example: anim8.dom.convert( element, '100px', '%', 'parentWidth' ) * returns how much percent 100px relativeTo parentWidth of the given element * * @param {HTMLElement} e * @param {String} from * @param {String} toUnit * @param {String|Number} relativeTo * @return {Number|false} */ var $convert = (function() { /** * Calculators how many pixels a given value & unit is. * * For Example: anim8.toPixels( 100, 'in' ) * returns how many pixels are in 1 inch, with up to 2 decimal points of accuracy. */ var toPixels = function(baseValue, baseUnit, defaultRate) { if ( document.body ) { try { var div = document.createElement('div'); document.body.appendChild( div ); div.style.width = baseValue + baseUnit; var pixels = (div.offsetWidth / baseValue); document.body.removeChild( div ); return pixels || defaultRate; } catch (e) { // Do nothing } } return defaultRate; }; var getFontSize = function(e, notUnit, relativeTo) { var fontSize = $style( e, 'fontSize' ); var parsed = $parseValue( fontSize ); if ( !parsed || parsed.unit === notUnit ) { return 12; } if ( parsed.unit === 'px' ) { return parsed.value; } return getConverterScale( e, conversions[ parsed.unit ].px, relativeTo ); }; var variables = {}; variables.parentWidth = function(e) { return e.parentNode.scrollWidth; }; variables.parentHeight = function(e) { return e.parentNode.scrollHeight; }; variables.width = function(e) { return e.offsetWidth; }; variables.height = function(e) { return e.offsetHeight; }; variables.fontSize = function(e) { return getFontSize( e, '%' ); }; variables.parentFontSize = function(e) { return getFontSize( e.parentNode, '%' ); }; variables.htmlFontSize = function(e) { var htmlElement = document.getElementsByTagName("html")[0]; return getFontSize( htmlElement, '%' ); }; var conversions = {}; conversions['pt'] = { px: toPixels( 100, 'pt', 1 ) }; conversions['in'] = { px: toPixels( 100, 'in', 72 ) }; conversions['cm'] = { px: toPixels( 1000, 'cm', 72 / 2.54 ) }; conversions['mm'] = { px: toPixels( 100000, 'mm', 72 / 25.4 ) }; conversions['vw'] = { px: toPixels( 1000, 'vw', 1024 * 0.01 ) }; conversions['deg'] = { rad: Math.PI / 180.0 }; conversions['em'] = { px: function(e, relativeTo) { return getFontSize( e, 'em', relativeTo ); } }; conversions['rem'] = { px: function(e, relativeTo) { var htmlElement = document.getElementsByTagName("html")[0]; return getFontSize( htmlElement, 'rem', relativeTo ); } }; conversions['%'] = { px: function(e, relativeTo) { if ( isNumber( relativeTo ) ) { return relativeTo; } if ( relativeTo in variables ) { return variables[ relativeTo ]( e ) * 0.01; } return 1.0; } }; // Populate conversions going other way. for (var unit in conversions) { for (var to in conversions[ unit ]) { if ( !(to in conversions) ) { conversions[ to ] = {}; } if ( !(unit in conversions[ to ]) ) { var given = conversions[ unit ][ to ]; if ( isNumber( given ) ) { conversions[ to ][ unit ] = 1.0 / given; } if ( isFunction( given ) ) { conversions[ to ][ unit ] = (function(converter) { return function(e, relativeTo) { return 1.0 / converter( e, relativeTo ); }; })( given ); } } } } // Given an element, convert, and relativeTo - return the number we need to multiply by. var getConverterScale = function(e, converter, relativeTo) { if ( isNumber( converter ) ) { return converter; } else if ( isFunction( converter ) ) { return converter( e, relativeTo ); } return 1.0; }; return function(e, from, toUnit, relativeTo) { if ( isNumber( from ) ) { return from; } var parsed = $parseValue( from ); if ( !parsed ) { return false; } var value = parsed.value; var fromUnit = parsed.unit; if ( !fromUnit || fromUnit === toUnit ) { return value; } // First see if we have a direct conversion available... if ( fromUnit in conversions && toUnit in conversions[ fromUnit ] ) { var converter = conversions[ fromUnit ][ toUnit ]; value *= getConverterScale( e, converter, relativeTo ); } // Otherwise convert it to px, then to the desired unit else if ( fromUnit in conversions && conversions[ fromUnit ].px && toUnit in conversions.px ) { var converter1 = conversions[ fromUnit ].px; var converter2 = conversions.px[ toUnit ]; var combined = getConverterScale( e, converter1, relativeTo ) * getConverterScale( e, converter2, relativeTo ); value *= combined; } return value; }; })(); /** * Easings equivalent to the CSS animations. These are approximations since the * exact functions are not performant enough. */ Easings['cssEase'] = Easings.ease; Easings['cssEaseIn'] = Easings.quad; Easings['cssEaseOut'] = EasingTypes.out( Easings.quad ); Easings['cssEaseInOut'] = EasingTypes.inout( Easings.quad ); Easings['cssLinear'] = Easings.linear; var Attributes = {}; /** * The default attribute. */ Attributes['default'] = {defaultValue: 0}; /** * All animatable attributes for AnimatorDoms & HTMLElements. */ Attributes.padding = {defaultValue: 0, defaultUnit: 'px'}; Attributes.paddingTop = {defaultValue: 0, defaultUnit: 'px'}; Attributes.paddingRight = {defaultValue: 0, defaultUnit: 'px'}; Attributes.paddingBottom = {defaultValue: 0, defaultUnit: 'px'}; Attributes.paddingLeft = {defaultValue: 0, defaultUnit: 'px'}; Attributes.margin = {defaultValue: 0, defaultUnit: 'px'}; Attributes.marginTop = {defaultValue: 0, defaultUnit: 'px'}; Attributes.marginRight = {defaultValue: 0, defaultUnit: 'px'}; Attributes.marginBottom = {defaultValue: 0, defaultUnit: 'px'}; Attributes.marginLeft = {defaultValue: 0, defaultUnit: 'px'}; Attributes.borderRadius = {defaultValue: 0, defaultUnit: 'px'}; Attributes.borderTopLeftRadius = {defaultValue: 0, defaultUnit: 'px'}; Attributes.borderTopRightRadius = {defaultValue: 0, defaultUnit: 'px'}; Attributes.borderBottomLeftRadius = {defaultValue: 0, defaultUnit: 'px'}; Attributes.borderBottomRightRadius = {defaultValue: 0, defaultUnit: 'px'}; Attributes.borderWidth = {defaultValue: 0, defaultUnit: 'px'}; Attributes.borderTopWidth = {defaultValue: 0, defaultUnit: 'px'}; Attributes.borderRightWidth = {defaultValue: 0, defaultUnit: 'px'}; Attributes.borderBottomWidth = {defaultValue: 0, defaultUnit: 'px'}; Attributes.borderLeftWidth = {defaultValue: 0, defaultUnit: 'px'}; Attributes.outlineWidth = {defaultValue: 0, defaultUnit: 'px'}; Attributes.outlineOffset = {defaultValue: 0}; Attributes.textIndent = {defaultValue: 0, defaultUnit: 'px'}; Attributes.tabSize = {defaultValue: 0, defaultUnit: 'px'}; Attributes.borderSpacing = {defaultValue: 0, defaultUnit: 'px'}; Attributes.fontSize = {defaultValue: 1, defaultUnit: 'em'}; Attributes.lineHeight = {defaultValue: 1, defaultUnit: 'em'}; Attributes.letterSpacing = {defaultValue: 0, defaultUnit: 'px'}; Attributes.wordSpacing = {defaultValue: 0, defaultUnit: 'px'}; Attributes.origin = {defaultValue: {x:50, y:50}, defaultUnit: '%', property: 'transformOrigin', calculator: '2d'}; Attributes.originX = {defaultValue: 50, defaultUnit: '%', property: 'transformOrigin'}; Attributes.originY = {defaultValue: 50, defaultUnit: '%', property: 'transformOrigin'}; Attributes.opacity = {defaultValue: 1}; Attributes.zIndex = {defaultValue: 1}; Attributes.width = {defaultValue: 0, defaultUnit: 'px'}; Attributes.minWidth = {defaultValue: 0, defaultUnit: 'px'}; Attributes.maxWidth = {defaultValue: 0, defaultUnit: 'px'}; Attributes.height = {defaultValue: 0, defaultUnit: 'px'}; Attributes.minHeight = {defaultValue: 0, defaultUnit: 'px'}; Attributes.maxHeight = {defaultValue: 0, defaultUnit: 'px'}; Attributes.angle = {defaultValue: 0, property: 'orbit', defaultUnit: 'deg'}; Attributes.distance = {defaultValue: 0, property: 'orbit', defaultUnit: 'px'}; Attributes.orbitOffset = {defaultValue: {x:50, y:50}, defaultUnit: '%', property: 'orbit', calculator: '2d'}; Attributes.top = {defaultValue: 0, defaultUnit: 'px'}; Attributes.right = {defaultValue: 0, defaultUnit: 'px'}; Attributes.bottom = {defaultValue: 0, defaultUnit: 'px'}; Attributes.left = {defaultValue: 0, defaultUnit: 'px'}; Attributes.center = {defaultValue: {x:0, y:0}, defaultUnit: 'px', property: 'center', calculator: '2d'}; Attributes.centerX = {defaultValue: 0, defaultUnit: 'px', property: 'center'}; Attributes.centerY = {defaultValue: 0, defaultUnit: 'px', property: 'center'}; Attributes.blur = {defaultValue: 0, property: 'filter', defaultUnit: 'px'}; Attributes.sepia = {defaultValue: 0, property: 'filter', defaultUnit: '%'}; Attributes.brightness = {defaultValue: 100, property: 'filter', defaultUnit: '%'}; Attributes.grayscale = {defaultValue: 0, property: 'filter', defaultUnit: '%'}; Attributes.contrast = {defaultValue: 100, property: 'filter', defaultUnit: '%'}; Attributes.invert = {defaultValue: 0, property: 'filter', defaultUnit: '%'}; Attributes.saturation = {defaultValue: 0, property: 'filter', defaultUnit: '%'}; Attributes.hueRotate = {defaultValue: 0, property: 'filter', defaultUnit: 'deg'}; Attributes.rotate = {defaultValue: 0, property: 'transform', defaultUnit: 'deg'}; Attributes.rotate3d = {defaultValue: {x:0, y:0, z:1, angle:0}, property: 'transform', calculator: 'quaternion', defaultUnit: 'deg'}; Attributes.translate = {defaultValue: {x:0, y:0}, property: 'transform', calculator: '2d', defaultUnit: 'px'}; Attributes.translateX = {defaultValue: 0, property: 'transform', defaultUnit: 'px'}; Attributes.translateY = {defaultValue: 0, property: 'transform', defaultUnit: 'px'}; Attributes.translateZ = {defaultValue: 0, property: 'transform', defaultUnit: 'px'}; Attributes.translate3d = {defaultValue: {x:0, y:0, z:0}, property: 'transform', calculator: '3d', defaultUnit: 'px'}; Attributes.scale = {defaultValue: {x:1, y:1}, property: 'transform', calculator: '2d'}; Attributes.scaleX = {defaultValue: 1, property: 'transform'}; Attributes.scaleY = {defaultValue: 1, property: 'transform'}; Attributes.scaleZ = {defaultValue: 1, property: 'transform'}; Attributes.scale3d = {defaultValue: {x:1, y:1, z:1}, property: 'transform', calculator: '3d'}; Attributes.skew = {defaultValue: {x:0, y:0}, defaultUnit: 'deg', property: 'transform', calculator: '2d'}; Attributes.skewX = {defaultValue: 0, defaultUnit: 'deg', property: 'transform'}; Attributes.skewY = {defaultValue: 0, defaultUnit: 'deg', property: 'transform'}; Attributes.backface = {defaultValue: 0}; Attributes.visibility = {defaultValue: 1}; Attributes.backgroundColor = {defaultValue: Color(), calculator: 'rgba'}; Attributes.color = {defaultValue: Color(), calculator: 'rgba'}; Attributes.borderTopColor = {defaultValue: Color(), calculator: 'rgba'}; Attributes.borderRightColor = {defaultValue: Color(), calculator: 'rgba'}; Attributes.borderBottomColor = {defaultValue: Color(), calculator: 'rgba'}; Attributes.borderLeftColor = {defaultValue: Color(), calculator: 'rgba'}; Attributes.borderColor = {defaultValue: Color(), calculator: 'rgba'}; Attributes.outlineColor = {defaultValue: Color(), calculator: 'rgba'}; Attributes.textDecorationColor = {defaultValue: Color(), calculator: 'rgba'}; Attributes.textShadowX = {defaultValue: 0, defaultUnit: 'px', property: 'textShadow'}; Attributes.textShadowY = {defaultValue: 0, defaultUnit: 'px', property: 'textShadow'}; Attributes.textShadowPosition = {defaultValue: {x: 0, y: 0}, defaultUnit: 'px', calculator: '2d', property: 'textShadow'}; Attributes.textShadowBlur = {defaultValue: 0, defaultUnit: 'px', property: 'textShadow'}; Attributes.textShadowColor = {defaultValue: Color(), calculator: 'rgba', property: 'textShadow'}; Attributes.shadowX = {defaultValue: 0, defaultUnit: 'px', property: 'shadow'}; Attributes.shadowY = {defaultValue: 0, defaultUnit: 'px', property: 'shadow'}; Attributes.shadowPosition = {defaultValue: {x: 0, y: 0}, defaultUnit: 'px', calculator: '2d', property: 'shadow'}; Attributes.shadowBlur = {defaultValue: 0, defaultUnit: 'px', property: 'shadow'}; Attributes.shadowSpread = {defaultValue: 0, defaultUnit: 'px', property: 'shadow'}; Attributes.shadowColor = {defaultValue: Color(), calculator: 'rgba', property: 'shadow'}; Attributes.shadowInset = {defaultValue: 0, property: 'shadow'}; Attributes.scrollTop = {defaultValue: 0}; Attributes.scrollLeft = {defaultValue: 0}; /** * Returns an attribute based on the given input. If the input is an object it's assumed to be an attribute and it's * returned immediately. If the input is a string the attribute with the given name is returned. Otherwise * the default attribute is returned. * * @param {Object|String} attr */ function $attribute(attr) { if ( isObject( attr ) && isDefined( attr.defaultValue ) ) { return attr; } if ( isString( attr ) && attr in Attributes ) { return Attributes[ attr ]; } return Attributes['default']; } function factory(nm, relativeTo) { return { get: function(e, anim) { if (anim.animating[nm] === false) { var style = $style( e, nm ); var converted = $convert( e, style, anim.units[ nm ], relativeTo ); if ( converted !== false ) { anim.frame[ nm ] = converted; anim.animating[ nm ] = true; } } }, set: function(e, anim) { anim.styles[ nm ] = anim.value( nm ); }, unset: function(e, anim, attr) { e.style[ nm ] = null; } }; } function factoryDerivable(nm, relativeTo, deriver) { return { get: function(e, anim) { if (anim.animating[nm] === false) { var style = $style( e, nm ); var converted = $convert( e, style, anim.units[ nm ], relativeTo ); if ( converted !== false ) { anim.frame[ nm ] = converted; anim.animating[ nm ] = true; } else if ( isFunction( deriver ) ) { converted = $convert( e, deriver( e ), anim.units[ nm ], relativeTo ); if ( converted !== false ) { anim.frame[ nm ] = converted; anim.animating[ nm ] = true; } } } }, set: function(e, anim) { anim.styles[ nm ] = anim.value( nm ); }, unset: function(e, anim, attr) { e.style[ nm ] = null; } }; } function factoryColor(nm) { return { get: function(e, anim) { if (anim.animating[nm] === false) { var style = $style( e, nm ); var parsed = Color.parse( style ); if (parsed !== false) { anim.frame[nm] = parsed; anim.animating[nm] = true; } } }, set: function(e, anim) { anim.styles[ nm ] = Color.format( anim.frame[nm] ); }, unset: function(e, anim, attr) { e.style[ nm ] = null; } }; } function factoryNumberAttribute(nm) { return { get: function(e, anim) { if (anim.animating[nm] === false) { var parsed = parseFloat( e[ nm ] ); if (isFinite(parsed)) { anim.frame[nm] = parsed; anim.animating[nm] = true; } } }, set: function(e, anim) { anim.attributes[ nm ] = anim.frame[nm]; }, unset: function(e, anim, attr) { e[ nm ] = null; } }; } var Properties = {}; Properties.noop = { get: function(e, anim) { }, set: function(e, anim) { }, unset: function(e, anim) { } }; Properties.padding = factory( 'padding', 'parentWidth' ); Properties.paddingTop = factory( 'paddingTop', 'parentWidth' ); Properties.paddingRight = factory( 'paddingRight', 'parentWidth' ); Properties.paddingBottom = factory( 'paddingBottom', 'parentWidth' ); Properties.paddingLeft = factory( 'paddingLeft', 'parentWidth' ); Properties.margin = factory( 'margin', 'parentWidth' ); Properties.marginTop = factory( 'marginTop', 'parentWidth' ); Properties.marginRight = factory( 'marginRight', 'parentWidth' ); Properties.marginBottom = factory( 'marginBottom', 'parentWidth' ); Properties.marginLeft = factory( 'marginLeft', 'parentWidth' ); Properties.borderRadius = factory( 'borderRadius', 'width' ); Properties.borderTopLeftRadius = factory( 'borderTopLeftRadius', 'width' ); Properties.borderTopRightRadius = factory( 'borderTopRightRadius', 'width' ); Properties.borderBottomLeftRadius = factory( 'borderBottomLeftRadius', 'width' ); Properties.borderBottomRightRadius = factory( 'borderBottomRightRadius', 'width' ); Properties.borderWidth = factory( 'borderWidth' ); Properties.borderTopWidth = factory( 'borderTopWidth' ); Properties.borderRightWidth = factory( 'borderRightWidth' ); Properties.borderBottomWidth = factory( 'borderBottomWidth' ); Properties.borderLeftWidth = factory( 'borderLeftWidth' ); Properties.outlineWidth = factory( 'outlineWidth' ); Properties.textIndent = factory( 'textIndent', 'parentWidth' ); Properties.tabSize = factory( 'tabSize', 'parentWidth' ); Properties.borderSpacing = factory( 'borderSpacing' ); Properties.fontSize = factory( 'fontSize', 'parentFontSize' ); Properties.lineHeight = factory( 'lineHeight', 'fontSize' ); Properties.letterSpacing = factory( 'letterSpacing' ); Properties.wordSpacing = factory( 'wordSpacing' ); Properties.zIndex = factory( 'zIndex' ); Properties.color = factoryColor( 'color' ); Properties.backgroundColor = factoryColor( 'backgroundColor' ); Properties.borderTopColor = factoryColor( 'borderTopColor' ); Properties.borderRightColor = factoryColor( 'borderRightColor' ); Properties.borderBottomColor = factoryColor( 'borderBottomColor' ); Properties.borderLeftColor = factoryColor( 'borderLeftColor' ); Properties.borderColor = factoryColor( 'borderColor' ); Properties.outlineColor = factoryColor( 'outlineColor' ); Properties.textDecorationColor = factoryColor( 'textDecorationColor' ); Properties.minWidth = factory( 'minWidth', 'parentWidth' ); Properties.maxWidth = factory( 'maxWidth', 'parentWidth' ); Properties.minHeight = factory( 'minHeight', 'parentHeight' ); Properties.maxHeight = factory( 'maxHeight', 'parentHeight' ); Properties.width = factoryDerivable('width', 'parentWidth', function(e) { return e.offsetWidth + 'px'; }); Properties.height = factoryDerivable('height', 'parentHeight', function(e) { return e.offsetHeight + 'px'; }); Properties.top = factoryDerivable('top', 'parentHeight', function(e) { return e.offsetTop + 'px'; }); Properties.right = factoryDerivable('right', 'parentWidth', function(e) { return (e.parentNode.scrollWidth - (e.offsetLeft + e.offsetWidth)) + 'px'; }); Properties.bottom = factoryDerivable('bottom', 'parentHeight', function(e) { return (e.parentNode.scrollHeight - (e.offsetTop + e.offsetHeight)) + 'px'; }); Properties.left = factoryDerivable('left', 'parentWidth', function(e) { return e.offsetLeft + 'px'; }); Properties.scrollTop = factoryNumberAttribute( 'scrollTop' ); Properties.scrollLeft = factoryNumberAttribute( 'scrollLeft' ); Properties.zIndex.set = function(e, anim) { anim.styles.zIndex = Math.floor( anim.frame.zIndex ); }; Properties.visibility = { get: function(e, anim) { if (anim.animating.visibility === false) { var style = $style( e, 'visibility' ); anim.frame.visibility = style === 'hidden' ? 0.0 : 1.0; anim.animating = true; } }, set: function(e, anim) { anim.styles.visibility = anim.frame.visibility < 0.5 ? 'hidden' : 'visible'; }, unset: function(e, anim) { e.style.visibility = null; } }; Properties.backface = (function() { var css = $prefix(['WebkitBackfaceVisibility', 'MozBackfaceVisibility', 'msBackfaceVisibility', 'BackfaceVisibility']); if ( !css ) { return Properties.noop; } return { get: function(e, anim) { if ( anim.animating.backface === false ) { var style = $style( e, css ); anim.frame.backface = (style === 'visible') ? 1.0 : 0.0; anim.animating.backface = true; } }, set: function(e, anim) { anim.styles[css] = anim.frame.backface < 0.5 ? 'none' : 'visible'; }, unset: function(e, anim) { e.style[ css ] = null; } }; })(); Properties.transformOrigin = (function() { var css = $prefix(['WebkitTransformOrigin', 'MozTransformOrigin', 'OTransformOrigin', 'msTransformOrigin', 'transformOrigin']); if ( !css ) { return Properties.noop; } var keywords = { 'left': '0%', 'center': '50%', 'right': '100%', 'top': '0%', 'bottom': '100%' }; var setOriginAttribute = function(e, value, anim, attr, relativeTo ) { if (anim.animating[attr] === false) { if ( value in keywords ) { value = keywords[ value ]; } var converted = $convert( e, value, anim.units[ attr ], relativeTo ); if ( converted !== false ) { anim.frame[ attr ] = converted; anim.animating[ attr ] = true; } } }; var setOrigin = function(e, split, anim) { if (anim.animating.origin === false) { if ((split.length === 1) || (split.length === 2 && split[0] === split[1]) || (split.length === 3 && split[0] === split[1] && split[1] === split[2])) { setOriginAttribute( e, split[0], anim, 'origin', 'width' ); } } }; return { get: function(e, anim) { var style = $style( e, css ); if (style) { var origin = style.toLowerCase(); var split = origin.split(' '); switch (split.length) { case 3: setOriginAttribute( e, split[0], anim, 'originX', 'width' ); setOriginAttribute( e, split[1], anim, 'originY', 'height' ); setOriginAttribute( e, split[2], anim, 'originZ' ); setOrigin( e, split, anim ); break; case 2: setOriginAttribute( e, split[0], anim, 'originX', 'width' ); setOriginAttribute( e, split[1], anim, 'originY', 'height' ); setOrigin( e, split, anim ); break; case 1: setOriginAttribute( e, split[0], anim, 'originX', 'width' ); setOriginAttribute( e, split[0], anim, 'originY', 'height' ); setOrigin( e, split, anim ); break; } } }, set: function(e, anim) { var style = null; if ( isDefined( anim.frame.originZ ) ) { style = anim.valueOr( 'originX', 'origin', 'x' ) + ' ' + anim.valueOr( 'originY', 'origin', 'y' ) + ' ' + anim.valueOr( 'originZ', 'origin', 'z' ); } else { style = anim.valueOr( 'originX', 'origin', 'x' ) + ' ' + anim.valueOr( 'originY', 'origin', 'y' ); } anim.styles[css] = style; }, unset: function(e, anim, attr) { unset( e, anim, attr, this, css, null ); } }; })(); Properties.transform = (function() { var css = $prefix(['WebkitTransform', 'MozTransform', 'OTransform', 'msTransform', 'transform']); if ( !css ) { return Properties.noop; } var parse = function( e, value, anim, attr, relativeTo ) { var desiredUnit = anim.units[ attr ]; var converted = $convert( e, value, desiredUnit, relativeTo ); if ( converted !== false ) { return converted; } // TODO show convert this to desiredUnit, however defaultValue may be non-scalar. return anim.getAttribute( attr ).defaultValue; }; var getter1d = function(e, anim, parsed, attr) { return parse( e, parsed[1], anim, attr, 'width' ); }; var getter2d = function(e, anim, parsed, attr) { return { x: parse( e, parsed[1], anim, attr, 'width' ), y: parse( e, parsed[2], anim, attr, 'height' ) }; }; var getter3d = function(e, anim, parsed, attr) { return { x: parse( e, parsed[1], anim, attr, 'width' ), y: parse( e, parsed[2], anim, attr, 'height' ), z: parse( e, parsed[3], anim, attr ) }; }; var getter4d = function(e, anim, parsed, attr) { return { x: parse( e, parsed[1], anim, attr, 'width' ), y: parse( e, parsed[2], anim, attr, 'height' ), z: parse( e, parsed[3], anim, attr ), angle: parse( e, parsed[4], anim, attr ) }; }; var setter1d = function(attr, value, unit) { return attr + '(' + value + unit + ')'; }; var setter2d = function(attr, value, unit) { return attr + '(' + value.x + unit + ',' + value.y + unit + ')'; }; var setter3d = function(attr, value, unit) { return attr + '(' + value.x + unit + ',' + value.y + unit + ',' + value.z + unit + ')'; }; var setter4d = function(attr, value, unit) { return attr + '(' + value.x + ',' + value.y + ',' + value.z + ',' + value.angle + unit + ')'; }; var combine = function(ax, ay, bx, by, ascl, bscl) { return { x: (ascl * ax) + (bscl * bx), y: (ascl * ay) + (bscl * by) }; }; var place1d = function(anim, e, attr, value, relativeTo) { if ( anim.animating[ attr ] === false ) { anim.frame[ attr ] = $convert( e, value, anim.units[ attr ], relativeTo ); anim.animating[ attr ] = true; } }; var place2d = function(anim, e, attr, valueX, valueY, relativeToX, relativeToY) { if ( anim.animating[ attr ] === false ) { anim.frame[ attr ] = { x: $convert( e, valueX, anim.units[ attr ], relativeToX ), y: $convert( e, valueY, anim.units[ attr ], relativeToY ) }; anim.animating[ attr ] = true; } }; var place3d = function(anim, e, attr, valueX, valueY, valueZ, relativeToX, relativeToY, relativeToZ) { if ( anim.animating[ attr ] === false ) { anim.frame[ attr ] = { x: $convert( e, valueX, anim.units[ attr ], relativeToX ), y: $convert( e, valueY, anim.units[ attr ], relativeToY ), z: $convert( e, valueZ, anim.units[ attr ], relativeToZ ) }; anim.animating[ attr ] = true; } }; var place4d = function(anim, e, attr, valueX, valueY, valueZ, valueRotate, relativeToX, relativeToY, relativeToZ, relativeToRotate) { if ( anim.animating[ attr ] === false ) { anim.frame[ attr ] = { x: $convert( e, valueX, anim.units[ attr ], relativeToX ), y: $convert( e, valueY, anim.units[ attr ], relativeToY ), z: $convert( e, valueZ, anim.units[ attr ], relativeToZ ), angle: $convert( e, valueRotate, anim.units[ attr ], relativeToRotate ) }; anim.animating[ attr ] = true; } }; var regexes = { translate: /translate\(([^,]+)\s*,\s*([^\)]+)\)/i, translate3d: /translate3d\(([^,]+)\s*,\s*([^,]+)\s*,\s*([^\)]+)\)/i, translateX: /translateX\(([^\)]+)\)/i, translateY: /translateY\(([^\)]+)\)/i, translateZ: /translateZ\(([^\)]+)\)/i, scale: /scale\(([^,]+)\s*,\s*([^\)]+)\)/i, scale3d: /scale3d\(([^,]+)\s*,\s*([^,]+)\s*,\s*([^\)]+)\)/i, scaleX: /scaleX\(([^\)]+)\)/i, scaleY: /scaleY\(([^\)]+)\)/i, scaleZ: /scaleZ\(([^\)]+)\)/i, rotate: /rotate\(([^\)]+)\)/i, skew: /skew\(([^,]+)\s*,\s*([^\)]+)\)/i, skewX: /skewX\(([^\)]+)\)/i, skewY: /skewY\(([^\)]+)\)/i, rotate3d: /rotate3d\(([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^\)]+)\)/i, rotateX: /rotateX\(([^\)]+)\)/i, rotateY: /rotateY\(([^\)]+)\)/i, rotateZ: /rotateZ\(([^\)]+)\)/i }; var matrix = /matrix\(([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\)/i; var getters = { translate: getter2d, translate3d: getter3d, translateX: getter1d, translateY: getter1d, translateZ: getter1d, scale: getter2d, scale3d: getter3d, scaleX: getter1d, scaleY: getter1d, scaleZ: getter1d, rotate: getter1d, rotate3d: getter4d, rotateX: getter1d, rotateY: getter1d, rotateZ: getter1d, skew: getter2d, skewX: getter1d, skewY: getter1d }; var setters = { translate: setter2d, translate3d: setter3d, translateX: setter1d, translateY: setter1d, translateZ: setter1d, scale: setter2d, scale3d: setter3d, scaleX: setter1d, scaleY: setter1d, scaleZ: setter1d, rotate: setter1d, rotate3d: setter4d, rotateX: setter1d, rotateY: setter1d, rotateZ: setter1d, skew: setter2d, skewX: setter1d, skewY: setter1d }; var props = new FastMap( regexes ); var regex = props.values; var attrs = props.keys; props.setters = []; props.getters = []; for (var prop in getters) { var i = props.indexOf( prop ); props.getters[ i ] = getters[ prop ]; props.setters[ i ] = setters[ prop ]; } return { get: function(e, anim) { var style = $style( e, css ); var matrixParsed = matrix.exec( style ); if ( matrixParsed ) { var a = parseFloat( matrixParsed[ 1 ] ); var b = parseFloat( matrixParsed[ 2 ] ); var c = parseFloat( matrixParsed[ 3 ] ); var d = parseFloat( matrixParsed[ 4 ] ); var tx = parseFloat( matrixParsed[ 5 ] ); var ty = parseFloat( matrixParsed[ 6 ] ); // Make sure the matrix is invertible if ((a * d - b * c) !== 0) { // Take care of translation var translateX = tx + 'px'; var translateY = ty + 'px'; // Compute X scale factor and normalize first row. var scaleX = Math.sqrt( a * a + b * b ); if ( scaleX !== 0 ) { a /= scaleX; b /= scaleX; } // Compute shear factor and make 2nd row orthogonal to 1st. var skew = a * c + b * d; var combined = combine( c, d, a, b, 1.0, -skew ); c = combined.x; d = combined.y; // Now, compute Y scale and normalize 2nd row. var scaleY = Math.sqrt( c * c + d * d ); if ( scaleY !== 0 ) { c /= scaleY; d /= scaleY; skew /= scaleY; } // Now, get the rotation out var rotate = Math.atan2( b, a ) + 'rad'; // Place values in animator. place2d( anim, e, 'translate', translateX, translateY, 'width', 'height' ); place3d( anim, e, 'translate3d', translateX, translateY, 0, 'width', 'height' ); place1d( anim, e, 'translateX', translateX, 'width' ); place1d( anim, e, 'translateY', translateY, 'height' ); place2d( anim, e, 'scale', scaleX, scaleY ); place1d( anim, e, 'scaleX', scaleX ); place1d( anim, e, 'scaleY', scaleY ); place3d( anim, e, 'scale3d', scaleX, scaleY, 1 ); place1d( anim, e, 'rotate', rotate ); place4d( anim, e, 'rotate3d', 0, 0, 1, rotate ); place1d( anim, e, 'rotateZ', rotate ); place2d( anim, e, 'skew', skew, skew ); place1d( anim, e, 'skewX', skew ); place1d( anim, e, 'skewY', skew ); return; } } for (var attr in anim.animating) { var i = props.indexOf( attr ); if ( i !== -1 && anim.animating[ attr ] === false ) { var parsed = regex[ i ].exec( style ); if ( parsed ) { anim.frame[ attr ] = props.getters[ i ]( e, anim, parsed, attr ); anim.animating[ attr ] = true; } } } }, set: function(e, anim) { var transforms = []; for (var i = 0; i < attrs.length; i++) { var attr = attrs[ i ]; if ( attr in anim.frame ) { transforms.push( props.setters[ i ]( attr, anim.frame[ attr ], anim.units[ attr ] ) ); } } if (transforms.length) { anim.styles[ css ] = transforms.join( ' ' ); } }, unset: function(e, anim, attr) { unset( e, anim, attr, this, css, '' ); } }; })(); Properties.opacity = (function() { var css = $prefix(['WebkitOpacity', 'MozOpacity', 'KhtmlOpacity', 'opacity']); if ( !css ) { return Properties.noop; } return { get: function(e, anim) { if (anim.animating.opacity === false) { var style = $style( e, css ); var opacity = parseFloat( style ); if ( !isNaN(opacity) ) { anim.frame.opacity = opacity; anim.animating.opacity = true; } } }, set: function(e, anim) { anim.styles[ css ] = clamp( anim.frame.opacity, 0, 1 ); }, unset: function(e, anim) { e.style[ css ] = null; } }; })(); Properties.shadow = (function() { var css = $prefix(['WebkitBoxShadow', 'MozBoxShadow', 'boxShadow']); if ( !css ) { return Properties.noop; } var parsePart = function( e, anim, attr, value, relativeTo ) { if ( anim.updating[ attr ] === false && value ) { var parsed = $convert( e, value, anim.units[ attr ], relativeTo ); if ( parsed !== false ) { anim.frame[ attr ] = parsed; anim.updating[ attr ] = true; } } }; return { get: function(e, anim) { var style = $style( e, css ); var parts = style.split( ' ' ); if ( parts.length < 3 ) { return; } var inset = 0; if ( parts[ 0 ] === 'inset' ) { inset = 1; parts.shift(); } var x = parts[ 0 ]; var y = parts[ 1 ]; var blur = false, spread = false, color = false; switch ( parts.length ) { case 3: color = parts[ 2 ]; break; case 4: blur = parts[ 2 ]; color = parts[ 3 ]; break; case 5: blur = parts[ 2 ]; spread = parts[ 3 ]; color = parts[ 4 ]; break; } parsePart( e, anim, 'shadowX', x, 'width' ); parsePart( e, anim, 'shadowY', y, 'height' ); parsePart( e, anim, 'shadowBlur', blur, 'width' ); parsePart( e, anim, 'shadowSpread', spread, 'width' ); if ( anim.updating.shadowPosition === false ) { var parsedX = $convert( e, x, anim.units.shadowPosition, 'width' ); var parsedY = $convert( e, y, anim.units.shadowPosition, 'height' ); if ( parsedX !== false && parsedY !== false ) { anim.frame.shadowPosition = { x: parsedX, y: parsedY }; anim.updating.shadowPosition = true; } } if ( anim.updating.shadowInset === false ) { anim.frame.shadowInset = inset; anim.updating.shadowInset = true; } if ( anim.updating.shadowColor === false ) { var parsed = Color.parse( color ); if ( parsed !== false ) { anim.frame.shadowColor = parsed; anim.updating.shadowColor = true; } } }, set: function(e, anim) { var style = ''; if ( anim.frame.shadowInset ) { style += 'inset '; // TODO test - fixed but not sure } style += anim.valueOr( 'shadowX', 'shadowPosition', 'x' ) + ' '; style += anim.valueOr( 'shadowY', 'shadowPosition', 'y' ) + ' '; if ( isNumber( anim.frame.shadowBlur ) ) { style += anim.value( 'shadowBlur' ) + ' '; } if ( isNumber( anim.frame.shadowSpread ) ) { style += anim.value( 'shadowSpread' ) + ' '; } style += Color.format( anim.frame.shadowColor ); anim.styles[ css ] = style; }, unset: function(e, anim, attr) { unset( e, anim, attr, this, css, null ); } }; })(); Properties.textShadow = (function() { var css = $prefix(['WebkitTextShadow', 'MozTextShadow', 'textShadow']); if ( !css ) { return Properties.noop; } var parsePart = function( e, anim, attr, value, relativeTo ) { if ( anim.updating[ attr ] === false && value ) { var parsed = $convert( e, value, anim.units[ attr ], relativeTo ); if ( parsed !== false ) { anim.frame[ attr ] = parsed; anim.updating[ attr ] = true; } } }; return { get: function(e, anim) { var style = $style( e, css ); var parts = style.split( ' ' ); if ( parts.length < 3 ) { return; } var x = parts[ 0 ]; var y = parts[ 1 ]; var blur = false, color = false; switch ( parts.length ) { case 3: color = parts[ 2 ]; break; case 4: blur = parts[ 2 ]; color = parts[ 3 ]; break; } parsePart( e, anim, 'textShadowX', x, 'width' ); parsePart( e, anim, 'textShadowY', y, 'height' ); parsePart( e, anim, 'textShadowBlur', blur, 'width' ); if ( anim.updating.textShadowPosition === false ) { var parsedX = $convert( e, x, anim.units.textShadowPosition, 'width' ); var parsedY = $convert( e, y, anim.units.textShadowPosition, 'height' ); if ( parsedX !== false && parsedY !== false ) { anim.frame.textShadowPosition = { x: parsedX, y: parsedY }; anim.updating.textShadowPosition = true; } } if ( anim.updating.textShadowColor === false ) { var parsed = Color.parse( color ); if ( parsed !== false ) { anim.frame.textShadowColor = parsed; anim.updating.textShadowColor = true; } } }, set: function(e, anim) { var style = ''; if ( anim.frame.shadowInset ) { style += 'inset '; // TODO test - fixed but not sure } style += anim.valueOr( 'textShadowX', 'textShadowPosition', 'x' ) + ' '; style += anim.valueOr( 'textShadowY', 'textShadowPosition', 'y' ) + ' '; if ( isNumber( anim.frame.textShadowBlur ) ) { style += anim.value( 'textShadowBlur' ) + ' '; } if ( isNumber( anim.frame.textShadowSpread ) ) { style += anim.value( 'textShadowSpread' ) + ' '; } style += Color.format( anim.frame.textShadowColor ); anim.styles[ css ] = style; }, unset: function(e, anim, attr) { unset( e, anim, attr, this, css, null ); } }; })(); Properties.filter = (function() { var css = $prefix(['WebkitFilter', 'MozFilter', 'OFilter', 'msFilter', 'filter']); if ( !css ) { return Properties.noop; } var methods = { grayscale: 'grayscale', sepia: 'sepia', saturate: 'saturate', hueRotate: 'hue-rotate', invert: 'invert', brightness: 'brightness', contrast: 'contrast', blur: 'blur' }; var patterns = {}; for (var attr in methods) { patterns[attr] = new RegExp( methods[attr] + '\(([^\)]+)\)', 'i'); } return { get: function(e, anim) { var style = $style( e, css ); for (var attr in patterns) { if ( anim.animating[attr] === false ) { var parsed = patterns[attr].exec( style ); if ( parsed ) { var converted = $convert( e, parsed[1], anim.units[ attr ] ); if ( converted !== false ) { anim.frame[ attr ] = converted; anim.animating[ attr ] = true; } } } } }, set: function(e, anim) { // we don't check anim.updated[attr] here since the current value of a transform property is important var filters = []; for (var attr in methods) { if ( attr in anim.frame ) { filters.push( methods[attr] + '(' + anim.value( attr ) + ')' ); } } if (filters.length) { anim.styles[ css ] = filters.join(' '); } }, unset: function(e, anim, attr) { unset( e, anim, attr, this, css, null ); } }; })(); Properties.center = { get: function(e, anim) { var cx = (e.offsetLeft + e.offsetWidth * 0.5) + 'px'; var cy = (e.offsetTop + e.offsetHeight * 0.5) + 'px'; if ( anim.animating.center === false ) { var desiredUnit = anim.units.center; var ccx = $convert( e, cx, desiredUnit, 'parentWidth' ); var ccy = $convert( e, cy, desiredUnit, 'parentHeight' ); if ( ccx !== false && ccy !== false ) { anim.frame.center = { x: ccx, y: ccy }; anim.animating.center = true; } } if ( anim.animating.centerX === false ) { var desiredUnit = anim.units.centerX; var ccx = $convert( e, cx, desiredUnit, 'parentWidth' ); if ( ccx !== false ) { anim.frame.centerX = ccx; anim.animating.centerX = true; } } if ( anim.animating.centerY === false ) { var desiredUnit = anim.units.centerY; var ccy = $convert( e, cy, desiredUnit, 'parentHeight' ); if ( ccy !== false ) { anim.frame.centerY = ccy; anim.animating.centerY = true; } } }, preset: function(e, anim) { anim.cached.width = $convert( e, e.offsetWidth + 'px', anim.units.centerX || anim.units.center, 'parentWidth' ); anim.cached.height = $convert( e, e.offsetHeight + 'px', anim.units.centerY || anim.units.center, 'parentHeight' ); }, set: function(e, anim) { var rw = anim.cached.width * 0.5; var rh = anim.cached.height * 0.5; if ( anim.updated.center ) { anim.styles.left = (anim.frame.center.x - rw) + anim.units.center; anim.styles.top = (anim.frame.center.y - rh) + anim.units.center; } if ( anim.updated.centerX ) { anim.styles.left = (anim.frame.centerX - rw) + anim.units.centerX; } if ( anim.updated.centerY ) { anim.styles.top = (anim.frame.centerY - rh) + anim.units.centerY; } }, unset: function(e, anim, attr) { } }; Properties.orbit = { DEGREE_TO_RADIAN: Math.PI / 180.0, RADIAN_TO_DEGREE: 180.0 / Math.PI, get: function(e, anim) { var ox = (e.parentNode.scrollWidth * 0.5); var oy = (e.parentNode.scrollHeight * 0.5); var cx = (e.offsetLeft + e.offsetWidth * 0.5); var cy = (e.offsetTop + e.offsetHeight * 0.5); var dx = cx - ox; var dy = cy - oy; if ( anim.animating.orbitOffset === false ) { var cunit = anim.units.orbitOffset; var cox = $convert( e, ox + 'px', cunit, 'parentWidth' ); var coy = $convert( e, oy + 'px', cunit, 'parentHeight' ); if ( cox !== false && coy !== false ) { anim.frame.orbitOffset = { x: cox, y: coy }; anim.animating.orbitOffset = false; } } if ( anim.animating.distance === false ) { anim.frame.distance = Math.sqrt( dx * dx + dy * dy ); anim.animating.distance = true; } if ( anim.animating.angle === false ) { anim.frame.angle = Math.atan2( dy, dx ) * this.RADIAN_TO_DEGREE; anim.animating.angle = true; } }, preset: function(e, anim) { anim.cached.parentWidth = e.parentNode.scrollWidth; anim.cached.parentHeight = e.parentNode.scrollHeight; anim.cached.width = e.offsetWidth; anim.cached.height = e.offsetHeight; }, set: function(e, anim) { // TODO calculator this correctly var cunit = anim.units.orbitOffset || '%'; var orbitX = anim.frame.orbitOffset ? anim.frame.orbitOffset.x : 50; var orbitY = anim.frame.orbitOffset ? anim.frame.orbitOffset.y : 50; var originUnit = anim.units.origin || '%'; var originX = anim.frame.origin ? anim.frame.origin.x : 50; var originY = anim.frame.origin ? anim.frame.origin.y : 50; var cox = $convert( e, orbitX + cunit, 'px', anim.cached.parentWidth / 100.0 ); var coy = $convert( e, orbitY + cunit, 'px', anim.cached.parentHeight / 100.0 ); var ox = $convert( e, originX + originUnit, 'px', anim.cached.width / 100.0 ); var oy = $convert( e, originY + originUnit, 'px', anim.cached.height / 100.0 ); var angle = (anim.frame.angle || 0.0) * this.DEGREE_TO_RADIAN; var distance = anim.frame.distance || 0.0; var cos = Math.cos( angle ) * distance; var sin = Math.sin( angle ) * distance; anim.styles.left = (cox + cos - ox) + 'px'; anim.styles.top = (coy + sin - oy) + 'px'; }, unset: function(e, anim, attr) { } }; /** * Returns a property for the given name. * * @param {String|Object} * @return {Object} */ function $property(prop) { if ( isObject( prop ) && isFunction( prop.get ) && isFunction( prop.set ) ) { return prop; } if ( isString( prop ) && prop in Properties ) { return Properties[ prop ]; } throw prop + ' is not a valid property'; } /** * Instantiates a new AnimatorDom given a subject. * * @param {HTMLElement} e * @class AnimatorDom * @constructor * @extends Animator */ function AnimatorDom(subject) { this.reset( subject ); this.properties = new FastMap(); this.propertiesPreset = new FastMap(); this.attributeToProperty = {}; this.animating = {}; this.cached = {}; this.units = {}; this.styles = {}; this.attributes = {}; this.styled = false; this.stylesUpdated = false; } /** * Extends anim8.Animator */ Class.extend( AnimatorDom, Animator, { preupdate: function(now) { // If there are events with paths that contain computed values we should // populate the frame directly from the HTML element. var aa = this.attrimatorsAdded; if ( aa.length ) { var properties = {}; for (var i = aa.length - 1; i >= 0; i--) { var attrimator = aa[ i ]; var attr = attrimator.attribute; if ( !(attr in this.frame) && attrimator.hasComputed() ) { properties[ this.attributeToProperty[ attr ] ] = true; this.animating[ attr ] = false; } } for (var prop in properties) { this.properties.get( prop ).get( this.subject, this ); } for (var i = aa.length - 1; i >= 0; i--) { var attrimator = aa[ i ]; var attr = attrimator.attribute; this.setDefault( attr ); attrimator.start( now, this ); } aa.length = 0; } // If a property currently being animated requires some heads up before it // gets or sets a value, notify it. TODO removed dead properties. var presets = this.propertiesPreset.values; for (var i = presets.length - 1; i >= 0; i--) { presets[ i ].preset( this.subject, this ); } this.trigger('preupdate'); return this; }, update: function(now) { this._update( now ); this.getStyles(); this.stylesUpdated = true; return this; }, apply: function() { if ( !this.styled && !this.stylesUpdated ) { this.getStyles(); } if ( this.styled ) { for (var prop in this.styles) { this.subject.style[ prop ] = this.styles[ prop ]; } for (var prop in this.attributes) { this.subject[ prop ] = this.attributes[ prop ]; } for (var attr in this.frame) { this.updated[ attr ] = false; } this.styled = false; } this.stylesUpdated = false; this.trigger('apply'); this.trimAttrimators(); return this; }, placeAttrimator: function( attrimator ) { this._placeAttrimator( attrimator ); var attr = attrimator.attribute; var attribute = this.getAttribute( attr ); var propertyName = attribute.propertyName; var property = attribute.property; this.properties.put( propertyName, property ); this.attributeToProperty[ attr ] = propertyName; this.units[ attr ] = attribute.defaultUnit; if ( attrimator.input && attrimator.input.units && attr in attrimator.input.units ) { this.units[ attr ] = attrimator.input.units[ attr ]; } if ( isFunction( property.preset ) ) { this.propertiesPreset.put( propertyName, property ); } return this; }, restore: function() { var props = this.properties.values; for (var i = props.length - 1; i >= 0; i--) { props[ i ].unset( this.subject, this, true ); } this.frame = {}; return this; }, unset: function( attributes ) { var attributes = toArray( coalesce( attributes, this.frame ) ); for (var i = attributes.length - 1; i >= 0; i--) { var attr = attributes[ i ]; var prop = this.attributeToProperty[ attr ]; var property = this.properties.get( prop ); if ( property ) { property.unset( this.subject, this, attr ); } this.attrimators.remove( attr ); delete this.frame[ attr ]; } return this; }, set: function( attributes ) { var props = {}; var updated = {}; var units = {}; var styles = {}; var attrs = {}; for (var attr in attributes) { var attribute = this.getAttribute( attr ); var value = attributes[ attr ]; units[ attr ] = attribute.defaultUnit; if ( isString( value ) ) { var parsed = $parseValue( value, attribute.defaultUnit ); if ( parsed !== false ) { units[ attr ] = parsed.unit || attribute.defaultUnit; value = parsed.value; } } var parsed = attribute.parse( value ); if ( parsed !== false ) { props[ attribute.propertyName ] = attribute.property; attributes[ attr ] = parsed; updated[ attr ] = true; } } var flash = { source: this, units: units, frame: attributes, updated: updated, styles: styles, attributes: attrs, cached: {}, get: function(attributes) { return this.source.get( attributes ); }, value: function(attr) { return attributes[ attr ] + units[ attr ]; }, valueOr: function(attr, other, subproperty) { var value = attributes[ attr ]; if ( !isDefined( value ) ) { value = attributes[ attr = other ]; if ( isDefined( subproperty ) ) { value = value[ subproperty ]; } } return value + units[ attr ]; } }; for (var prop in props) { var property = props[ prop ]; if ( isFunction( property.preset ) ) { props[ prop ].preset( this.subject, flash ); } } for (var prop in props) { props[ prop ].set( this.subject, flash ); } for (var prop in styles) { this.subject.style[ prop ] = styles[ prop ]; } for (var prop in attrs) { this.subject[ prop ] = attrs[ prop ]; } return this; }, /** * Builds the styles map in preparation to be applied. * * @method getStyles */ getStyles: function() { this.styles = {}; this.attributes = {}; var applyProperties = {}; for (var attr in this.frame) { if ( this.updated[ attr ] ) { var prop = this.attributeToProperty[ attr ]; if ( this.properties.has( prop ) ) { applyProperties[ prop ] = true; } } } for (var prop in applyProperties) { this.properties.get( prop ).set( this.subject, this ); this.styled = true; } }, /** * Gets the current attribute values for all attributes specified. The argument must be an object * where the key is the name of an attribute and the value is the desired unit. * * @method get * @param {Object} attributes * @return {Object} */ get: function(attributes) { var props = {}; var animating = {}; var units = {}; var out = {}; for (var attr in attributes) { var attribute = this.getAttribute( attr ); animating[ attr ] = false; units[ attr ] = attributes[ attr ] || attribute.defaultUnit; props[ attribute.propertyName ] = attribute.property; out[ attr ] = attribute.cloneDefault(); } var flash = { source: this, units: units, frame: out, animating: animating, unit: function(attr) { return units[ attr ]; } }; for (var prop in props) { props[ prop ].get( this.subject, flash ); } return out; }, /** * Returns a function that returns the current value for the given attribute when invoked. * * @param {String} attribute * @param {String} desiredUnit * @param {String} relativeTo * @return {Function} */ ref: function(attribute, desiredUnit, relativeTo) { var animator = this; var request = {}; return function() { if ( attribute in animator.frame && isNumber( animator.frame[ attribute ] ) ) { return $convert( animator.e, animator.value( attribute ), desiredUnit, relativeTo ); } request[ attribute ] = desiredUnit; var current = animator.get( request ); if ( isDefined( current[ attribute ] ) ) { return current[ attribute ]; } return animator.getAttribute( attribute ).defaultValue; }; }, /** * Returns the value for the given attribute as a string with the current units. * * @method value * @param {String} attr * @return {String} */ value: function(attr) { return this.frame[ attr ] + this.units[ attr ]; }, /** * Returns the value for the given attribute as a string with the current units. * if the attribute doesn't exist a secondary one is looked up. If that value * is an object and contains the given subproperty the value is resolved once again. * * @method valueOr * @param {String} attr * @param {String} other * @param [String] subproperty * @return {String} */ valueOr: function(attr, other, subproperty) { var value = this.frame[ attr ]; if ( !isDefined( value ) ) { value = this.frame[ attr = other ]; if ( isDefined( subproperty ) ) { value = value[ subproperty ]; } } return value + this.units[ attr ]; }, /** * Tweens a single attribute to a target value. * * **See:** {{#crossLink "Core/anim8.options:method"}}{{/crossLink}} * * @method tweenTo * @param {String} attr * @param {T} target * @param {String|Array|Object} [options] * @param {Boolean} [cache=false] * @param {String} [unit] * @chainable */ tweenTo: function(attr, target, options, cache, unit) { this.convertExisting( attr, unit ); this._tweenTo( attr, target, options, cache ); this.units[ attr ] = unit || this.units[ attr ]; return this; }, /** * Tweens multiple attributes to target values. * * **See:** {{#crossLink "Core/anim8.options:method"}}{{/crossLink}} * * @method tweenManyTo * @param {Object} targets * @param {String|Array|Object} [options] * @param {Boolean} [cache=false] * @param {Object} [units] * @chainable */ tweenManyTo: function(targets, options, cache, units) { this.convertExistingMany( units ); this._tweenManyTo( targets, options, cache ); override( this.units, units ); return this; }, /** * Tweens a single attribute from a starting value to the current value. * * **See:** {{#crossLink "Core/anim8.options:method"}}{{/crossLink}} * * @method tweenFrom * @param {String} attr * @param {T} starting * @param {String|Array|Object} [options] * @param {Boolean} [cache=false] * @param {String} [unit] * @chainable */ tweenFrom: function(attr, starting, options, cache, unit) { this.convertExisting( attr, unit ); this._tweenFrom( attr, starting, options, cache ); this.units[ attr ] = unit || this.units[ attr ]; return this; }, /** * Tweens multiple attributes from starting values to the current values. * * **See:** {{#crossLink "Core/anim8.options:method"}}{{/crossLink}} * * @method tweenManyFrom * @param {Object} startings * @param {String|Array|Object} [options] * @param {Boolean} [cache=false] * @param {Object} [units] * @chainable */ tweenManyFrom: function(startings, options, cache, units) { this.convertExistingMany( units ); this._tweenManyFrom( startings, options, cache ); override( this.units, units ); return this; }, /** * Tweens an attribute from a starting value to an ending value. * * **See:** {{#crossLink "Core/anim8.options:method"}}{{/crossLink}} * * @method tween * @param {String} attr * @param {T} starts * @param {T} ends * @param {String|Array|Object} [options] * @param {Boolean} [cache=false] * @param {String} [unit] * @chainable */ tween: function(attr, starts, ends, options, cache, unit) { this.convertExisting( attr, unit ); this._tween( attr, starts, ends, options, cache ); this.units[ attr ] = unit || this.units[ attr ]; return this; }, /** * Tweens multiple attributes from starting values to ending values. * * **See:** {{#crossLink "Core/anim8.options:method"}}{{/crossLink}} * * @method tweenMany * @param {Object} starts * @param {Object} ends * @param {String|Array|Object} [options] * @param {Boolean} [cache=false] * @param {Object} [units] * @chainable */ tweenMany: function(starts, ends, options, cache, units) { this.convertExistingMany( units ); this._tweenMany( starts, ends, options, cache ); override( this.units, units ); return this; }, /** * Moves an attribute relative to its current value. * * **See:** {{#crossLink "Core/anim8.options:method"}}{{/crossLink}} * * @method move * @param {String} attr * @param {T} amount * @param {String|Array|Object} [options] * @param {Boolean} [cache=false] * @param {String} [unit] * @chainable */ move: function(attr, amount, options, cache, unit) { this.convertExisting( attr, unit ); this._move( attr, amount, options, cache ); this.units[ attr ] = unit || this.units[ attr ]; return this; }, /** * Moves multiple attribute relative to their current value. * * **See:** {{#crossLink "Core/anim8.options:method"}}{{/crossLink}} * * @method moveMany * @param {Object} amounts * @param {String|Array|Object} [options] * @param {Boolean} [cache=false] * @param {Object} [units] * @chainable */ moveMany: function(amounts, options, cache, units) { this.convertExistingMany( units ); this._moveMany( amounts, options, cache ); override( this.units, units ); return this; }, /** * Follows the attribute along the given path definition. * * **See:** {{#crossLink "Core/anim8.options:method"}}{{/crossLink}} * * @method follow * @param {String} attr * @param {Path|Object|String} path * @param {Object} [options] * @param {Boolean} [cache=false] * @param {String} [unit] * @chainable */ follow: function(attr, path, options, cache, unit) { this.convertExisting( attr, unit ); this._follow( attr, path, options, cache ); this.units[ attr ] = unit || this.units[ attr ]; return this; }, /** * Converts any existing attributes to the desired units. * * @method convertExistingMany * @param {Object} units */ convertExistingMany: function(units) { if ( units && isObject( units ) ) { var current = this.get( units ); for (var attr in current) { this.frame[ attr ] = current[ attr ]; } } }, /** * Converts any existing attribute to the desired unit. * * @method convertExisting * @param {String} attr * @param {String} toUnit */ convertExisting: function(attr, toUnit) { if ( toUnit && attr in this.frame && attr in this.units && this.units[ attr ] !== toUnit ) { var request = {}; request[ attr ] = toUnit; this.convertExistingMany( request ); } } }); /** * A factory for HTML Elements */ function FactoryDom() { this.cached = {}; this.ids = 0; this.elementAttribute = 'anim8'; this.priority = 5; this.attributes = {}; } Class.extend( FactoryDom, Factory, { /** * Determines whether the given subject is valid for this factory to create Animators for. * * @param {any} subject * @return {Boolean} */ is: function(subject) { return isElement( subject ); }, /** * Returns an animator given a subject. * * @param {HTMLElement} subject * @return {anim8.Animator} */ animatorFor: function(subject) { var animatorId = subject.getAttribute( this.elementAttribute ); if (!(animatorId in this.cached)) { var animator = new AnimatorDom( subject ); subject.setAttribute( this.elementAttribute, animatorId = animator.id = ++this.ids ); animator.factory = this; this.cached[animatorId] = animator; } return this.cached[ animatorId ]; }, /** * Destroys the animator by unlinking the animator from the subject. * * @param {anim8.Animator} animator */ destroy: function(animator) { delete this.cached[ animator.id ]; }, /** * Returns the attribute descriptor for the given attribute. * * @param {String} attr * @return {Object} */ attribute: function(attr) { var attribute = this.attributes[ attr ]; if ( !attribute ) { attribute = this.attributes[ attr ] = $attribute( attr ); var calculatorName = attribute.calculator; var calculator = $calculator( calculatorName ); var defaultValue = calculator.parse( attribute.defaultValue, calculator.ZERO ); var propertyName = coalesce( attribute.property, attr ); var property = $property( propertyName ); var defaultUnit = attribute.defaultUnit || ''; attribute.calculatorName = calculatorName; attribute.calculator = calculator; attribute.defaultValue = defaultValue; attribute.name = attr; attribute.propertyName = propertyName; attribute.property = property; attribute.defaultUnit = defaultUnit; attribute.parse = function(value, ignoreRelative) { return this.calculator.parse( value, this.defaultValue, ignoreRelative ); }; attribute.cloneDefault = function() { return this.calculator.clone( this.defaultValue ); }; } return attribute; } }); var browser = { IE: (function() { if (!(window.ActiveXObject || this.ActiveXObject) && ("ActiveXObject" in window || "ActiveXObject" in this)) { return 11; } if (!document.all) { return false; } if (!document.compatMode) { return 5; } if (!(window.XMLHttpRequest || this.XMLHttpRequest)) { return 6; } if (!document.querySelector) { return 7; } if (!document.addEventListener) { return 8; } if (!(window.atob || this.atob)) { return 9; } return 10; })() }; var Matrix = { identity: function() { return { m11: 1.0, m12: 0.0, m21: 0.0, m22: 1.0 }; }, multiply: function(a, b) { return { m11: (a.m11 * b.m11) + (a.m12 * b.m21), m22: (a.m21 * b.m12) + (a.m22 * b.m22), m21: (a.m21 * b.m11) + (a.m22 * b.m21), m12: (a.m11 * b.m12) + (a.m12 * b.m22) }; }, rotate: function(radians) { var cos = Math.cos( radians ); var sin = Math.sin( radians ); return { m11: cos, m12: -sin, m21: sin, m22: cos }; }, scale: function(scaleX, scaleY) { return { m11: scaleX, m12: 0.0, m21: 0.0, m22: scaleY }; }, skew: function(skewX, skewY) { return { m11: 1.0, m12: Math.tan( skewX ), m21: Math.tan( skewY ), m22: 1.0 }; }, transform: function(matrix, x, y) { return { x: matrix.m11 * x + matrix.m12 * y, y: matrix.m21 * x + matrix.m22 * y }; }, adjustment: function(matrix, w, h) { var x0 = w * matrix.m11; var x1 = h * matrix.m12; var x2 = w * matrix.m11 + h * matrix.m12; var xmin = Math.min( x0, Math.min( x1, Math.min( x2, 0 ) ) ); var xmax = Math.max( x0, Math.max( x1, Math.max( x2, 0 ) ) ); var y0 = w * matrix.m21; var y1 = h * matrix.m22; var y2 = w * matrix.m21 + h * matrix.m22; var ymin = Math.min( y0, Math.min( y1, Math.min( y2, 0 ) ) ); var ymax = Math.max( y0, Math.max( y1, Math.max( y2, 0 ) ) ); return { x: xmax - xmin, y: ymax - ymin }; } }; function concatenateStyle(anim, style, value) { if ( isDefined( anim.styles[ style ] ) ) { anim.styles[ style ] += ' ' + value; } else { anim.styles[ style ] = value; } } function setProperty(attr, property) { var attribute = Attributes[ attr ]; if ( isString( attribute.property ) || !isDefined( attribute.property ) ) { attribute.property = property; } else { attribute.propertyName = property; attribute.property = $property( property ); } } /* transform, blur, opacity, margin top & left IE <= 8 */ if ( browser.IE && browser.IE <= 8 ) { Properties.ieTransform = { presettings: { width: { savedAs: 'width', property: 'width', relativeTo: 'parentWidth', defaultProperty: 'offsetWidth', toUnit: 'px' }, height: { savedAs: 'height', property: 'height', relativeTo: 'parentHeight', defaultProperty: 'offsetHeight', toUnit: 'px' }, rotate: { savedAs: 'rotate', property: 'rotate', toUnit: 'rad' }, rotate3d: { savedAs: 'rotate', property: 'rotate3d', subproperty: 'angle', toUnit: 'rad' }, skewX: { savedAs: 'skewX', property: 'skewX', toUnit: 'rad' }, skew2dX: { savedAs: 'skewX', property: 'skew', subproperty: 'x', toUnit: 'rad'/*, remove: true*/ }, skewY: { savedAs: 'skewY', property: 'skewY', toUnit: 'rad' }, skew2dY: { savedAs: 'skewY', property: 'skew', subproperty: 'y', toUnit: 'rad'/*, remove: true*/ }, translateX: { savedAs: 'translateX', property: 'translateX', relativeTo: 'width', toUnit: 'px' }, translate2dX: { savedAs: 'translateX', property: 'translate', subproperty: 'x', toUnit: 'px', relativeTo: 'width' }, translate3dX: { savedAs: 'translateX', property: 'translate3d', subproperty: 'x', toUnit: 'px', relativeTo: 'width'/*, remove: true*/ }, translateY: { savedAs: 'translateY', property: 'translateY', relativeTo: 'height', toUnit: 'px' }, translate2dY: { savedAs: 'translateY', property: 'translate', subproperty: 'y', toUnit: 'px', relativeTo: 'height' }, translate3dY: { savedAs: 'translateY', property: 'translate3d', subproperty: 'y', toUnit: 'px', relativeTo: 'height'/*, remove: true*/ }, scaleX: { savedAs: 'scaleX', property: 'scaleX' }, scale2dX: { savedAs: 'scaleX', property: 'scale', subproperty: 'x' }, scale3dX: { savedAs: 'scaleX', property: 'scale3d', subproperty: 'x'/*, remove: true*/ }, scaleY: { savedAs: 'scaleY', property: 'scaleY' }, scale2dY: { savedAs: 'scaleY', property: 'scale', subproperty: 'y' }, scale3dY: { savedAs: 'scaleY', property: 'scale3d', subproperty: 'y'/*, remove: true*/ }, originX: { savedAs: 'originX', property: 'originX', toUnit: '%', relativeTo: 'width' }, origin2dX: { savedAs: 'originX', property: 'origin', subproperty: 'x', toUnit: '%', relativeTo: 'width'/*, remove: true*/ }, originY: { savedAs: 'originY', property: 'originY', toUnit: '%', relativeTo: 'height' }, origin2dY: { savedAs: 'originY', property: 'origin', subproperty: 'y', toUnit: '%', relativeTo: 'height'/*, remove: true*/ }, marginLeft: { savedAs: 'marginLeft', property: 'marginLeft', toUnit: 'px', relativeTo: 'parentWidth' }, marginTop: { savedAs: 'marginTop', property: 'marginTop', toUnit: 'px', relativeTo: 'parentWidth' }, blur: { savedAs: 'blur', property: 'blur', toUnit: 'px', relativeTo: 'parentWidth', remove: true }, opacity: { savedAs: 'opacity', property: 'opacity', remove: true } }, get: function(e, anim) { var settings = this.presettings; Properties.marginLeft.get( e, anim ); Properties.marginTop.get( e, anim ); this.getFramed( e, anim, settings.rotate ); this.getFramed( e, anim, settings.rotate3d ); this.getFramed( e, anim, settings.skewX ); this.getFramed( e, anim, settings.skewY ); this.getFramed( e, anim, settings.skew2dX ); this.getFramed( e, anim, settings.skew2dY ); this.getFramed( e, anim, settings.translateX ); this.getFramed( e, anim, settings.translate2dX ); this.getFramed( e, anim, settings.translate3dX ); this.getFramed( e, anim, settings.translateY ); this.getFramed( e, anim, settings.translate2dY ); this.getFramed( e, anim, settings.translate3dY ); this.getFramed( e, anim, settings.scaleX ); this.getFramed( e, anim, settings.scale2dX ); this.getFramed( e, anim, settings.scale3dX ); this.getFramed( e, anim, settings.scaleY ); this.getFramed( e, anim, settings.scale2dY ); this.getFramed( e, anim, settings.scale3dY ); this.getFramed( e, anim, settings.originX ); this.getFramed( e, anim, settings.originY ); this.getFramed( e, anim, settings.origin2dX ); this.getFramed( e, anim, settings.origin2dY ); this.getFramed( e, anim, settings.blur ); this.getFramed( e, anim, settings.opacity ); }, getFramed: function(e, flash, def) { var anim = coalesce( flash.source, flash ); var attr = def.property; var value = anim.frame[ attr ]; if ( isBoolean( flash.animating[ attr ] ) && isDefined( value ) ) { var fromUnit = def.toUnit; var toUnit = flash.units[ attr ]; if ( def.subproperty ) { var attribute = anim.getAttribute( attr ); if ( !flash.frame[ attr ] ) { flash.frame[ attr ] = attribute.cloneDefault(); } var converted = value[ def.subproperty ]; if ( fromUnit !== toUnit ) { converted = $convert( e, converted + fromUnit, toUnit, def.relativeTo ); } if ( converted !== false ) { flash.frame[ attr ][ def.subproperty ] = converted; flash.animating[ attr ] = true; } } else { var converted = value; if ( fromUnit !== toUnit ) { converted = $convert( e, converted + fromUnit, toUnit, def.relativeTo ); } if ( converted !== false ) { flash.frame[ attr ] = converted; flash.animating[ attr ] = true; } } } return ( flash.animating[ attr ] === true ); }, resolveRelativeTo: function(anim, relativeTo) { var cached = anim.cached[ relativeTo ]; if ( isNumber( cached ) ) { return cached * 0.01; } return relativeTo; }, cacheConverted: function(e, anim, def) { var value = anim.frame[ def.property ]; var canConvert = isDefined( value ); if ( canConvert ) { var valueUnit = anim.units[ def.property ]; var actualValue = ( def.subproperty ? value[ def.subproperty ] : value ); if ( valueUnit !== def.toUnit ) { var united = actualValue + valueUnit; var relativeTo = this.resolveRelativeTo( anim, def.relativeTo ); actualValue = $convert( e, united, def.toUnit, relativeTo ); } if ( actualValue !== false ) { anim.cached[ def.savedAs ] = actualValue; } else { canConvert = false; } } else if ( def.remove ) { delete anim.cached[ def.savedAs ]; } return canConvert; }, cacheValue: function(e, anim, def) { var value = anim.frame[ def.property ]; var hasValue = isDefined( value ); if ( hasValue ) { anim.cached[ def.savedAs ] = ( def.subproperty ? value[ def.subproperty ] : value ); } else if ( def.remove ) { delete anim.cached[ def.savedAs ]; } return hasValue; }, cacheDimension: function(e, anim, def) { if ( !this.cacheConverted( e, anim, def ) ) { var computedValue = $style( e, def.property ); anim.cached[ def.savedAs ] = $convert( e, computedValue, 'px', def.relativeTo ) || e[ def.defaultProperty ]; } }, cachedOrDefault: function(e, anim, value, attribute, toUnit, relativeTo) { if ( isDefined( value ) ) { return value; } if ( attribute.defaultUnit === toUnit ) { return attribute.defaultValue; } relativeTo = this.resolveRelativeTo( anim, relativeTo ); return $convert( e, attribute.defaultValue + attribute.defaultUnit, toUnit, relativeTo ); }, preset: function(e, anim) { var cached = anim.cached; var settings = this.presettings; var attrs = Attributes; this.cacheDimension( e, anim, settings.width ); this.cacheDimension( e, anim, settings.height ); if ( !this.cacheConverted( e, anim, settings.rotate ) ) { this.cacheConverted( e, anim, settings.rotate3d ); } if ( !this.cacheConverted( e, anim, settings.skewX ) ) { this.cacheConverted( e, anim, settings.skew2dX ); } if ( !this.cacheConverted( e, anim, settings.skewY ) ) { this.cacheConverted( e, anim, settings.skew2dY ); } if ( isDefined( cached.skewX ) || isDefined( cached.skewY ) ) { cached.skewX = this.cachedOrDefault( e, anim, cached.skewX, attrs.skewX, 'rad' ); cached.skewY = this.cachedOrDefault( e, anim, cached.skewY, attrs.skewY, 'rad' ); } if ( !this.cacheConverted( e, anim, settings.translateX ) ) { if ( !this.cacheConverted( e, anim, settings.translate2dX ) ) { this.cacheConverted( e, anim, settings.translate3dX ); } } if ( !this.cacheConverted( e, anim, settings.translateY ) ) { if ( !this.cacheConverted( e, anim, settings.translate2dY ) ) { this.cacheConverted( e, anim, settings.translate3dY ); } } if ( isDefined( cached.translateX ) || isDefined( cached.translateY ) ) { cached.translateX = this.cachedOrDefault( e, anim, cached.translateX, attrs.translateX, 'px', 'width' ); cached.translateY = this.cachedOrDefault( e, anim, cached.translateY, attrs.translateY, 'px', 'height' ); } if ( !this.cacheValue( e, anim, settings.scaleX ) ) { if ( !this.cacheValue( e, anim, settings.scale2dX ) ) { this.cacheValue( e, anim, settings.scale3dX ); } } if ( !this.cacheValue( e, anim, settings.scaleY ) ) { if ( !this.cacheValue( e, anim, settings.scale2dY ) ) { this.cacheValue( e, anim, settings.scale3dY ); } } if ( !this.cacheConverted( e, anim, settings.originX ) ) { this.cacheConverted( e, anim, settings.origin2dX ); } if ( !this.cacheConverted( e, anim, settings.originY ) ) { this.cacheConverted( e, anim, settings.origin2dY ); } cached.originX = this.cachedOrDefault( e, anim, cached.originX, attrs.originX, '%', 'width' ); cached.originY = this.cachedOrDefault( e, anim, cached.originY, attrs.originY, '%', 'height' ); if ( !isDefined( cached.baseMarginLeft ) ) { var margins = anim.get({ marginLeft: 'px', marginTop: 'px' }); cached.baseMarginLeft = margins.marginLeft || 0; cached.baseMarginTop = margins.marginTop || 0; } if ( this.cacheConverted( e, anim, settings.marginLeft ) ) { cached.marginLeft -= cached.baseMarginLeft; } else { cached.marginLeft = 0; } if ( this.cacheConverted( e, anim, settings.marginTop ) ) { cached.marginTop -= cached.baseMarginTop; } else { cached.marginTop = 0; } this.cacheValue( e, anim, settings.opacity ); this.cacheConverted( e, anim, settings.blur ); }, unset: function(e, anim, attr) { if ( attr === true ) { e.style.filter = ''; e.style.marginLeft = (anim.cached.baseMarginLeft || 0) + 'px'; e.style.marginTop = (anim.cached.baseMarginTop || 0) + 'px'; anim.cached = {}; } }, set: function(e, anim) { var cached = anim.cached; var attrs = Attributes; var w = cached.width; var h = cached.height; var anchorX = cached.originX * 0.01; var anchorY = cached.originY * 0.01; var dx = 0; var dy = 0; var matrix = Matrix.identity(); if ( isDefined( cached.scaleX ) || isDefined( cached.scaleY ) ) { matrix = Matrix.multiply( matrix, Matrix.scale( coalesce( cached.scaleX, attrs.scaleX.defaultValue ), coalesce( cached.scaleY, attrs.scaleY.defaultValue ) )); } if ( isDefined( cached.skewX ) ) { matrix = Matrix.multiply( matrix, Matrix.skew( cached.skewX, cached.skewY ) ); } if ( isDefined( cached.rotate ) ) { matrix = Matrix.multiply( matrix, Matrix.rotate( cached.rotate ) ); } if ( isDefined( cached.translateX ) ) { dx += cached.translateX; dy += cached.translateY; } // Calculate the new size of the element based on the matrix. We need to // adjust by the difference because IE is special. var newSize = Matrix.adjustment( matrix, w, h ); dx += (w - newSize.x) * 0.5; dy += (h - newSize.y) * 0.5; // Adjust for a non-centered transformation var hw = w * 0.5; var hh = h * 0.5; var origin = Matrix.transform( matrix, hw, hh ); dx += (hw - origin.x) * (anchorX * 2 - 1); dy += (hh - origin.y) * (anchorY * 2 - 1); // If margin is already specified, add it to the new margin value. dx += cached.marginLeft + cached.baseMarginLeft; dy += cached.marginTop + cached.baseMarginTop; // Set the margin to account for a lack of translation. anim.styles.marginLeft = dx + 'px'; anim.styles.marginTop = dy + 'px'; // The array of filter operations var filters = []; // Transformations if ( matrix.m11 !== 1.0 || matrix.m12 !== 0.0 || matrix.m21 !== 0.0 || matrix.m22 !== 1.0 ) { var transformFilter = 'progid:DXImageTransform.Microsoft.Matrix(SizingMethod=\'auto expand\'' + ', M11=' + matrix.m11 + ', M12=' + matrix.m12 + ', M21=' + matrix.m21 + ', M22=' + matrix.m22 + ')'; filters.push( transformFilter ); } // Opacity if ( isNumber( cached.opacity ) && !isNaN( cached.opacity ) ) { var opacityFilter = 'progid:DXImageTransform.Microsoft.Alpha(Opacity=' + ( cached.opacity * 100 ) + ')'; filters.push( opacityFilter ); } // Blur if ( isNumber( cached.blur ) && !isNaN( cached.blur ) ) { var blurFilter = 'progid:DXImageTransform.Microsoft.Blur(PixelRadius=\'' + ( cached.blur ) + 'px\')'; filters.push( blurFilter ); } // Set the filter properties! anim.styles.filter = filters.join( ' ' ); // Force layout anim.styles.zoom = 1; } }; var attributes = [ 'marginLeft', 'marginTop', 'blur', 'opacity', 'origin', 'originX', 'originY', 'translate', 'translateX', 'translateY', 'translateZ', 'translate3d', 'rotate', 'rotate3d', 'skew', 'skewX', 'skewY', 'scale', 'scaleX', 'scaleY', 'scaleZ', 'scale3d' ]; for ( var i = 0; i < attributes.length; i++ ) { setProperty( attributes[ i ], 'ieTransform' ); } } /* minWidth < IE 8 */ if ( browser.IE && browser.IE < 8 ) { Properties.minWidth.set = function(e, anim) { anim.styles.width = 'expression( this.scrollWidth \< ' + (anim.frame.minWidth + 1) + ' ? "' + anim.frame.minWidth + anim.units.minWidth + '" : "auto")'; }; } /* maxWidth < IE 8 */ if ( browser.IE && browser.IE < 8 ) { Properties.maxWidth.set = function(e, anim) { anim.styles.width = 'expression( this.scrollWidth > ' + (anim.frame.maxWidth - 1) + ' ? "' + anim.frame.maxWidth + anim.units.maxWidth + '" : "auto")'; }; } /* minHeight < IE 8 */ if ( browser.IE && browser.IE < 8 ) { Properties.minHeight.set = function(e, anim) { anim.styles.height = 'expression( this.scrollHeight \< ' + (anim.frame.minHeight + 1) + ' ? "' + anim.frame.minHeight + anim.units.minHeight + '" : "auto")'; }; } /* maxHeight < IE 8 */ if ( browser.IE && browser.IE < 8 ) { Properties.maxHeight.set = function(e, anim) { anim.styles.height = 'expression( this.scrollHeight > ' + (anim.frame.maxHeight - 1) + ' ? "' + anim.frame.maxHeight + anim.units.maxHeight + '" : "auto")'; }; } // Register Factory anim8.Factories['default'] = anim8.Factories['dom'] = new FactoryDom(); // Classes anim8.AnimatorDom = AnimatorDom; anim8.FactoryDom = FactoryDom; // Functions anim8.isElement = isElement; // Variables anim8.browser = browser; anim8.Matrix = Matrix; // Namespace anim8.dom = { Attributes: Attributes, attribute: $attribute, convert: $convert, style: $style, parseValue: $parseValue, property: $property, prefix: $prefix, concatenateStyle: concatenateStyle, setProperty: setProperty, unset: unset, factory: factory, factoryDerivable: factoryDerivable, factoryColor: factoryColor }; return anim8; }));