    /**
     * @class Ext.fx.Manager
     * Animation Manager which keeps track of all current animations and manages them on a frame by frame basis.
     * @private
     * @singleton
     */

    Ext.define('Ext.fx.Manager', {

        /* Begin Definitions */

        singleton: true,

        requires: ['Ext.util.MixedCollection',
                   'Ext.fx.target.Element',
                   'Ext.fx.target.CompositeElement',
                   'Ext.fx.target.Sprite',
                   'Ext.fx.target.CompositeSprite',
                   'Ext.fx.target.Component'],

        mixins: {
            queue: 'Ext.fx.Queue'
        },

        /* End Definitions */

        constructor: function() {
            this.items = Ext.create('Ext.util.MixedCollection');
            this.mixins.queue.constructor.call(this);

            // this.requestAnimFrame = (function() {
            //     var raf = window.requestAnimationFrame ||
            //               window.webkitRequestAnimationFrame ||
            //               window.mozRequestAnimationFrame ||
            //               window.oRequestAnimationFrame ||
            //               window.msRequestAnimationFrame;
            //     if (raf) {
            //         return function(callback, element) {
            //             raf(callback);
            //         };
            //     }
            //     else {
            //         return function(callback, element) {
            //             window.setTimeout(callback, Ext.fx.Manager.interval);
            //         };
            //     }
            // })();
        },

        /**
         * @cfg {Number} interval Default interval in miliseconds to calculate each frame.  Defaults to 16ms (~60fps)
         */
        interval: 16,

        /**
         * @cfg {Boolean} forceJS Turn off to not use CSS3 transitions when they are available
         */
        forceJS: true,

        // @private Target factory
        createTarget: function(target) {
            var me = this,
                useCSS3 = !me.forceJS && Ext.supports.Transitions,
                targetObj;

            me.useCSS3 = useCSS3;

            // dom id
            if (Ext.isString(target)) {
                target = Ext.get(target);
            }
            // dom element
            if (target && target.tagName) {
                target = Ext.get(target);
                targetObj = Ext.create('Ext.fx.target.' + 'Element' + (useCSS3 ? 'CSS' : ''), target);
                me.targets.add(targetObj);
                return targetObj;
            }
            if (Ext.isObject(target)) {
                // Element
                if (target.dom) {
                    targetObj = Ext.create('Ext.fx.target.' + 'Element' + (useCSS3 ? 'CSS' : ''), target);
                }
                // Element Composite
                else if (target.isComposite) {
                    targetObj = Ext.create('Ext.fx.target.' + 'CompositeElement' + (useCSS3 ? 'CSS' : ''), target);
                }
                // Draw Sprite
                else if (target.isSprite) {
                    targetObj = Ext.create('Ext.fx.target.Sprite', target);
                }
                // Draw Sprite Composite
                else if (target.isCompositeSprite) {
                    targetObj = Ext.create('Ext.fx.target.CompositeSprite', target);
                }
                // Component
                else if (target.isComponent) {
                    targetObj = Ext.create('Ext.fx.target.Component', target);
                }
                else if (target.isAnimTarget) {
                    return target;
                }
                else {
                    return null;
                }
                me.targets.add(targetObj);
                return targetObj;
            }
            else {
                return null;
            }
        },

        /**
         * Add an Anim to the manager. This is done automatically when an Anim instance is created.
         * @param {Ext.fx.Anim} anim
         */
        addAnim: function(anim) {
            var items = this.items,
                task = this.task;
            // var me = this,
            //     items = me.items,
            //     cb = function() {
            //         if (items.length) {
            //             me.task = true;
            //             me.runner();
            //             me.requestAnimFrame(cb);
            //         }
            //         else {
            //             me.task = false;
            //         }
            //     };

            items.add(anim);

            // Start the timer if not already running
            if (!task && items.length) {
                task = this.task = {
                    run: this.runner,
                    interval: this.interval,
                    scope: this
                };
                Ext.TaskManager.start(task);
            }

            // //Start the timer if not already running
            // if (!me.task && items.length) {
            //     me.requestAnimFrame(cb);
            // }
        },

        /**
         * Remove an Anim from the manager. This is done automatically when an Anim ends.
         * @param {Ext.fx.Anim} anim
         */
        removeAnim: function(anim) {
            // this.items.remove(anim);
            var items = this.items,
                task = this.task;
            items.remove(anim);
            // Stop the timer if there are no more managed Anims
            if (task && !items.length) {
                Ext.TaskManager.stop(task);
                delete this.task;
            }
        },

        /**
         * @private
         * Filter function to determine which animations need to be started
         */
        startingFilter: function(o) {
            return o.paused === false && o.running === false && o.iterations > 0;
        },

        /**
         * @private
         * Filter function to determine which animations are still running
         */
        runningFilter: function(o) {
            return o.paused === false && o.running === true && o.isAnimator !== true;
        },

        /**
         * @private
         * Runner function being called each frame
         */
        runner: function() {
            var me = this,
                items = me.items;

            me.targetData = {};
            me.targetArr = {};

            // Single timestamp for all animations this interval
            me.timestamp = new Date();

            // Start any items not current running
            items.filterBy(me.startingFilter).each(me.startAnim, me);

            // Build the new attributes to be applied for all targets in this frame
            items.filterBy(me.runningFilter).each(me.runAnim, me);

            // Apply all the pending changes to their targets
            me.applyPendingAttrs();
        },

        /**
         * @private
         * Start the individual animation (initialization)
         */
        startAnim: function(anim) {
            anim.start(this.timestamp);
        },

        /**
         * @private
         * Run the individual animation for this frame
         */
        runAnim: function(anim) {
            if (!anim) {
                return;
            }
            var me = this,
                targetId = anim.target.getId(),
                useCSS3 = me.useCSS3 && anim.target.type == 'element',
                elapsedTime = me.timestamp - anim.startTime,
                target, o;

            this.collectTargetData(anim, elapsedTime, useCSS3);

            // For CSS3 animation, we need to immediately set the first frame's attributes without any transition
            // to get a good initial state, then add the transition properties and set the final attributes.
            if (useCSS3) {
                // Flush the collected attributes, without transition
                anim.target.setAttr(me.targetData[targetId], true);

                // Add the end frame data
                me.targetData[targetId] = [];
                me.collectTargetData(anim, anim.duration, useCSS3);

                // Pause the animation so runAnim doesn't keep getting called
                anim.paused = true;

                target = anim.target.target;
                // We only want to attach an event on the last element in a composite
                if (anim.target.isComposite) {
                    target = anim.target.target.last();
                }

                // Listen for the transitionend event
                o = {};
                o[Ext.supports.CSS3TransitionEnd] = anim.lastFrame;
                o.scope = anim;
                o.single = true;
                target.on(o);
            }
            // For JS animation, trigger the lastFrame handler if this is the final frame
            else if (elapsedTime >= anim.duration) {
                me.applyPendingAttrs(true);
                delete me.targetData[targetId];
                delete me.targetArr[targetId];
                anim.lastFrame();
            }
        },

        /**
         * Collect target attributes for the given Anim object at the given timestamp
         * @param {Ext.fx.Anim} anim The Anim instance
         * @param {Number} timestamp Time after the anim's start time
         */
        collectTargetData: function(anim, elapsedTime, useCSS3) {
            var targetId = anim.target.getId(),
                targetData = this.targetData[targetId],
                data;

            if (!targetData) {
                targetData = this.targetData[targetId] = [];
                this.targetArr[targetId] = anim.target;
            }

            data = {
                duration: anim.duration,
                easing: (useCSS3 && anim.reverse) ? anim.easingFn.reverse().toCSS3() : anim.easing,
                attrs: {}
            };
            Ext.apply(data.attrs, anim.runAnim(elapsedTime));
            targetData.push(data);
        },

        /**
         * @private
         * Apply all pending attribute changes to their targets
         */
        applyPendingAttrs: function(isLastFrame) {
            var targetData = this.targetData,
                targetArr = this.targetArr,
                targetId;
            for (targetId in targetData) {
                if (targetData.hasOwnProperty(targetId)) {
                    targetArr[targetId].setAttr(targetData[targetId], false, isLastFrame);
                }
            }
        }
    });