//========================================================================
// ImageAutoStep - add animation controls to an image viewer
//
// To build an image viewer with animation controls:
//
// Create a form that selects the image to view. For example, you might
// have elements to specify a date or location or type of image.
//
// Write a function to read the form data and display the appropriate
// image.
//
// Write a function to step the images forward or backward by a given
// number of frames. This function should update the form elements and
// call the image display function.
//
// Create a new ImageAutoStep object and pass the name of the forward and
// backward stepping function to the constructor. 
//
// Add animation control buttons to your form and call the ImageAutoStep
// object's corresponding function from the button's onclick event
// handler. Avoid using submit buttons, either <input type=submit...> or
// <input type=image...>, use either <input type=button...> or <button
/// type=button...>
//
// Finally, the onload event handler of the image's IMG tag must call the
// animation control object's nextFrame() method.
//
//
// Here's how to include the script in the viewer page:
//
// <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
// <html>
// <head>
// ...
// <meta http-equiv="Content-Script-Type" content="text/javascript">
// <script src="javascript/ImageAutoStep.js"></script>
// ...
//
// author Ken Knowles 9-May-2005
// National Snow & Ice Data Center
// Copyright (C) 2005 University of Colorado
//
// $Header: /NSIDC_CVS/FILES/kwk/javascript/ImageAutoStep.js,v 1.10 2005/08/27 16:02:13 knowlesk Exp $
//========================================================================

//
// Constructor
//
function ImageAutoStep(frameJumpFunction) {

// 
// Arguments
//
// frameJumpFunction
//   Method to display image +/-N frames from this one
//   returns true for success, false for failure.
//   It get's called like this, status = frameJump(numFrames)
//
  this.frameJump = frameJumpFunction;

//
// configurable parameters:
//
// number of frames to jump while playing
//
  this.stepSize = 1;

//
// delay between frames determines speed of animation
//
  this.maxSpeed = 8;
  this.frameDelay = new Array(this.maxSpeed+1);
  this.frameDelay[0] = 2000; // milliseconds
  for (var speed = 1; speed <= this.maxSpeed; speed++) {
    this.frameDelay[speed] = 0.5 * this.frameDelay[speed - 1];
  }
  this.speed = this.defaultSpeed = Math.floor(this.maxSpeed/2);

//
// number of frames for rocking and looping
//
  this.loopLength = 6;

// end configurable

//
// internal state variables (should be treated as private)
//
  this.timer = null; // timer to kick-off next frameJump
  this.frameStep = 0; // 0 = stopped, negative for reverse
  this.loopFrameCounter = 0; // position within loop
  this.offsetToNextFrame = 0; // pending jumpFrame
  this.isRocking = false;
  this.isLooping = false;

//
// each instance is a property of the top level window
// and can be accessed by name
//
  if (!window.imageAutoStep) window.imageAutoStep = new Array();
  this.name = "ImageAutoStep" + window.imageAutoStep.length;
  window.imageAutoStep[this.name] = this;

}

//
// prototype methods
//
ImageAutoStep.prototype.toString = function() {
  return this.name;
}

//
// onclick for "Stop" button
// Stop animation.
//
ImageAutoStep.prototype.stop = function() {

  if (this.timer) clearTimeout(this.timer);
  this.timer = null;

  this.frameStep = 0;
  this.offsetToNextFrame = 0;
  this.loopFrameCounter = 0;

  this.isRocking = false;
  this.isLooping = false;
}

//
// onclick for "Next Image" button
// Single step forward to display next image in sequence.
//
ImageAutoStep.prototype.next = function() {
  this.stop();
  this.frameJump(1);
}

//
// onclick for "Previous Image" button
// Single step backwards to display previous image in sequence.
//
ImageAutoStep.prototype.prev = function() {
  this.stop();
  this.frameJump(-1);
}

//
// set the animation speed
//
ImageAutoStep.prototype.setSpeed = function(speed) {

  if (speed < 0) speed = 0;
  if (speed > this.maxSpeed) speed = this.maxSpeed;

  this.speed = speed;
}

//
// onclick for "Faster" button
// Reduce delay between frames, but only while playing.
//
ImageAutoStep.prototype.faster = function() {
  this.setSpeed(this.speed + 1);
}

//
// onclick for "Slower" button
// Increase delay between frames, but only while playing.
//
ImageAutoStep.prototype.slower = function() {
  this.setSpeed(this.speed - 1);
}

//
// onclick for "Forward" button
// Play animation forward at the current speed.
//
ImageAutoStep.prototype.fwd = function() {
  this.stop();
  this.frameStep = this.stepSize;
  this.nextFrame();
}

//
// onclick for "Forward Play" button
// Play animation forward at normal speed
//
ImageAutoStep.prototype.fwdPlay = function() {
  this.setSpeed(this.defaultSpeed);
  this.fwd();
}

//
// onclick for "Fast Forward" button
// Each button push speeds up forward play.
//
ImageAutoStep.prototype.fwdFast = function() {
    this.fwd();
    this.faster();
}

//
// onclick for "Slow Forward" button
// Each button push slows down forward play.
//
ImageAutoStep.prototype.fwdSlow = function() {
    this.fwd();
    this.slower();
}

//
// onclick for "Reverse" button
// Play animation backward at the current speed.
//
ImageAutoStep.prototype.rev = function() {
  this.stop();
  this.frameStep = -this.stepSize;
  this.nextFrame();
}

//
// onclick for "Reverse Play" button
// Play animation backward at normal speed.
//
ImageAutoStep.prototype.revPlay = function() {
  this.setSpeed(this.defaultSpeed);
  this.rev();
}

//
// onclick for "Fast Reverse" button
// Each button push speeds up backward play.
//
ImageAutoStep.prototype.revFast = function() {
    this.rev();
    this.faster();
}

//
// onclick for "Slow Reverse" button
// Each button push slows down backward play.
//
ImageAutoStep.prototype.revSlow = function() {
    this.rev();
    this.slower();
}

//
// onclick for "Rock" button
// Play animation back and forth over a few frames.
//
ImageAutoStep.prototype.rock = function() {
  this.stop();
  this.frameStep = this.stepSize;
  this.isRocking = true;
  this.nextFrame();
}

//
// onclick for "Loop" button
// Play animation in a loop over a few frames.
//
ImageAutoStep.prototype.loop = function() {
  this.stop();
  this.frameStep = this.stepSize;
  this.isLooping = true;
  this.nextFrame();
}

//
// onload for IMG
// handles timing for animation sequence
//
ImageAutoStep.prototype.nextFrame = function() {
  var action;
  var next_loopFrameCounter;

//
// nothing to do if not in some play mode
//
  if (0 == this.frameStep) return;

//
// to avoid multiple animation sequences clear any pending timer
//
  if (this.timer) clearTimeout(this.timer);
  this.timer = null;

//
// only get a new offset if there isn't one already
//
  if (0 == this.offsetToNextFrame) {
//
// for rocking, reverse direction when next frame would be out-of-bounds
//
    next_loopFrameCounter = this.loopFrameCounter + this.frameStep;

    if (this.isRocking) {
      if ((this.frameStep > 0 && next_loopFrameCounter >= this.loopLength) ||
	  (this.frameStep < 0 && next_loopFrameCounter < 0)) {

// reverse direction
	this.frameStep = -this.frameStep;
      }
      this.offsetToNextFrame = this.frameStep;

//
// for looping, return to beginning when next frame would be out-of-bounds
//
    } else if (this.isLooping) {
      if (next_loopFrameCounter >= this.loopLength) {

// jump backwards in sync with frameStep
	this.offsetToNextFrame = -this.loopFrameCounter;

      } else {
	this.offsetToNextFrame = this.frameStep;
      }

//
// for normal play mode just go with the next frame
//
    } else {
      this.offsetToNextFrame = this.frameStep;
    }
  }

//  debugMsg("nextFrame: counter="+this.loopFrameCounter+" step="+this.frameStep+"offset="+this.offsetToNextFrame);

//
// Set a timer to load the next frame. This function--nextFrame()--should
// be the onload attribute of the IMG tag, and so, will be called again
// when the timer goes off and the image is loaded.
//
  this.timer = setTimeout("playImageAutoStep('" + this + "')", 
			  this.frameDelay[this.speed]);

}

//
// event handler for timer call
//
function playImageAutoStep(playerName) {
  var player = window.imageAutoStep[playerName];
  var thereIsMore = true;

  if (0 == player.offsetToNextFrame) return true;

// jump to next frame 
  thereIsMore = player.frameJump(player.offsetToNextFrame);
  player.loopFrameCounter += player.offsetToNextFrame;
  player.offsetToNextFrame = 0;

  if (!thereIsMore) player.stop();
}
