    /**
     * This class manages animation for a specific {@link #target}. The animation allows
     * animation of various properties on the target, such as size, position, color and others.
     *
     * ## Starting Conditions
     *
     * The starting conditions for the animation are provided by the {@link #from} configuration.
     * Any/all of the properties in the {@link #from} configuration can be specified. If a particular
     * property is not defined, the starting value for that property will be read directly from the target.
     *
     * ## End Conditions
     *
     * The ending conditions for the animation are provided by the {@link #to} configuration. These mark
     * the final values once the animations has finished. The values in the {@link #from} can mirror
     * those in the {@link #to} configuration to provide a starting point.
     *
     * ## Other Options
     *
     *  - {@link #duration}: Specifies the time period of the animation.
     *  - {@link #easing}: Specifies the easing of the animation.
     *  - {@link #iterations}: Allows the animation to repeat a number of times.
     *  - {@link #alternate}: Used in conjunction with {@link #iterations}, reverses the direction every second iteration.
     *
     * ## Example Code
     *
     *     @example
     *     var myComponent = Ext.create('Ext.Component', {
     *         renderTo: document.body,
     *         width: 200,
     *         height: 200,
     *         style: 'border: 1px solid red;'
     *     });
     *
     *     Ext.create('Ext.fx.Anim', {
     *         target: myComponent,
     *         duration: 1000,
     *         from: {
     *             width: 400 //starting width 400
     *         },
     *         to: {
     *             width: 300, //end width 300
     *             height: 300 // end height 300
     *         }
     *     });
     */
    Ext.define('Ext.fx.Anim', {

        /* Begin Definitions */

        mixins: {
            observable: 'Ext.util.Observable'
        },

        requires: ['Ext.fx.Manager', 'Ext.fx.Animator', 'Ext.fx.Easing', 'Ext.fx.CubicBezier', 'Ext.fx.PropertyHandler'],

        /* End Definitions */

        /**
         * @property {Boolean} isAnimation
         * `true` in this class to identify an object as an instantiated Anim, or subclass thereof.
         */
        isAnimation: true,

        /**
         * @cfg {Function} callback
         * A function to be run after the animation has completed.
         */

        /**
         * @cfg {Function} scope
         * The scope that the {@link #callback} function will be called with
         */

        /**
         * @cfg {Number} duration
         * Time in milliseconds for a single animation to last. If the {@link #iterations} property is
         * specified, then each animate will take the same duration for each iteration.
         */
        duration: 250,

        /**
         * @cfg {Number} delay
         * Time to delay before starting the animation.
         */
        delay: 0,

        /* @private used to track a delayed starting time */
        delayStart: 0,

        /**
         * @cfg {Boolean} dynamic
         * Currently only for Component Animation: Only set a component's outer element size bypassing layouts.
         * Set to true to do full layouts for every frame of the animation.
         */
        dynamic: false,

        /**
         * @cfg {String} easing
         * This describes how the intermediate values used during a transition will be calculated.
         * It allows for a transition to change speed over its duration.
         *
         * - backIn
         * - backOut
         * - bounceIn
         * - bounceOut
         * - ease
         * - easeIn
         * - easeOut
         * - easeInOut
         * - elasticIn
         * - elasticOut
         * - cubic-bezier(x1, y1, x2, y2)
         *
         * Note that cubic-bezier will create a custom easing curve following the CSS3 [transition-timing-function][0]
         * specification.  The four values specify points P1 and P2 of the curve as (x1, y1, x2, y2). All values must
         * be in the range [0, 1] or the definition is invalid.
         *
         * [0]: http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag
         */
        easing: 'ease',

        /**
         * @cfg {Object} keyframes
         * Animation keyframes follow the CSS3 Animation configuration pattern. 'from' is always considered '0%' and 'to'
         * is considered '100%'. **Every keyframe declaration must have a keyframe rule for 0% and 100%, possibly defined using
         * "from" or "to".**  A keyframe declaration without these keyframe selectors is invalid and will not be available for
         * animation.  The keyframe declaration for a keyframe rule consists of properties and values. Properties that are unable to
         * be animated are ignored in these rules, with the exception of 'easing' which can be changed at each keyframe. For example:
         *
         *     keyframes : {
         *         '0%': {
         *             left: 100
         *         },
         *         '40%': {
         *             left: 150
         *         },
         *         '60%': {
         *             left: 75
         *         },
         *         '100%': {
         *             left: 100
         *         }
         *     }
         */

        /**
         * @private
         */
        damper: 1,

        /**
         * @private
         */
        bezierRE: /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,

        /**
         * Run the animation from the end to the beginning
         * Defaults to false.
         * @cfg {Boolean} reverse
         */
        reverse: false,

        /**
         * Flag to determine if the animation has started
         * @property running
         * @type Boolean
         */
        running: false,

        /**
         * Flag to determine if the animation is paused. Only set this to true if you need to
         * keep the Anim instance around to be unpaused later; otherwise call {@link #end}.
         * @property paused
         * @type Boolean
         */
        paused: false,

        /**
         * @cfg {Number} iterations
         * Number of times to execute the animation.
         */
        iterations: 1,

        /**
         * @cfg {Boolean} alternate
         * Used in conjunction with iterations to reverse the animation each time an iteration completes.
         */
        alternate: false,

        /**
         * Current iteration the animation is running.
         * @property currentIteration
         * @type Number
         */
        currentIteration: 0,

        /**
         * Starting time of the animation.
         * @property startTime
         * @type Date
         */
        startTime: 0,

        /**
         * Contains a cache of the interpolators to be used.
         * @private
         * @property propHandlers
         * @type Object
         */

        /**
         * @cfg {String/Object} target
         * The {@link Ext.fx.target.Target} to apply the animation to.  This should only be specified when creating an Ext.fx.Anim directly.
         * The target does not need to be a {@link Ext.fx.target.Target} instance, it can be the underlying object. For example, you can
         * pass a Component, Element or Sprite as the target and the Anim will create the appropriate {@link Ext.fx.target.Target} object
         * automatically.
         */

        /**
         * @cfg {Object} from
         * An object containing property/value pairs for the beginning of the animation.  If not specified, the current state of the
         * Ext.fx.target will be used. For example:
         *
         *     from: {
         *         opacity: 0,       // Transparent
         *         color: '#ffffff', // White
         *         left: 0
         *     }
         *
         */

        /**
         * @cfg {Object} to (required)
         * An object containing property/value pairs for the end of the animation. For example:
         *
         *     to: {
         *         opacity: 1,       // Opaque
         *         color: '#00ff00', // Green
         *         left: 500
         *     }
         *
         */

        // @private
        frameCount: 0,

        // @private
        constructor: function(config) {
            var me = this,
                curve;

            config = config || {};
            // If keyframes are passed, they really want an Animator instead.
            if (config.keyframes) {
                return new Ext.fx.Animator(config);
            }
            Ext.apply(me, config);
            if (me.from === undefined) {
                me.from = {};
            }
            me.propHandlers = {};
            me.config = config;
            me.target = Ext.fx.Manager.createTarget(me.target);
            me.easingFn = Ext.fx.Easing[me.easing];
            me.target.dynamic = me.dynamic;

            // If not a pre-defined curve, try a cubic-bezier
            if (!me.easingFn) {
                me.easingFn = String(me.easing).match(me.bezierRE);
                if (me.easingFn && me.easingFn.length == 5) {
                    curve = me.easingFn;
                    me.easingFn = Ext.fx.CubicBezier.cubicBezier(+curve[1], +curve[2], +curve[3], +curve[4]);
                }
            }
            me.id = Ext.id(null, 'ext-anim-');
            me.addEvents(
                /**
                 * @event beforeanimate
                 * Fires before the animation starts. A handler can return false to cancel the animation.
                 * @param {Ext.fx.Anim} this
                 */
                'beforeanimate',
                 /**
                  * @event afteranimate
                  * Fires when the animation is complete.
                  * @param {Ext.fx.Anim} this
                  * @param {Date} startTime
                  */
                'afteranimate',
                 /**
                  * @event lastframe
                  * Fires when the animation's last frame has been set.
                  * @param {Ext.fx.Anim} this
                  * @param {Date} startTime
                  */
                'lastframe'
            );
            me.mixins.observable.constructor.call(me);
            Ext.fx.Manager.addAnim(me);
        },

        /**
         * @private
         * Helper to the target
         */
        setAttr: function(attr, value) {
            return Ext.fx.Manager.items.get(this.id).setAttr(this.target, attr, value);
        },

        /**
         * @private
         * Set up the initial currentAttrs hash.
         */
        initAttrs: function() {
            var me = this,
                from = me.from,
                to = me.to,
                initialFrom = me.initialFrom || {},
                out = {},
                start, end, propHandler, attr;

            for (attr in to) {
                if (to.hasOwnProperty(attr)) {
                    start = me.target.getAttr(attr, from[attr]);
                    end = to[attr];
                    // Use default (numeric) property handler
                    if (!Ext.fx.PropertyHandler[attr]) {
                        if (Ext.isObject(end)) {
                            propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler.object;
                        } else {
                            propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler.defaultHandler;
                        }
                    }
                    // Use custom handler
                    else {
                        propHandler = me.propHandlers[attr] = Ext.fx.PropertyHandler[attr];
                    }
                    out[attr] = propHandler.get(start, end, me.damper, initialFrom[attr], attr);
                }
            }
            me.currentAttrs = out;
        },

        /**
         * @private
         * Fires beforeanimate and sets the running flag.
         */
        start: function(startTime) {
            var me = this,
                delay = me.delay,
                delayStart = me.delayStart,
                delayDelta;

            if (delay) {
                if (!delayStart) {
                    me.delayStart = startTime;
                    return;
                }
                else {
                    delayDelta = startTime - delayStart;
                    if (delayDelta < delay) {
                        return;
                    }
                    else {
                        // Compensate for frame delay;
                        startTime = new Date(delayStart.getTime() + delay);
                    }
                }
            }
            if (me.fireEvent('beforeanimate', me) !== false) {
                me.startTime = startTime;
                if (!me.paused && !me.currentAttrs) {
                    me.initAttrs();
                }
                me.running = true;
                me.frameCount = 0;
            }
        },

        /**
         * @private
         * Calculate attribute value at the passed timestamp.
         * @returns a hash of the new attributes.
         */
        runAnim: function(elapsedTime) {
            var me = this,
                attrs = me.currentAttrs,
                duration = me.duration,
                easingFn = me.easingFn,
                propHandlers = me.propHandlers,
                ret = {},
                easing, values, attr, lastFrame;

            if (elapsedTime >= duration) {
                elapsedTime = duration;
                lastFrame = true;
            }
            if (me.reverse) {
                elapsedTime = duration - elapsedTime;
            }

            for (attr in attrs) {
                if (attrs.hasOwnProperty(attr)) {
                    values = attrs[attr];
                    easing = lastFrame ? 1 : easingFn(elapsedTime / duration);
                    ret[attr] = propHandlers[attr].set(values, easing);
                }
            }
            me.frameCount++;

            return ret;
        },

        /**
         * @private
         * Perform lastFrame cleanup and handle iterations
         * @returns a hash of the new attributes.
         */
        lastFrame: function() {
            var me = this,
                iter = me.iterations,
                iterCount = me.currentIteration;

            iterCount++;
            if (iterCount < iter) {
                if (me.alternate) {
                    me.reverse = !me.reverse;
                }
                me.startTime = new Date();
                me.currentIteration = iterCount;
                // Turn off paused for CSS3 Transitions
                me.paused = false;
            }
            else {
                me.currentIteration = 0;
                me.end();
                me.fireEvent('lastframe', me, me.startTime);
            }
        },

        endWasCalled: 0,

        /**
         * Fire afteranimate event and end the animation. Usually called automatically when the
         * animation reaches its final frame, but can also be called manually to pre-emptively
         * stop and destroy the running animation.
         */
        end: function() {
            if (this.endWasCalled++) {
                return;
            }
            var me = this;
            me.startTime = 0;
            me.paused = false;
            me.running = false;
            Ext.fx.Manager.removeAnim(me);
            me.fireEvent('afteranimate', me, me.startTime);
            Ext.callback(me.callback, me.scope, [me, me.startTime]);
        },

        isReady: function() {
            return this.paused === false && this.running === false && this.iterations > 0;
        },

        isRunning: function() {
            return this.paused === false && this.running === true && this.isAnimator !== true;
        }
    });
    // Set flag to indicate that Fx is available. Class might not be available immediately.
    Ext.enableFx = true;