function AnimationFrame(left, top, width, height, time)
{
  this.Left = left;
  this.Top = top;
  this.Width = width;
  this.Height = height;
  this.Time = time;
 
  this.Copy = function(frame)
  {
    this.Left = frame.Left;
    this.Top = frame.Top;
    this.Width = frame.Width;
    this.Height = frame.Height;
    this.Time = frame.Time;
  }
 
  this.Apply = function(element)
  {
    element.style.left = Math.round(this.Left) + 'px';
    element.style.top = Math.round(this.Top) + 'px';
    element.style.width = Math.round(this.Width) + 'px';
    element.style.height = Math.round(this.Height) + 'px';
  }
}

function AnimationObject(element)
{
  if(typeof(element) == "string")
    element = document.getElementById(element);
 
  var frames = null; 
  var timeoutID = -1;
  var running = 0;
  var currentFI = 0;
  var currentData = null;
  var lastTick = -1;
  var callback = null;
 
  var prevDir = 0;
 
  this.AddFrame = function(frame)
  {
    frames.push(frame);
  }
 
  this.SetCallback = function(cb)
  {
    callback = cb;
  }
 
  this.ClearFrames = function()
  {
    if(running != 0)
      this.Stop();
    frames = new Array();
    frames.push(new AnimationFrame(0,0,0,0,0));
    frames[0].Time = 0;
    frames[0].Left = parseInt(element.style.left);
    frames[0].Top = parseInt(element.style.top);
    frames[0].Width = parseInt(element.style.width);
    frames[0].Height = parseInt(element.style.height);
    currentFI = 0;
    prevDir = 0;
    currentData = new AnimationFrame(0,0,0,0,0);   
  }
 
  this.ResetToStart = function()
  {
    if(running != 0)
      this.Stop();
    currentFI = 0;
    prevDir = 0;
    currentData = new AnimationFrame(0,0,0,0,0);
    frames[0].Apply(element);
  }
 
  this.ResetToEnd = function()
  {
    if(running != 0)
      this.Stop();
    currentFI = 0;
    prevDir = 0;
    currentData = new AnimationFrame(0,0,0,0,0);
    frames[frames.length - 1].Apply(element);
  }
 
  this.Stop = function()
  {
    if(running == 0)
      return;
    if(timeoutID != -1)
      clearTimeout(timeoutID);
    prevDir = running;
    running = 0;
  }
 
  this.RunForward = function()
  {
    if(running == 1)
      return;
    if(running == -1)
      this.Stop();
    if(frames.length == 1 || element == null)
      return; 
     
    lastTick = new Date().getTime();

    //Start from the begining
    if(prevDir == 0)
    {
      currentFI = 1;
      currentData.Time = 0;
      currentData.Left = parseInt(element.style.left);
      currentData.Top = parseInt(element.style.top);
      currentData.Width = parseInt(element.style.width);
      currentData.Height = parseInt(element.style.height);
      frames[0].Copy(currentData);
    }
    else if(prevDir != 1)
    {
      currentFI++;
      currentData.Time =
          frames[currentFI].Time - currentData.Time;
    }
     
    running = 1;
    animate();
  }
 
  this.RunBackward = function()
  {
    if(running == -1)
      return;
    if(running == 1)
      this.Stop();
    if(frames.length == 1 || element == null)
      return;
       
    lastTick = new Date().getTime();
   
    //Start from the end
    if(prevDir == 0)
    {
      currentFI = frames.length-2;
      currentData.Left = parseInt(element.style.left);
      currentData.Top = parseInt(element.style.top);
      currentData.Width = parseInt(element.style.width);
      currentData.Height = parseInt(element.style.height);
      currentData.Time = frames[frames.length-1].Time;
      frames[frames.length-1].Copy(currentData);
      currentData.Time = 0;
    }
    else if(prevDir != -1)
    {
      currentData.Time =
          frames[currentFI].Time - currentData.Time;
      currentFI--;
    }
     
    running = -1;
    animate();
  }
   
  function animate()
  {
    if(running == 0)
      return;
    var curTick = new Date().getTime();
    var tickCount = curTick - lastTick;
    lastTick = curTick;
   
    var timeLeft =
       frames[((running == -1) ? currentFI+1 : currentFI)].Time
       - currentData.Time;
   
    while(timeLeft <= tickCount)
    {
      currentData.Copy(frames[currentFI]);
      currentData.Time = 0;
      currentFI += running;
      if(currentFI>= frames.length || currentFI <0)
      {
        currentData.Apply(element);
        lastTick = -1;
        running = 0;
        prevDir = 0;
        if(callback != null)
          callback();
        return;
      }
      tickCount = tickCount - timeLeft;
      timeLeft =
        frames[((running == -1) ? currentFI+1 : currentFI)].Time
        - currentData.Time;
    }
   
    if(tickCount != 0)
    {
      currentData.Time += tickCount;
      var ratio = currentData.Time/
        frames[((running == -1) ? currentFI+1 : currentFI)].Time;

      currentData.Left = frames[currentFI-running].Left +
         (frames[currentFI].Left
         - frames[currentFI-running].Left)
         * ratio;

      currentData.Top = frames[currentFI-running].Top +
         (frames[currentFI].Top
         - frames[currentFI-running].Top)
         * ratio;
      currentData.Width = frames[currentFI-running].Width +
         (frames[currentFI].Width
         - frames[currentFI-running].Width)
         * ratio;

      currentData.Height = frames[currentFI-running].Height +
         (frames[currentFI].Height
         - frames[currentFI-running].Height)
         * ratio;
    }
   
    currentData.Apply(element);

    timeoutID = setTimeout(animate, 33);
  }
 
  this.ClearFrames();
}