//
// Mark Hadfield (m.hadfield@niwa.cri.nz) April 2000
//
// 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.image_ext = ".png";     // Extension for image URL
  this.n_frames = 10;          // Number of frames
  this.first_frame = 0;        // First frame to show

  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 = 70;             // Interframe delay (ms)
  this.current = null;         // Current frame number
  this.next = null;            // Next frame number

  this.delay_first = 0;      // Extra delay (ms) on first frame
  this.delay_last = 2000;       // 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
  
  // Specify methods (function definitions below)

  this.FrameURL = FrameURL;     // Generate the URL for each frame
  
  this.Load = Load;             // Load images
  this.Write = Write;           // Write HTML code for the animator and user interface
  
  this.Display = Display;       // Playback methods
  this.Play = Play;
  this.PlayNext = PlayNext;
  this.Stop = Stop;
  this.StopStart = StopStart;

  this.Initialize = Initialize; // Call Load, Write and Display(1)

  // 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
// TPTEST    this.images = new Array(this.n_frames);
    this.images = new Array(this.n_frames - this.first_frame);


    // Load the images
// TPTEST    for (var i = 1; i <= this.n_frames; i++)
    for (var i = 1; i <= this.n_frames - this.first_frame; i++)
    {
      this.images[i-1] = new Image();
	// TPTEST      this.images[i-1].src = this.FrameURL(i);
      this.images[i-1].src = this.FrameURL(i + this.first_frame);
    }
  }
  
  //Return URL for specified frame
  function FrameURL(num)
  {
    num = Number(num);
  
    if (this.fcst_begin > 0)
      var fcst_length = this.fcst_begin+(num-1)*this.fcst_increment;
    else
      var fcst_length = (num-1)*this.fcst_increment;


    if (fcst_length < 10)
      return this.image_root + "000" + fcst_length + this.image_ext;
    else if(fcst_length < 100)
      return this.image_root + "00" + fcst_length + this.image_ext;
    else if(fcst_length < 1000)
      return this.image_root + "0" + fcst_length + this.image_ext;
    else
      return this.image_root + fcst_length + this.image_ext;

// TPTEST USE TIMESTAMPS
//    var basename;
//    if (fcst_length < 10)
//      basename = this.image_root + "000" + "-";
//    else if(fcst_length < 100)
//      basename = this.image_root + "00" + "-";
//    else if(fcst_length < 1000)
//      basename = this.image_root + "0" + "-";
//    else
//      basename = this.image_root + fcst_length;


  }
  
  // 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.
  
    form_name = 'form_animator_'+this.id
    img_name = 'img_animator_'+this.id
  
    document.write('<form name="'+form_name+'">')
    document.write('<table border="7">');
    document.write('  <tr>');
    document.write('    <td bgcolor="#AAAAAA" width="180" align="center" valign="middle">');
    // document.write('    <img src = "http://www.f5chaser.com/antcam/images/antcam.jpg"><BR><BR>');
//document.write('      Loop: ');
//document.write('      <select name="select_mode">');
//document.write('        <option>Once</option>');
//document.write('        <option>Repeat</option>');
//document.write('        <option>Autoreverse</option>');
//document.write('      </select>');
    //document.write('      <br /> <hr width="70%" size="2" />');
    document.write('      Play: ');
//document.write('      <input type="button" name="button_bwrd" value="Bwrd" accesskey="B" />');
document.write('      <input type="button" name="button_stop" value="Stop" accesskey="S" />');
document.write('      <input type="button" name="button_fwrd" value="Fwrd" accesskey="F" />');
    // document.write('      <br /><hr width="70%" size="2" />');

    document.write('&nbsp;&nbsp;&nbsp;');
    document.write('      Step: <input type="button" name="button_first" value="First" accesskey="I" />');
    document.write('      <input type="button" name="button_prev" value="Prev"  accesskey="P" />');
    document.write('      <input type="button" name="button_next" value="Next"  accesskey="N" />');
    document.write('      <input type="button" name="button_last" value="Last"  accesskey="L" />');
    // document.write('      <br />');
    // document.write('      <hr width="70%" size="2" />');
    document.write('&nbsp;&nbsp;&nbsp;');
    document.write('      <label>Delay (ms):');
    document.write('        <input type="text" name="input_delay" size="3" />');
    document.write('      </label>');
    //document.write('      <br />');
    //document.write('      <hr width="70%" size="2" />');
//document.write('      <label>Extra delay (first):');
//document.write('        <input type="text" name="input_delay_first" size="3" />');
//document.write('      </label>');
//document.write('      <label>Extra delay (last):');
//document.write('        <input type="text" name="input_delay_last" size="3" />');
//document.write('      </label>');
//document.write('      <br />');
//document.write('      <hr width="70%" size="2" />');
    document.write('<br>');

    document.write('      <label>');
    document.write('        Frame <input type="text" name="input_current" size="3" />');
    document.write('      </label>');
    document.write('      <label>');
    document.write('        of <input type="text" name="input_n_frames" size="3" disabled="disabled" />');
    document.write('      </label>');
    document.write('  </td>');
    document.write(' </tr>');
    document.write(' <tr>');
    document.write('  <td bgcolor="#ffffff" align="center" valign="middle">');
    document.write('    <img name="'+img_name+'" border="0" alt="The animation" width="'+this.image_width+'" height="'+this.image_height+'" />');
    document.write('  </td>');
    document.write('</tr>');
    document.write('</table>');
    document.write('</form>');
    
    // 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
  
    this.form = document.forms[form_name]
    this.img = document.images[img_name]
    
    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).
    
// TPTEST    this.form.select_mode.onchange = new Function("this.form.animator.mode=this.selectedIndex;")
    
// TPTEST    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);")

    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);")
    
// TPTEST    this.form.input_delay_first.onfocus = new Function("this.form.animator.Stop(); this.select();")
// TPTEST    this.form.input_delay_first.onchange = new Function("this.form.animator.delay_first=parseInt(this.value);")
    
// TPTEST    this.form.input_delay_last.onfocus = new Function("this.form.animator.Stop(); this.select();")
// TPTEST    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.
    
    // TPTEST this.form.select_mode.selectedIndex = this.mode;
    this.form.input_n_frames.value = this.n_frames - this.first_frame;
	// TPTEST    this.form.input_n_frames.value = this.n_frames;
    this.form.input_delay.value = this.delay;
    // TPTEST this.form.input_delay_first.value = this.delay_first;
    // TPTEST this.form.input_delay_last.value = this.delay_last;
  }
  
  
  // Display specified frame
  function Display(num)
  {
    num = Number(num);
  
    if (num < 1) num = 1;
    if (num > this.n_frames) num = this.n_frames;
  
    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.active = true;
    this.PlayNext();
  }
  
  // Play next frame
  function PlayNext()
  {
    if (this.active)
    {
      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 - this.first_frame) 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:
// TPTEST              if (this.next > this.n_frames) this.next = 1;
              if (this.next > this.n_frames - this.first_frame) 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;
      }    
    }
  }
  
  // Stop the animation
  function Stop()
  {
    if (this.active) window.clearTimeout(this.timeID);
    this.active = false;
  }
  
  // Stop or restart the animation
  function StopStart()
  {
    if (this.active) this.Stop(); else this.Play();
  }
  
  // Initialize
  function Initialize()
  {
    this.Load();
    this.Write();
    this.Display(1);
  }
  
} // End of Animator constructor
