  /**
   *  CanvasAnimator
   *  manages smooth cropping animation
   *
   *  This object is called internally to manage animation.
   *  An in-memory div is animated and a progress callback
   *  is used to update the selection coordinates of the
   *  visible selection in realtime.
   */
  var CanvasAnimator = function(stage){
    this.stage = stage;
    this.core = stage.core;
    this.cloneStagePosition();
  };

  CanvasAnimator.prototype = {

    cloneStagePosition: function(){
      var s = this.stage;
      this.angle = s.angle;
      this.scale = s.scale;
      this.offset = s.offset;
    },

    getElement: function(){
      var s = this.stage;

      return $('<div />')
        .css({
          position: 'absolute',
          top: s.offset[0]+'px',
          left: s.offset[1]+'px',
          width: s.angle+'px',
          height: s.scale+'px'
        });
    },

    animate: function(cb){
      var t = this;

      this.scale = this.stage.boundScale(this.scale);
      t.stage.triggerEvent('croprotstart');

      t.getElement().animate({
        top: t.offset[0]+'px',
        left: t.offset[1]+'px',
        width: t.angle+'px',
        height: t.scale+'px'
      },{
        easing: t.core.opt.animEasing,
        duration: t.core.opt.animDuration,
        complete: function(){
          t.stage.triggerEvent('croprotend');
          (typeof cb == 'function') && cb.call(this);
        },
        progress: function(anim){
          var props = {}, i, tw = anim.tweens;

          for(i=0;i<tw.length;i++){
            props[tw[i].prop] = tw[i].now; }

          t.stage.setAngle(props.width)
            .setScale(props.height)
            .setOffset(props.top,props.left)
            .redraw();
        }
      });
    }

  };
  Jcrop.stage.Canvas.prototype.getAnimator = function(){
    return new CanvasAnimator(this);
  };
  Jcrop.registerComponent('CanvasAnimator',CanvasAnimator);

