text: /**
   * 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', {
      mixins: {
          observable: 'Ext.util.Observable'
      },
  
      requires: [
          'Ext.fx.Manager',
          'Ext.fx.Animator',
          'Ext.fx.Easing',
          'Ext.fx.CubicBezier',
          'Ext.fx.PropertyHandler'
      ],
  
      /**
       * @property {Boolean} isAnimation
       * `true` in this class to identify an object as an instantiated Anim, or subclass thereof.
       */
      isAnimation: true,
  
      /**
       * @cfg {Function/String} callback
       * A function to be run after the animation has completed.
       * @controllable
       */
  
      /**
       * @cfg {Object} scope
       * The scope that the {@link #callback} function will be called with
       */
  
      /**
       * @cfg {Boolean} remove
       * `true` to remove the target when the animation is complete, using the appropriate removal
       * method for the target. For example, a component will be destroyed, elements will be removed.
       */
  
      /**
       * @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
       * Tracks 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 un-paused 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} autoEnd
       * `true` to immediately force this animation to its final state. This can be useful
       * in cases where you want the final effect of an animation, but need to the actual
       * animation dynamically. Also see the {@link #jumpToEnd} method.
       */
      autoEnd: false,
  
      /**
       * @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,
  
      /**
       * @event beforeanimate
       * Fires before the animation starts. A handler can return false to cancel the animation.
       * @param {Ext.fx.Anim} this
       */
  
      /**
       * @event afteranimate
       * Fires when the animation is complete.
       * @param {Ext.fx.Anim} this
       * @param {Date} startTime
       */
  
      /**
       * @event lastframe
       * Fires when the animation's last frame has been set.
       * @param {Ext.fx.Anim} this
       * @param {Date} startTime
       */
  
      /**
       * @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.mixins.observable.constructor.call(me);
  
          Ext.fx.Manager.addAnim(me);
  
          if (config.autoEnd) {
              me.running = true;
              me.jumpToEnd();
          }
      },
  
      /**
       * @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;
          }
      },
  
      /**
       * Immediately force this animation to its final state.
       */
      jumpToEnd: function(suppressEvent) {
          var me = this;
  
          if (!me.endWasCalled) {
              if (!me.currentAttrs) {
                  me.initAttrs();
              }
  
              Ext.fx.Manager.jumpToEnd(me);
              me.end(suppressEvent);
          }
      },
  
      /**
       * @private
       * Calculate attribute value at the passed timestamp.
       * @return 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
       * @return 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 preemptively
       * stop and destroy the running animation.
       */
      end: function(suppressEvent) {
          var me = this;
  
          if (me.endWasCalled++) {
              return;
          }
  
          me.startTime = 0;
          me.paused = false;
          me.running = false;
          Ext.fx.Manager.removeAnim(me);
  
          if (!suppressEvent) {
              me.fireEvent('afteranimate', me, me.startTime);
              Ext.callback(me.callback, me.scope, [me, me.startTime]);
          }
  
          if (me.remove) {
              me.target.destroy();
          }
      },
  
      isReady: function() {
          return this.paused === false && this.running === false && this.iterations > 0;
      },
  
      isRunning: function() {
          return this.paused === false && this.running === true && this.isAnimator !== true;
      }
  });
  
  /**
   * @member Ext
   * @property {Boolean} enableFx
   * True if the {@link Ext.fx.Anim} Class is available.
   */
  Ext.enableFx = true; // Indicate that Fx is available. Class might not be available immediately.
  