//# POC:             510
//# PVCS  ID:        %W% %G%
//# Version		 1.5 27Dec02
//# Approved By:     Webmaster
//# Classification:  Unclassified
//# Filename:  /HTDOCS/PUBLIC/UTIL/loop.js
//
// Javascript Animation Player
//
// Mark Hadfield (m.hadfield@niwa.cri.nz) April 2000
// Extensive mods by LT David Fuller, 
//    Fleet Numerical, U.S. Navy
//    May 2001
//
// More modifications made by AG1(AW/SW) Christopher
//    Fleet Numerical, U.S. Navy
//    September2002
// 	* Important Changes
//		* To use this looper, you must create an Array object
// 		* containing the URL of the files to be looped thru
//  *06 Dec 02 - added funciton Swapper(int); Allow the user
//  * to control the images with a row of buttons
//
//
//
//
// This JavaScript script animates a series of image files
// in an HTML document.
//
// I modelled it on a similar script
// used in the CDC map room (http://www.cdc.noaa.gov/~map/maproom/).
// The original is marked (c)BASTaRT, Praha, Czech Republic,
// 1996 (current contact Martin Holeeko <martin@etnetera.cz>)
// and was modified by D. Watson and A. Earnhart (CIRA/CSU)
// July 1997 and Greg Thompson (NCAR/RAP) December 1997.
//
// This version is heavily modified and shares very little code with
// the original. Design considerations include:
//    * Script is self-contained and is invoked via the SRC property
//      of a SCRIPT element in the document header.
//    * The animator code is packaged in a JavaScript object,
//      allowing more than one animator object per HTML document.
//    * Animator user interface now written by the script (method
//      Write).
//    * UI is constructed with HTML elements (INPUT & SELECT)
//      rather than images.
//    * The document invoking the script needs only a few SCRIPT
//      elements in the body to modify the script's global variables,
//      generate the user interface and initiate playback.
//    * HTML code generated by the script is more-or-less compliant
//      with HTML 4.01 and XHTML 1.0.
//    * I tried to simplify the playback control code using logic
//      I developed for an IDL animator class.
//    * The script has been tested on Netscape 4.7 (also occasionally
//      with 4.08) and Internet Explorer 5.0.

// Global variable used to assign each Animator object a unique ID.
var animator_counter = 1;

function Animator()
{
  // Assign each animator object a unique ID number
  this.id = animator_counter++;

  // Specify default properties. These can be overridden
  // by <script> elements in the HTML document

  this.image_root = "image";   // Root for image URL
  
  this.n_frames = 10;          // Number of frames
  

  this.image_width = 100;      // Width of image area
  this.image_height = 100;     // Height of image area

  this.mode = 1;               // Mode (0=once, 1=repeat, 2=autoreverse)
  this.active = false;         // false=stopped, true=playing
  this.direction = 1;          // 0=backward, 1=forward
  this.delay = 400;             // Interframe delay (ms)
  this.current = null;         // Current frame number
  this.next = null;            // Next frame number
  
   
  // This code was added as a temporary work around for a problem with
  // client browsers retrieving the images with each loop in of caching the images

  this.loop_start = this.current; // frame loop started on
  this.max_loop = 50;          // Maximum number of loop iterations before it needs to be stopped
                               // This will help stop problems with cache less browsers
  this.consec_loop = 0;        // how many times has the loop run

  // *******************************************************************************
  
  this.delay_first = 500;      // Extra delay (ms) on first frame
  this.delay_last = 500;       // Extra delay (ms) on last frame

  this.fcst_begin = 0;         // Used in generating URLs for the image
  this.fcst_increment = 1;     //   files

  this.timeID = null;          // Timeout identifier
  
  // *********************** variables used for omitting images ****************
  this.omitImages = false;     // boolean value to set if the loop will be able to omit images or not
                               // true = yes it will false = no
  
  this.imageOmit;              // will be defined as an array to indicate whether to omit the 
                               // image from the loop
                               
  // ***************************************************************************                               
  
  // Specify methods (function definitions below)

  this.FrameURL = FrameURL;     // Generate the URL for each frame
  
  this.Load = Load;             // Load images
  // Write HTML code for the animator and user interface
  this.Write = Write;           
  this.Write2 = Write2;           
  this.form_name = "";
  this.form = "";
  this.img_name = "";
  this.img = "";
  


  this.Display = Display;       // Playback methods
  this.Play = Play;
  this.PlayNext = PlayNext;
  this.Stop = Stop;
  this.StopStart = StopStart;
  this.Swapper = Swapper;       // Piano key mouseover image selector
  this.ImageOmit = ImageOmit;
  //this.OmitImage = OmitImage;   // Omits or unOmits images from loop

  this.Initialize = Initialize; // Call Load, Write and Display(1)
  this.Initialize2 = Initialize2; // Call Load, Write and Display(1)
} // End of Animator constructor

  // Define functions to be used as methods for animator object.

  // Load images into the animator object
  function Load()
  {
    // Generate an array of Image objects and set the src property for each one.
    // This causes the corresponding image files to be preloaded and allows for
    // rapid transfer of the image data into the animator's IMG element
    // when the time comes to display it.

    // Pre-allocate the array
    this.images = new Array(this.n_frames);
    if(this.omitImages)
    {
        this.imageOmit = new Array(this.n_frames);
    }

    // Load the images
    for (var i = 0; i < this.n_frames; i++)
    {
      this.images[i] = new Image();
      this.images[i].src = this.FrameURL(i);
      if(this.omitImages)
      {
        this.imageOmit[i] = false;
      }
    }
  }

  //Return URL for specified frame
  function FrameURL(num)
  {
          // this.files[] is an array created by the 
          // calling script with the images to loop thru
          return this.image_root + this.files[num];
  }
  
  // Write HTML code for the animator into the current document
  function Write()
  {
    // The user interface and the animation images are displayed in a table. The original
    // user interface was constructed largely from graphical images but I have rewritten it
    // using HTML 4 elements like <SELECT> and <INPUT> with text labels. The advantage of this
    // is that these elements respond better to interaction with the mouse.
    
    // The user interface is embedded in a <FORM> element. A reference to the current animator object
    // is attached to this form element below. The event handlers access the animator via this reference.
    
    // Generate names for the <FORM> and <IMG> elements. These must be unique in the document.
  
    this.form_name = 'form_animator_'+this.id
    this.img_name = 'img_animator_'+this.id
    // The above HTML code has created (amongst other things) a Form object
    // (the <FORM> element) and an Image object (the <IMG> element). We save
    // references to these in the current animator object and we save a
    // reference to the animator in the form for use in event handlers
  }

  function Write2() {
    this.form = document.forms[this.form_name];
    this.img = document.images[this.img_name];
    //this.img = document.images[0];
    
    this.form.animator = this;
    
    // Attach event handlers to the user interface elements. The form is referred to
    // as "this.form" (remembering that "this" for the event handlers refers to the element that
    // originates the event).
    
    this.form.select_mode.onchange = new Function("this.form.animator.mode=this.selectedIndex;")
    
    this.form.button_bwrd.onclick = new Function("this.form.animator.direction=0; this.form.animator.Play();")
    this.form.button_stop.onclick = new Function("this.form.animator.Stop();")
    this.form.button_fwrd.onclick = new Function("this.form.animator.direction=1; this.form.animator.Play();")
    
    this.form.button_first.onclick = new Function("this.form.animator.Stop(); this.form.animator.Display(1);")
    this.form.button_prev.onclick = new Function("this.form.animator.Stop(); this.form.animator.Display(this.form.animator.current-1);")
    this.form.button_next.onclick = new Function("this.form.animator.Stop(); this.form.animator.Display(this.form.animator.current+1);")
    this.form.button_last.onclick = new Function("this.form.animator.Stop(); this.form.animator.Display(this.form.animator.n_frames);")
    
    if(this.omitImages) // if we are using the checkboxes to allow the user to omit images from the loop 
    {                   // we need to set up event listeners for their onclick value
        for(i = 1; i <= this.n_frames; i++)
        {
            this.form.elements[12 + i].onclick = new Function("this.form.animator.ImageOmit(this.form.elements[" + (12 + i) + "].checked," + i + ");")
        }     
    }
    
    this.form.input_delay.onfocus = new Function("this.form.animator.Stop(); this.select();")
    this.form.input_delay.onchange = new Function("this.form.animator.delay=parseInt(this.value);")
    
    this.form.input_delay_first.onfocus = new Function("this.form.animator.Stop(); this.select();")
    this.form.input_delay_first.onchange = new Function("this.form.animator.delay_first=parseInt(this.value);")
    
    this.form.input_delay_last.onfocus = new Function("this.form.animator.Stop(); this.select();")
    this.form.input_delay_last.onchange = new Function("this.form.animator.delay_last=parseInt(this.value);")
    
    this.form.input_current.onfocus = new Function("this.form.animator.Stop(); this.select();")
    this.form.input_current.onchange = new Function("this.form.animator.Display(parseInt(this.value));")
    
    this.form.input_n_frames.onchange = new Function("this.value=this.form.animator.n_frames;")

    // Set up an "onclick" event handler for the <IMG> element here (this works in Internet Explorer
    // but is ignored by Netscape). This element is not a child of the <FORM> so we locate the form in the
    // "forms" property of the document object (c.f. the PlayNext method).

    this.img.onclick=new Function("document.forms['"+this.form.name+"'].animator.StopStart();")
    
    // Set values of UI elements. The only parameters that cannot be set after initialisation
    // like this are width and height of the image area.
    
    this.form.select_mode.selectedIndex = this.mode;
    this.form.input_n_frames.value = this.n_frames;
    this.form.input_delay.value = this.delay;
    this.form.input_delay_first.value = this.delay_first;
    this.form.input_delay_last.value = this.delay_last;
  }

  
  
  // Display specified frame
  function Display(num)
  {
    num = Number(num);
  
    if (num < 1){ num = this.n_frames;}

    if (num > this.n_frames){ num = 1;}

    this.current = num;
  
    this.img.src = this.images[this.current-1].src; // Display image
  
    this.form.input_current.value = this.current;   // Display frame number
  }
  
  // Initiate playback
  function Play()
  {
    this.Stop();
    this.next = this.current;
    this.loop_start = this.current;
    this.consec_loop = 0;
    this.active = true;
    this.PlayNext();
  }

  // Play next frame
  function PlayNext()
  {
    if (this.active)
    {
      if(this.current == this.loop_start)
      {
      	this.consec_loop += 1;

	 }
      if (this.consec_loop == this.max_loop)
      {
      	alert("The loop has run for 50 consecutive loops.\nPlease hit the play button if you wish to continue.");
		this.Stop();
      }

      else if(this.omitImages)
      {
        // check to see if the image is to be omitted
        while(this.imageOmit[this.next] == true)
        {
            this.next = this.next + (-1+2*this.direction);
        }
        
	 	this.current = this.next;
      	this.next = this.current + (-1+2*this.direction)

      	this.Display(this.current);

      	delay_time = this.delay;
      	if (this.current == 1) delay_time = delay_time + this.delay_first;
      	if (this.current == this.n_frames) delay_time = delay_time + this.delay_last;

      	// This is the string that is passed to setTimeout so that this method will
      	// be called again. It is processed at global level (I think) so
      	// the animator is identified via the form's animator property.
      	command = "document.forms['"+this.form.name+"'].animator.PlayNext()";

      	switch (this.mode)
      	{
       		case 0:
          	if ((this.next >= 1) && (this.next <= this.n_frames))
          	{
            		this.timeID = window.setTimeout(command, delay_time);
          	}
          	else this.Stop();
          	break;
        	case 1:
          	switch(this.direction)
          	{
            		case 0:
              			if (this.next < 1) this.next = this.n_frames;
              			break;
            		case 1:
              			if (this.next > this.n_frames) this.next = 1;
              			break;
          	}
          	this.timeID = window.setTimeout(command, delay_time);
          	break;
        	case 2:
          	if (this.next < 1)
          	{
            		this.direction = 1;
            		this.next = 2;
          	}
          	if (this.next > this.n_frames)
          	{
            		this.direction = 0;
            		this.next = this.n_frames - 1;
          	}
          	this.timeID = window.setTimeout(command, delay_time);
          	break;
      	} //end switch (this.mode)
       } // end else if
       else
       {
        this.current = this.next;
      	this.next = this.current + (-1+2*this.direction)

      	this.Display(this.current);

      	delay_time = this.delay;
      	if (this.current == 1) delay_time = delay_time + this.delay_first;
      	if (this.current == this.n_frames) delay_time = delay_time + this.delay_last;

      	// This is the string that is passed to setTimeout so that this method will
      	// be called again. It is processed at global level (I think) so
      	// the animator is identified via the form's animator property.
      	command = "document.forms['"+this.form.name+"'].animator.PlayNext()";

      	switch (this.mode)
      	{
       		case 0:
          	if ((this.next >= 1) && (this.next <= this.n_frames))
          	{
            		this.timeID = window.setTimeout(command, delay_time);
          	}
          	else this.Stop();
          	break;
        	case 1:
          	switch(this.direction)
          	{
            		case 0:
              			if (this.next < 1) this.next = this.n_frames;
              			break;
            		case 1:
              			if (this.next > this.n_frames) this.next = 1;
              			break;
          	}
          	this.timeID = window.setTimeout(command, delay_time);
          	break;
        	case 2:
          	if (this.next < 1)
          	{
            		this.direction = 1;
            		this.next = 2;
          	}
          	if (this.next > this.n_frames)
          	{
            		this.direction = 0;
            		this.next = this.n_frames - 1;
          	}
          	this.timeID = window.setTimeout(command, delay_time);
          	break;
      	} //end switch (this.mode)
       } // end else

     }// end if
  } // end PlayNext()


  // Stop the animation
  function Stop()
  {
    if (this.active)
    {
         window.clearTimeout(this.timeID);
    }
    this.active = false;
    this.consec_loop = 0;
  }

  // Stop or restart the animation
  function StopStart()
  {
    this.consec_loop = 0;
    if (this.active) this.Stop(); else this.Play();
  }
  
  //Stop loop and display single image
  function Swapper(num)
  {
    this.Stop();
    this.Display(num);
  }
  
  //Omits images from the loop
  function ImageOmit(Status, i)
  {
    if (Status == true)
        this.imageOmit[i] = true;
    else
      this.imageOmit[i] = false;
  }
    
  // Initialize
  function Initialize()
  {
    this.Load();
    this.Write();
  }
  
  function Initialize2()
  {
    this.Write2();
    this.Display(1);
  }
  
